【问题标题】:How can I implement an .ui file to package a PyQt5 application with fbs?如何实现 .ui 文件以使用 fbs 打包 PyQt5 应用程序?
【发布时间】:2020-10-15 18:56:28
【问题描述】:

编辑:我的问题可能与这个重复:link。习惯用谷歌搜索,没想到要通过 Stack 搜索栏。

我一直在尝试使用 Pyinstaller 打包 PyQt5 应用程序(没有成功),并决定使用 fbs 尝试一下。但我正在努力重写我的主要 python 文件以使其与编译器一起工作。经过两周试图弄清楚如何最终解决这些问题,我想向更高级的开发人员寻求帮助。

我的项目是一个虚拟板,可拖动元素以这种方式组织:

  • “main.py”文件
  • “board.ui”文件
  • 一个包含图像的文件夹(在使用 Qt 设计器创建 UI 时使用)

第一版代码(在尝试将其转换为 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_())

此代码在编译后可以正常工作,但无论我如何尝试,我都无法将其打包分发 - 控制台会打开,但 UI 永远不会出现。所以我决定尝试使用 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'))

我尝试使用那里提出的解决方案(用于 UI 实现):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 上工作。

我对编程很陌生,这是我关于 Stack Overflow 的第一个问题(在过去的几个月里非常有用),所以如果有不清楚的地方请告诉我,我会尽力解释正确。

感谢您的洞察力!

编辑:

所选答案有帮助。不过,出于其他原因,我不得不稍微改变一下结构,这是当前代码:

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 中用于构建 UI 的图片包含在文件夹中,直接在 UI 文件夹中。现在项目使用 fbs run 命令运行良好。冻结后获取的可执行文件返回“no module named main”错误,但似乎与其他原因有关。

【问题讨论】:

    标签: python pyqt5 fbs


    【解决方案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
    

    ma​​in.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)
    

    【讨论】:

    • 谢谢!由于未解决的引用(我的问题中没有包含),我不得不稍微更改代码,但它现在似乎正在工作,至少使用“fbs run”命令。您对它和结构的洞察力确实有所帮助。我将编辑我的消息并添加代码。不过,我仍然无法将其打包,但我现在会检查所有可能的原因。谢谢!
    猜你喜欢
    • 2016-09-15
    • 1970-01-01
    • 2018-04-27
    • 2020-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    相关资源
    最近更新 更多