Как я могу реализовать файл .ui для упаковки приложения PyQt5 с помощью fbs?

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. Исполняемый файл, полученный после замораживания, возвращает основную ошибку отсутствия модуля с именем, но, похоже, это связано с другими причинами.


person comte_mo    schedule 14.10.2020    source источник


Ответы (1)


ApplicationContext не является виджетом, поэтому loadUi нелогичен, вам следует использовать окно. Также, поскольку вы не указываете, где находится .ui, вы должны использовать следующую структуру:

└── src
    ├── build
    │   └── settings
    │       ├── base.json
    │       ├── linux.json
    │       └── mac.json
    └── main
        ├── icons
        │   ├── base
        │   │   ├── 16.png
        │   │   ├── 24.png
        │   │   ├── 32.png
        │   │   ├── 48.png
        │   │   └── 64.png
        │   ├── Icon.ico
        │   ├── linux
        │   │   ├── 1024.png
        │   │   ├── 128.png
        │   │   ├── 256.png
        │   │   └── 512.png
        │   ├── mac
        │   │   ├── 1024.png
        │   │   ├── 128.png
        │   │   ├── 256.png
        │   │   └── 512.png
        │   └── README.md
        ├── python
        │   └── main.py
        └── resources
            └── base
                └── ui
                    └── board.ui

main.py

from fbs_runtime.application_context.PyQt5 import ApplicationContext, cached_property
from PyQt5 import QtGui, QtWidgets, uic

import sys


class AppContext(ApplicationContext):
    def run(self):
        self.initUI()
        return self.app.exec_()

    def initUI(self):
        uic.loadUi(self.get_resource("ui/board.ui"), self.window)
        version = self.build_settings['version']
        self.window.setWindowTitle("My Board v" + version)
        self.window.show()

    @cached_property
    def window(self):
        return QtWidgets.QMainWindow()


if __name__ == '__main__':
    appctxt = AppContext()
    exit_code = appctxt.run()
    sys.exit(exit_code)
person eyllanesc    schedule 14.10.2020
comment
Благодарю вас! Мне пришлось немного изменить код из-за неразрешенных ссылок (которые я не включил в свой вопрос), но, похоже, теперь он работает, по крайней мере, с командой запуска fbs. Ваше понимание этого и структуры действительно помогло. Я отредактирую свое сообщение и добавлю код. Однако у меня все еще есть проблемы с его упаковкой, но я сейчас проверю все возможные причины. Благодарю вас! - person comte_mo; 15.10.2020