将多个 .py 文件转换为 .exe
Convert multiple .py file to .exe
我正在尝试使用 PyInstaller 将我的 python 程序转换为可执行 (.exe) 文件,我有 2 个 python 文件:dummy_script.py
和 GUI.py
。
基本上,GUI.py
包含一个执行 dummy_script.py
的按钮。
dummy_script.py
:
import sys
import time
def flush_then_wait():
sys.stdout.flush()
sys.stderr.flush()
time.sleep(0.5)
sys.stdout.write("Script stdout 1\n")
sys.stdout.write("Script stdout 2\n")
sys.stdout.write("Script stdout 3\n")
sys.stderr.write("Total time: 00:05:00\n")
sys.stderr.write("Total complete: 10%\n")
flush_then_wait()
sys.stdout.write("name=Martin\n")
sys.stdout.write("Script stdout 4\n")
sys.stdout.write("Script stdout 5\n")
sys.stderr.write("Total complete: 30%\n")
flush_then_wait()
sys.stderr.write("Elapsed time: 00:00:10\n")
sys.stderr.write("Elapsed time: 00:00:50\n")
sys.stderr.write("Total complete: 50%\n")
sys.stdout.write("country=Nederland\n")
flush_then_wait()
sys.stderr.write("Elapsed time: 00:01:10\n")
sys.stderr.write("Total complete: 100%\n")
sys.stdout.write("Script stdout 6\n")
sys.stdout.write("Script stdout 7\n")
sys.stdout.write("website=www.mfitzp.com\n")
flush_then_wait()
GUI.py
:
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget, QProgressBar)
from PyQt5.QtCore import QProcess
import sys
import re
# A regular expression, to extract the % complete.
progress_re = re.compile("Total complete: (\d+)%")
def simple_percent_parser(output):
"""
Matches lines using the progress_re regex,
returning a single integer for the % progress.
"""
m = progress_re.search(output)
if m:
pc_complete = m.group(1)
return int(pc_complete)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.p = None
self.btn = QPushButton("Execute")
self.btn.pressed.connect(self.start_process)
self.text = QPlainTextEdit()
self.text.setReadOnly(True)
self.progress = QProgressBar()
self.progress.setRange(0, 100)
l = QVBoxLayout()
l.addWidget(self.btn)
l.addWidget(self.progress)
l.addWidget(self.text)
w = QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def message(self, s):
self.text.appendPlainText(s)
def start_process(self):
if self.p is None: # No process running.
self.message("Executing process")
self.p = QProcess() # Keep a reference to the QProcess (e.g. on self) while it's running.
self.p.readyReadStandardOutput.connect(self.handle_stdout)
self.p.readyReadStandardError.connect(self.handle_stderr)
self.p.stateChanged.connect(self.handle_state)
self.p.finished.connect(self.process_finished) # Clean up once complete.
self.p.start("python",["dummy_script.py"])
def handle_stderr(self):
data = self.p.readAllStandardError()
stderr = bytes(data).decode("utf8")
# Extract progress if it is in the data.
progress = simple_percent_parser(stderr)
if progress:
self.progress.setValue(progress)
self.message(stderr)
def handle_stdout(self):
data = self.p.readAllStandardOutput()
stdout = bytes(data).decode("utf8")
self.message(stdout)
def handle_state(self, state):
states = {
QProcess.NotRunning: 'Not running',
QProcess.Starting: 'Starting',
QProcess.Running: 'Running',
}
state_name = states[state]
self.message(f"State changed: {state_name}")
def process_finished(self):
self.message("Process finished.")
self.p = None
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
在GUI.py
中执行dummy_script.py
的概念是:
p = QProcess()
p.start("python3", ['dummy_script.py'])
现在,当我使用 PyInstaller 将整个程序打包成可执行文件时,dummy_script.py
丢失了。
如何确保 dummy_script.py
包含在 PyInstaller 包中?
参考:https://www.pythonguis.com/tutorials/qprocess-external-programs/
Chris 似乎为您提供了一个很好的答案。如果您对 pyinstaller 有任何进一步的问题。
其他参考文献:https://pyinstaller.readthedocs.io/en/stable/usage.html
我使用以下:
pathex 查找导入的其他路径
hiddenimports = 在 python 脚本中缺失或不可见。有时模块本身有隐藏的导入。
datas = 来自模块的所有数据
有时需要一个钩子,一个用于 Hydra 的钩子示例:
from PyInstaller.utils.hooks import
collect_data_files,collect_submodules
datas = collect_data_files('hydra.conf.hydra')
hiddenimports = collect_submodules('hydra')
The concept used to execute (dummy_script.py) in (GUI.py) is,
p = QProcess()
p.start("python3", ['dummy_script.py'])
PyInstaller 非常擅长查找和捆绑依赖项,但如果您这样 运行,它将无法确定 GUI.py
需要 dummy_script.py
。
更好的方法是导入您需要的代码并直接使用它,例如
from dummy_script import some_function
some_function()
其他选项
如果你只是像这样修改GUI.py
,PyInstaller should find dummy_script.py
on its own。
如果由于某种原因这不可行,您应该能够将其声明为隐藏导入 using a spec file。你可能有一个来自早期版本的规范文件,但如果你需要创建一个新的,你可以用这样的东西来做:
pyi-makespec GUI.py
然后编辑 spec 文件以将 dummy_script
添加到隐藏导入列表中:
a = Analysis(['GUI.py'],
# ...
hiddenimports=['dummy_script'],
# ...,
)
然后从修改后的规范文件再次构建:
pyinstaller foo.spec
这可能也行不通,因为您还没有 import
使用其他模块。在这种情况下,您可能需要将其声明为 a data file。
我正在尝试使用 PyInstaller 将我的 python 程序转换为可执行 (.exe) 文件,我有 2 个 python 文件:dummy_script.py
和 GUI.py
。
基本上,GUI.py
包含一个执行 dummy_script.py
的按钮。
dummy_script.py
:
import sys
import time
def flush_then_wait():
sys.stdout.flush()
sys.stderr.flush()
time.sleep(0.5)
sys.stdout.write("Script stdout 1\n")
sys.stdout.write("Script stdout 2\n")
sys.stdout.write("Script stdout 3\n")
sys.stderr.write("Total time: 00:05:00\n")
sys.stderr.write("Total complete: 10%\n")
flush_then_wait()
sys.stdout.write("name=Martin\n")
sys.stdout.write("Script stdout 4\n")
sys.stdout.write("Script stdout 5\n")
sys.stderr.write("Total complete: 30%\n")
flush_then_wait()
sys.stderr.write("Elapsed time: 00:00:10\n")
sys.stderr.write("Elapsed time: 00:00:50\n")
sys.stderr.write("Total complete: 50%\n")
sys.stdout.write("country=Nederland\n")
flush_then_wait()
sys.stderr.write("Elapsed time: 00:01:10\n")
sys.stderr.write("Total complete: 100%\n")
sys.stdout.write("Script stdout 6\n")
sys.stdout.write("Script stdout 7\n")
sys.stdout.write("website=www.mfitzp.com\n")
flush_then_wait()
GUI.py
:
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QPlainTextEdit,
QVBoxLayout, QWidget, QProgressBar)
from PyQt5.QtCore import QProcess
import sys
import re
# A regular expression, to extract the % complete.
progress_re = re.compile("Total complete: (\d+)%")
def simple_percent_parser(output):
"""
Matches lines using the progress_re regex,
returning a single integer for the % progress.
"""
m = progress_re.search(output)
if m:
pc_complete = m.group(1)
return int(pc_complete)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.p = None
self.btn = QPushButton("Execute")
self.btn.pressed.connect(self.start_process)
self.text = QPlainTextEdit()
self.text.setReadOnly(True)
self.progress = QProgressBar()
self.progress.setRange(0, 100)
l = QVBoxLayout()
l.addWidget(self.btn)
l.addWidget(self.progress)
l.addWidget(self.text)
w = QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def message(self, s):
self.text.appendPlainText(s)
def start_process(self):
if self.p is None: # No process running.
self.message("Executing process")
self.p = QProcess() # Keep a reference to the QProcess (e.g. on self) while it's running.
self.p.readyReadStandardOutput.connect(self.handle_stdout)
self.p.readyReadStandardError.connect(self.handle_stderr)
self.p.stateChanged.connect(self.handle_state)
self.p.finished.connect(self.process_finished) # Clean up once complete.
self.p.start("python",["dummy_script.py"])
def handle_stderr(self):
data = self.p.readAllStandardError()
stderr = bytes(data).decode("utf8")
# Extract progress if it is in the data.
progress = simple_percent_parser(stderr)
if progress:
self.progress.setValue(progress)
self.message(stderr)
def handle_stdout(self):
data = self.p.readAllStandardOutput()
stdout = bytes(data).decode("utf8")
self.message(stdout)
def handle_state(self, state):
states = {
QProcess.NotRunning: 'Not running',
QProcess.Starting: 'Starting',
QProcess.Running: 'Running',
}
state_name = states[state]
self.message(f"State changed: {state_name}")
def process_finished(self):
self.message("Process finished.")
self.p = None
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
在GUI.py
中执行dummy_script.py
的概念是:
p = QProcess()
p.start("python3", ['dummy_script.py'])
现在,当我使用 PyInstaller 将整个程序打包成可执行文件时,dummy_script.py
丢失了。
如何确保 dummy_script.py
包含在 PyInstaller 包中?
参考:https://www.pythonguis.com/tutorials/qprocess-external-programs/
Chris 似乎为您提供了一个很好的答案。如果您对 pyinstaller 有任何进一步的问题。
其他参考文献:https://pyinstaller.readthedocs.io/en/stable/usage.html
我使用以下: pathex 查找导入的其他路径 hiddenimports = 在 python 脚本中缺失或不可见。有时模块本身有隐藏的导入。 datas = 来自模块的所有数据
有时需要一个钩子,一个用于 Hydra 的钩子示例:
from PyInstaller.utils.hooks import
collect_data_files,collect_submodules
datas = collect_data_files('hydra.conf.hydra')
hiddenimports = collect_submodules('hydra')
The concept used to execute (dummy_script.py) in (GUI.py) is,
p = QProcess() p.start("python3", ['dummy_script.py'])
PyInstaller 非常擅长查找和捆绑依赖项,但如果您这样 运行,它将无法确定 GUI.py
需要 dummy_script.py
。
更好的方法是导入您需要的代码并直接使用它,例如
from dummy_script import some_function
some_function()
其他选项
如果你只是像这样修改GUI.py
,PyInstaller should find dummy_script.py
on its own。
如果由于某种原因这不可行,您应该能够将其声明为隐藏导入 using a spec file。你可能有一个来自早期版本的规范文件,但如果你需要创建一个新的,你可以用这样的东西来做:
pyi-makespec GUI.py
然后编辑 spec 文件以将 dummy_script
添加到隐藏导入列表中:
a = Analysis(['GUI.py'],
# ...
hiddenimports=['dummy_script'],
# ...,
)
然后从修改后的规范文件再次构建:
pyinstaller foo.spec
这可能也行不通,因为您还没有 import
使用其他模块。在这种情况下,您可能需要将其声明为 a data file。