[英]Path error in PySide2 application after packaging with PyInstaller
I'm trying to package a PySide2 test application with the following structure:我正在尝试使用以下结构打包 PySide2 测试应用程序:
.
├── main.py
├── main.spec
└── wizardUI
├── 10.toolBoxBtns.ui
├── 11.toolBoxShrCt.ui
├── 12.propertyBox.ui
├── 13.printing.ui
├── 14.settings.ui
├── 15.coclusion.ui
├── 1.welcomePage.ui
├── 2.graphicsScene.ui
├── 3.graphicsSceneText.ui
├── 4.textDialog.ui
├── 5.codeDialog.ui
├── 6.graphicsSceneBox.ui
├── 7.graphicsScenePixmap.ui
├── 8.graphicsSceneShrCt.ui
├── 9.toolbox.ui
└── wizard.py
When I try to run an executable I get this error:当我尝试运行可执行文件时,出现此错误:
FileNotFoundError: No such file or directory:'/home/artem/Desktop/testUI/dist/main/wizardUI'
FileNotFoundError: No such file or directory:'/home/artem/Desktop/testUI/dist/main/wizardUI'
Here's my wizard.py file这是我的 Wizard.py 文件
from PySide2 import QtCore, QtWidgets
from PySide2.QtUiTools import QUiLoader
import os
class tutorWizard(QtWidgets.QWizard):
""" Contains introduction tutorial """
def __init__(self, parent=None):
super(tutorWizard, self).__init__(parent)
self.setWindowTitle("Introduction tutorial")
pages = self.findPages()
self.initPages(pages)
def findPages(self):
ui_files = []
cnt = 1
current_dir = os.path.dirname(os.path.realpath(__file__))
while len(ui_files) != 15:
for file in os.listdir(current_dir):
if file.startswith("{}.".format(cnt)):
ui_files.append(os.path.join(current_dir, file))
cnt += 1
return ui_files
def initPages(self, files):
loader = QUiLoader()
for i in files:
file = QtCore.QFile(str(i))
file.open(QtCore.QFile.ReadOnly)
file.reset()
page = loader.load(file)
file.close()
self.addPage(page)
main.py is: main.py 是:
from PySide2.QtWidgets import QApplication
from wizardUI.wizard import tutorWizard
import sys
app = QApplication(sys.argv)
window = tutorWizard()
window.show()
sys.exit(app.exec_())
and .spec file is:和 .spec 文件是:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['main.py'],
pathex=['/home/artem/Desktop/testUI'],
binaries=[],
datas=[],
hiddenimports=['PySide2.QtXml'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main')
a.datas += Tree('/home/artem/Desktop/testUI/wizardUI')
Is there any way to solve this error without changig current_dir
variable in wizard.py ?有没有办法解决这个错误,而无需在Wizard.py 中更改
current_dir
变量?
Your code has the following problems:您的代码存在以下问题:
You are adding the Tree()
to a.datas after COLLECT so it will not be used in the compilation, you have to add it before.您在 COLLECT 之后将
Tree()
添加到 a.datas 以便它不会在编译中使用,您必须在之前添加它。
You can no longer use __file__ to get the directory path, instead you must use sys._MEIPASS .您不能再使用 __file__ 来获取目录路径,而必须使用sys._MEIPASS 。
I will also give the following improvements:我还将提供以下改进:
Considering the above, the solution is as follows:综上所述,解决方法如下:
main.py主文件
from PySide2.QtWidgets import QApplication
from wizardUI.wizard import tutorWizard
import sys
if __name__ == "__main__":
app = QApplication(sys.argv)
window = tutorWizard()
window.show()
sys.exit(app.exec_())
wizard.py向导.py
import os
import sys
from PySide2 import QtCore, QtWidgets, QtUiTools
# https://stackoverflow.com/a/42615559/6622587
if getattr(sys, 'frozen', False):
# If the application is run as a bundle, the pyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
current_dir = os.path.join(sys._MEIPASS, "wizardUI")
else:
current_dir = os.path.dirname(os.path.abspath(__file__))
class tutorWizard(QtWidgets.QWizard):
""" Contains introduction tutorial """
def __init__(self, parent=None):
super(tutorWizard, self).__init__(parent)
self.setWindowTitle("Introduction tutorial")
pages = self.findPages()
self.initPages(pages)
def findPages(self):
ui_files = []
cnt = 1
while len(ui_files) < 15:
for file in os.listdir(current_dir):
if file.startswith("{}.".format(cnt)):
ui_files.append(os.path.join(current_dir, file))
cnt += 1
return ui_files
def initPages(self, files):
loader = QtUiTools.QUiLoader()
for i in files:
file = QtCore.QFile(str(i))
if file.open(QtCore.QFile.ReadOnly):
page = loader.load(file)
self.addPage(page)
main.spec主要规格
# -*- mode: python ; coding: utf-8 -*-
# https://stackoverflow.com/a/50402636/6622587
import os
spec_root = os.path.abspath(SPECPATH)
block_cipher = None
a = Analysis(['main.py'],
pathex=[spec_root],
binaries=[],
datas=[],
hiddenimports=['PySide2.QtXml', 'packaging.specifiers', 'packaging.requirements'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
a.datas += Tree(os.path.join(spec_root, 'wizardUI'), 'wizardUI', excludes=["*.py"])
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main')
Another option is to use Qt Resource
instead of data.另一种选择是使用
Qt Resource
而不是数据。
resource.qrc资源.qrc
<RCC>
<qresource prefix="/">
<file>wizardUI/1.welcomePage.ui</file>
<file>wizardUI/2.graphicsScene.ui</file>
<file>wizardUI/3.graphicsSceneText.ui</file>
<file>wizardUI/4.textDialog.ui</file>
<file>wizardUI/5.codeDialog.ui</file>
<file>wizardUI/6.graphicsSceneBox.ui</file>
<file>wizardUI/7.graphicsScenePixmap.ui</file>
<file>wizardUI/8.graphicsSceneShrCt.ui</file>
<file>wizardUI/9.toolbox.ui</file>
<file>wizardUI/10.toolBoxBtns.ui</file>
<file>wizardUI/11.toolBoxShrCt.ui</file>
<file>wizardUI/12.propertyBox.ui</file>
<file>wizardUI/13.printing.ui</file>
<file>wizardUI/14.settings.ui</file>
<file>wizardUI/15.coclusion.ui</file>
</qresource>
</RCC>
Then convert it to .py using pyside2-rcc:然后使用 pyside2-rcc 将其转换为 .py:
pyside2-rcc resource.qrc -o resource_rc.py
Then you have to modify the scripts:然后你必须修改脚本:
main.py主文件
from PySide2.QtWidgets import QApplication
from wizardUI.wizard import tutorWizard
import sys
import resource_rc
if __name__ == "__main__":
app = QApplication(sys.argv)
window = tutorWizard()
window.show()
sys.exit(app.exec_())
wizard.py向导.py
from PySide2 import QtCore, QtWidgets, QtUiTools
class tutorWizard(QtWidgets.QWizard):
""" Contains introduction tutorial """
def __init__(self, parent=None):
super(tutorWizard, self).__init__(parent)
self.setWindowTitle("Introduction tutorial")
pages = self.findPages()
self.initPages(pages)
def findPages(self):
ui_files = []
cnt = 1
while len(ui_files) < 15:
it = QtCore.QDirIterator(":/wizardUI")
while it.hasNext():
filename = it.next()
name = QtCore.QFileInfo(filename).fileName()
if name.startswith("{}.".format(cnt)):
ui_files.append(filename)
cnt += 1
return ui_files
def initPages(self, files):
loader = QtUiTools.QUiLoader()
for i in files:
file = QtCore.QFile(str(i))
if file.open(QtCore.QFile.ReadOnly):
page = loader.load(file)
self.addPage(page)
And finally the structure of your project is as follows:最后,您的项目结构如下:
├── main.py
├── main.spec
├── resource.qrc
├── resource_rc.py
└── wizardUI
├── 10.toolBoxBtns.ui
├── 11.toolBoxShrCt.ui
├── 12.propertyBox.ui
├── 13.printing.ui
├── 14.settings.ui
├── 15.coclusion.ui
├── 1.welcomePage.ui
├── 2.graphicsScene.ui
├── 3.graphicsSceneText.ui
├── 4.textDialog.ui
├── 5.codeDialog.ui
├── 6.graphicsSceneBox.ui
├── 7.graphicsScenePixmap.ui
├── 8.graphicsSceneShrCt.ui
├── 9.toolbox.ui
└── wizard.py
It is a probleme of the path,这是路径的问题,
To be simple,简单来说,
We should use this condition to get the path of ui files:我们应该使用这个条件来获取 ui 文件的路径:
if getattr(sys, 'frozen', False):
ui_file_path = os.path.join(sys._MEIPASS, ui_file)
else:
ui_file_path = os.path.join(sys.path[0], ui_file)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.