[英]Creating a .exe from a python script which runs a separate python script using pyinstaller
I have a series of python scripts that connect together (one .py closes and runs a separate .py).我有一系列连接在一起的 python 脚本(一个 .py 关闭并运行一个单独的 .py)。 This works completely fine when running it through the terminal in VS Code or cmd line.
在 VS Code 或 cmd 行中通过终端运行它时,这完全正常。 Once it is in a .exe by pyinstaller, only the first code works and the program closes once it tries to execute a separate .py file.
一旦它被 pyinstaller 放在 .exe 中,只有第一个代码起作用,并且程序在尝试执行单独的 .py 文件时关闭。
All of the separate python files are saved in the same directory.所有单独的python文件都保存在同一目录中。 The first one to open, 'Main.py', has a tkinter interface that allows the user to select which .py script they want to run.
第一个打开的“Main.py”有一个 tkinter 界面,允许用户选择他们想要运行的 .py 脚本。 The code then closes the Main window and opens the selected python script using
exec(open('chosen .py').read())
.然后代码关闭主窗口并使用
exec(open('chosen .py').read())
打开选定的 python 脚本。 (This is a simplified version of the initial code but I am having the same issues) (这是初始代码的简化版本,但我遇到了同样的问题)
import tkinter as tk
from tkinter import ttk
from tkinter.constants import W
from tkinter import messagebox as mb
""" Open a window to select which separate script to run"""
root = tk.Tk()
root.title('Selection Window')
root.geometry('300x200')
frame_1 = tk.LabelFrame(root, text='Choose Program')
frame_1.pack()
# Using this function to update on radio button select
def radio_button_get():
global program_int
choice = radio_ID.get()
if(choice == 1):
program_int = 1
elif(choice == 2):
program_int = 2
# Display confirmation popup
def run_script():
if(program_int == 1):
select = mb.askokcancel("Confirm", "Run choice 1?")
if(select == 1):
root.destroy()
else:
return
if(program_int == 2):
select = mb.askokcancel("Confirm", "No selection")
if(select == 1):
root.destroy()
else:
return
# Create radio buttons to select program
radio_ID = tk.IntVar()
radio_ID.set(2)
program_int = 2 # Set default selection
choice_1 = tk.Radiobutton(frame_1, text='Execute Script 1', variable=radio_ID, value=1, command=radio_button_get)
choice_1.pack()
no_choice = tk.Radiobutton(frame_1, text='No Selection', variable=radio_ID, value=2, command=radio_button_get)
no_choice.pack()
# Button to run the selected code
run_button = ttk.Button(root, text='Run', command=run_script)
run_button.pack()
root.mainloop()
# Execute the other python script
if(program_int == 1):
exec(open('Script1.py').read())
The next code is the 'Script1.py' file which 'Main.py' runs at the end.下一个代码是“Main.py”最后运行的“Script1.py”文件。 This is the step which works fine in VS Code and cmd line, but causes the .exe from pyinstaller to close.
这是在 VS Code 和 cmd 行中运行良好的步骤,但会导致 pyinstaller 中的 .exe 关闭。
import tkinter as tk
from tkinter import ttk
""" Create this programs GUI window"""
root = tk.Tk()
root.title('Script 1')
def run():
root.destroy()
label = ttk.Label(root, text='Close to run')
label.pack()
button = ttk.Button(root, text='Close', command=run)
button.pack()
root.mainloop()
""" Do some code stuff here"""
# When above code is done, want to return to the Main.py window
exec(open('Main.py').read())
Each independent .py file have been successfully turned into .exe files with pyinstaller previously.每个独立的 .py 文件之前都已成功使用 pyinstaller 转换为 .exe 文件。 The cmd line command that I am using to execute pyinstaller is
pyinstaller 'Main.py'
This successfully creates a Main.exe in the dist folder and also includes a build folder.我用来执行 pyinstaller 的 cmd 行命令是
pyinstaller 'Main.py'
这成功地在 dist 文件夹中创建了一个 Main.exe,还包括一个 build 文件夹。
I have read through pyinstallers documentation, but have not found anything that I believe would be useful in this case.我已经通读了 pyinstallers 文档,但没有找到我认为在这种情况下有用的任何内容。 The nearest issue I could find was importing python scripts as modules in the .spec file options but since the code executes the python script as a separate entity, I don't think this is the fix.
我能找到的最近的问题是在 .spec 文件选项中将 python 脚本作为模块导入,但由于代码将 python 脚本作为单独的实体执行,我认为这不是解决方案。
Would the issue be in how the scripts are coded and referencing each other, or with the installation process with pyinstaller?问题是脚本如何编码和相互引用,还是与 pyinstaller 的安装过程有关? If I missed something in the documentation that would explain this issue, please let me know and I will look there!
如果我遗漏了文档中可以解释此问题的内容,请告诉我,我会查看!
Any help is greatly appreciated, thank you非常感谢任何帮助,谢谢
We must avoid using the .exec
command.我们必须避免使用
.exec
命令。 It is hacky but unsafe.这是hacky但不安全。 Ref: Running a Python script from another
参考:从另一个运行 Python 脚本
Instead use import
:而是使用
import
:
# Execute the other python script
if(program_int == 1):
import Script1
And here too:这里也是:
# When above code is done, want to return to the Main.py window
import Main
That's it, now use pyinstaller.就是这样,现在使用pyinstaller。
EDIT:编辑:
Why .exe
file fails to execute another script, and why exec()
is the problem:为什么
.exe
文件无法执行另一个脚本,以及为什么exec()
是问题所在:
According to the documentation:根据文档:
Pyinstaller analyzes your code to discover every other module and library your script needs in order to execute.
Pyinstaller 分析您的代码以发现您的脚本执行所需的所有其他模块和库。 Then it collects copies of all those files – including the active Python interpreter!
然后它收集所有这些文件的副本——包括活动的 Python 解释器! – and puts them with your script in a single folder, or optionally in a single executable file.
– 并将它们与您的脚本放在一个文件夹中,或者可以选择放在一个可执行文件中。
So, when pyinstaller is analyzing & creating the .exe file, it only executes the exec()
function that time (so no error thrown while pyinstaller runs), pyinstaller does not import it or copies it to your .exe.因此,当 pyinstaller 分析和创建 .exe 文件时,它只在那个时候执行
exec()
函数(因此在 pyinstaller 运行时不会抛出错误), pyinstaller 不会导入它或将其复制到您的 .exe。 file , and then after the .exe file is created, upon running it throws error that no such script file exists because it was never compiled into that .exe file. file ,然后在创建 .exe 文件后,在运行时会抛出错误,指出不存在此类脚本文件,因为它从未编译到该 .exe 文件中。
Thus, using import
will actually import the script as module, when pyinstaller is executed, and now your .exe file will give no error.因此,当执行 pyinstaller 时,使用
import
实际上会将脚本作为模块导入,现在您的 .exe 文件将不会出错。
Instead of importing the scripts as modules, for them to be re-executed again and again, import another script as a function in Main.py不是将脚本作为模块导入,而是为了一次又一次地重新执行它们,在 Main.py 中导入另一个脚本作为函数
Also, instead of destroying your Main root window (since you won't be able to open it again unless you create a new window), use .withdraw()
to hide it and then .deiconify()
to show.此外,不要破坏主根窗口(因为除非创建新窗口,否则您将无法再次打开它),
.withdraw()
使用.withdraw()
隐藏它,然后使用.deiconify()
显示。
First, in Script1.py:首先,在 Script1.py 中:
import tkinter as tk
from tkinter import ttk
""" Create this programs GUI window"""
def script1Function(root): #the main root window is recieved as parameter, since this function is not inside the scope of Main.py's root
root2 = tk.Tk() #change the name to root2 to remove any ambiguity
root2.title('Script 1')
def run():
root2.destroy() #destroy this root2 window
root.deiconify() #show the hidden Main root window
label = ttk.Label(root2, text='Close to run')
label.pack()
button = ttk.Button(root2, text='Close', command=run)
button.pack()
root2.mainloop()
Then, in Main.py:然后,在 Main.py 中:
import tkinter as tk
from tkinter import ttk
from tkinter.constants import W
from tkinter import messagebox as mb
from Script1 import script1Function #importing Script1's function
# Execute the other python script
def openScript1():
root.withdraw() #hide this root window
script1Function(root) #pass root window as parameter, so that Script1 can show root again
""" Open a window to select which separate script to run"""
root = tk.Tk()
root.title('Selection Window')
root.geometry('300x200')
frame_1 = tk.LabelFrame(root, text='Choose Program')
frame_1.pack()
# Using this function to update on radio button select
def radio_button_get():
global program_int
choice = radio_ID.get()
if(choice == 1):
program_int = 1
elif(choice == 2):
program_int = 2
# Display confirmation popup
def run_script():
global program_int #you forgot to make it global
if(program_int == 1):
select = mb.askokcancel("Confirm", "Run choice 1?")
if(select == 1):
openScript1()
else:
return
if(program_int == 2):
select = mb.askokcancel("Confirm", "No selection")
if(select == 1):
root.destroy()
else:
return
# Create radio buttons to select program
radio_ID = tk.IntVar()
radio_ID.set(2)
program_int = 2 # Set default selection
choice_1 = tk.Radiobutton(frame_1, text='Execute Script 1', variable=radio_ID, value=1, command=radio_button_get)
choice_1.pack()
no_choice = tk.Radiobutton(frame_1, text='No Selection', variable=radio_ID, value=2, command=radio_button_get)
no_choice.pack()
# Button to run the selected code
run_button = ttk.Button(root, text='Run', command=run_script)
run_button.pack()
root.mainloop()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.