EDIT: Мой вопрос может дублироваться с этим: ссылка. Я привык искать через Google, не понял, что должен смотреть через панель поиска Stack.
Я пытался упаковать приложение PyQt5 с помощью Pyinstaller (безуспешно) и решил попробовать его с помощью fbs. Но я изо всех сил пытаюсь переписать свой основной файл Python, чтобы он работал с компилятором. После двух недель попыток выяснить, как наконец решить эту проблему, я хотел бы обратиться за помощью к более продвинутым разработчикам.
Мой проект представляет собой виртуальную доску с перетаскиваемыми элементами, организованными следующим образом:
- файл main.py
- файл board.ui
- папка с изображениями (используется при создании пользовательского интерфейса с помощью Qt Designer)
Первая версия кода (перед попыткой конвертировать в fbs):
# Main Window
class App(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
# ... Several other functions called for initialization
# (changing language, e.g.)...
def initUI(self):
uic.loadUi("board.ui", self)
self.setWindowIcon(QtGui.QIcon('images/logo.png'))
self.setWindowTitle("My Board")
self.show()
# ... rest of the code ...
# (includes functions for initialization and interaction with UI elements
# (changing their text content, position, e.g.)
# and a subclass of QLabel for Drag and Drop interaction)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Этот код будет нормально работать при компиляции, но я не мог упаковать его для распространения, что бы я ни пытался — консоль открывалась, но пользовательский интерфейс никогда не появлялся. Поэтому я решил попробовать это с fbs (что, в качестве бонуса, заставит меня в следующий раз больше думать об организации проекта):
Новая версия кода (попытка организовать проект в соответствии с рекомендациями fbs):
class AppContext(ApplicationContext):
def run(self):
window = QMainWindow()
version = self.build_settings['version']
window.setWindowTitle("My Board v" + version)
self.initUI()
# ... other functions for initialization...
window.show()
return self.app.exec_()
@cached_property
def initUI(self):
uic.loadUi("board.ui", self)
# I know the next line has to be rewritten, I have tried to comment it out
# as it is another question - one step at the time
self.setWindowIcon(QtGui.QIcon('images/logo.png'))
self.setWindowTitle("My Board")
self.show()
# ...other cached properties linked to the previous initialization functions...
# ...rest of the code (same than in the first version)
if __name__ == '__main__':
appctxt = AppContext()
exit_code = appctxt.run()
sys.exit(exit_code)
Этот код даже не скомпилируется, я получаю эту трассировку:
Traceback (most recent call last):
File "C:/Users/...Board/main.py", line 529, in <module>
exit_code = appctxt.run()
File "C:/Users/...Board/main.py", line 25, in run
self.initUI()
File "C:/Users/...Board/main.py", line 45, in initUI
uic.loadUi("board.ui", self)
File "C:\Users\...Board\main.py\venv\lib\site-packages\PyQt5\uic\__init__.py", line 238, in
loadUi
return DynamicUILoader(package).loadUi(uifile, baseinstance, resource_suffix)
File "C:\Users\...Board\venv\lib\site-packages\PyQt5\uic\Loader\loader.py", line 66, in
loadUi
return self.parse(filename, resource_suffix)
File "C:\Users\...Board\venv\lib\site-packages\PyQt5\uic\uiparser.py", line 1037, in parse
actor(elem)
File "C:\Users\...Board\venv\lib\site-packages\PyQt5\uic\uiparser.py", line 822, in
createUserInterface
self.toplevelWidget = self.createToplevelWidget(cname, wname)
File "C:\Users\...Board\venv\lib\site-packages\PyQt5\uic\Loader\loader.py", line 59, in
createToplevelWidget
(type(self.toplevelInst), classname)))
TypeError: ('Wrong base class of toplevel widget', (<class '__main__.AppContext'>,
'QMainWindow'))
Я попытался использовать предложенное там решение (для реализации пользовательского интерфейса): https://forum.learnpyqt.com/t/ui-files-with-fbs/61/2
qtCreatorFile = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "board.ui")
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
Но я полагаю, я не мог понять, как это правильно реализовать. О неправильном базовом классе виджета верхнего уровня Я понятия не имею, что делать. Я действительно хотел бы узнать, какими должны быть мои следующие шаги, и понять это.
Уточню, я использую Python 3.6 и работаю над PyCharm.
Я новичок в программировании, и это мой самый первый вопрос о переполнении стека (который был очень полезен в последние месяцы), поэтому, пожалуйста, дайте мне знать, если что-то неясно, я сделаю все возможное, чтобы объяснить это более правильно.
Спасибо за ваше понимание!
ИЗМЕНИТЬ:
Выбранный ответ помог. По другим причинам мне пришлось немного изменить структуру, вот текущий код:
class AppContext(ApplicationContext):
def run(self):
self.window()
return self.app.exec_()
@cached_property
def window(self):
return App
class App(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
# ...Other functions called to initialize
# ...
def initUI(self):
uic.loadUi("src/main/resources/base/ui/board.ui", self)
self.setWindowTitle("Board Routine")
self.show()
Я следовал структуре, показанной eyllanesc, и включил папку с изображениями, используемыми в Qt Designer для создания пользовательского интерфейса, непосредственно в папке пользовательского интерфейса. Теперь проект хорошо работает с командой fbs run. Исполняемый файл, полученный после замораживания, возвращает основную ошибку отсутствия модуля с именем, но, похоже, это связано с другими причинами.