如何实现 .ui 文件以使用 fbs 打包 PyQt5 应用程序?
How can I implement an .ui file to package a PyQt5 application with fbs?
编辑: 我的问题可能与这个问题重复:。我习惯了通过 Google 进行搜索,但没想到我应该通过 Stack 搜索栏进行搜索。
我一直在尝试使用 Pyinstaller 打包 PyQt5 应用程序(但没有成功),并决定尝试使用 fbs。但我正在努力重写我的主要 python 文件以使其与编译器一起工作。经过两周的尝试找出最终解决这些问题的方法,我想请求更多高级开发人员的帮助。
我的项目是一个虚拟板,其中包含以这种方式组织的可拖动元素:
- 一个“main.py”文件
- 一个“board.ui”文件
- 带有图像的文件夹(在使用 Qt Designer 创建 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)
但我想我不知道如何正确地实施它。关于“顶层小部件的错误基础 class”,我不知道该怎么做。我真的很想知道我的下一步必须做什么,并了解它。
我应该准确地说我正在使用 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 运行 命令运行良好。冻结后得到的可执行文件returns出现“no module named main”错误,但似乎与其他原因有关。
ApplicationContext 不是小部件,因此 loadUi 不合逻辑,您应该做的是使用 window。另外,由于您没有指明 .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)
编辑: 我的问题可能与这个问题重复:
我一直在尝试使用 Pyinstaller 打包 PyQt5 应用程序(但没有成功),并决定尝试使用 fbs。但我正在努力重写我的主要 python 文件以使其与编译器一起工作。经过两周的尝试找出最终解决这些问题的方法,我想请求更多高级开发人员的帮助。
我的项目是一个虚拟板,其中包含以这种方式组织的可拖动元素:
- 一个“main.py”文件
- 一个“board.ui”文件
- 带有图像的文件夹(在使用 Qt Designer 创建 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)
但我想我不知道如何正确地实施它。关于“顶层小部件的错误基础 class”,我不知道该怎么做。我真的很想知道我的下一步必须做什么,并了解它。
我应该准确地说我正在使用 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 运行 命令运行良好。冻结后得到的可执行文件returns出现“no module named main”错误,但似乎与其他原因有关。
ApplicationContext 不是小部件,因此 loadUi 不合逻辑,您应该做的是使用 window。另外,由于您没有指明 .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)