this might be a noob question but I have a simple script to monitor the current in my area which I use customtkinter as my GUI.
The script have two buttons which are Start and Restart. The Start button is to start the program and the Restart in to simply to Restart the whole code again which you could see on the snippet of the code below.
import sys
import os
#from tkinter import Tk, Label, Button
import customtkinter
def restart_program():
"""Restarts the current program."""
root.destroy()
os.startfile("testbed.py")
root = customtkinter.CTk()
#Label(root, text="Hello World!").pack()
#Button(root, text="Restart", command=restart_program).pack()
label = customtkinter.CTkLabel(root, text="Hello World!").pack()
button = customtkinter.CTkButton(root, text="Restart", command=restart_program).pack()
root.mainloop()
but when I clicked on the restart button it does not execute the os.startfile("testbed.py") part is there something wrong with my logic?
Short Version:
I have a series of python scripts that connect together (one .py closes and runs a separate .py). This works completely fine when running it through the terminal in VS Code or cmd line. 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.
Details:
All of the separate python files are saved in the same directory. The first one to open, 'Main.py', has a tkinter interface that allows the user to select which .py script they want to run. The code then closes the Main window and opens the selected python script using exec(open('chosen .py').read()). (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. This is the step which works fine in VS Code and cmd line, but causes the .exe from pyinstaller to close.
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. 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.
I have read through pyinstallers documentation, but have not found anything that I believe would be useful in this case. 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.
Would the issue be in how the scripts are coded and referencing each other, or with the installation process with 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. It is hacky but unsafe. Ref: Running a Python script from another
Instead use 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.
EDIT:
Why .exe file fails to execute another script, and why exec() is the problem:
According to the documentation:
Pyinstaller analyzes your code to discover every other module and
library your script needs in order to execute. Then it collects copies
of all those files – including the active Python interpreter! – 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. 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.
Thus, using import will actually import the script as module, when pyinstaller is executed, and now your .exe file will give no error.
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
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.
First, in 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:
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()
I have a simple code (test.py) to generate a popup window as shown below. It works fine when I run it directly in console.
import tkinter as tk
from tkinter.messagebox import showinfo
def popup():
print("Hello World!")
root1 = tk.Tk()
b1 = tk.Button(root1, text="Print", command=popup)
b1.pack(fill='x')
root1.mainloop()
But when I call this code (test.py) from another py script by
exec(open('test.py').read())
It gives error message "can't invoke “event” command: application has been destroyed". I have checked the prior discussion on this topic, but it doesn't seem to help my case.
can't invoke "event" command: application has been destroyed
Can anybody please help? Thanks!
I'm trying to make a launcher for another program but I just started with Python so I made a button, but I struggle to figure out how to execute another .py file. Any help?
When the button is pressed it activates the open_file() function and os opens the .py script.
from tkinter import *
import os
def open_file():
os.system('python file path here')
root=Tk()
btn = Button(root, text='Open .PY File', command=open_file)
btn.pack()
root.mainloop()
Here is a solution using from subprocess import call. All you have to do is replace 'YOUR_FILE_NAME' with... your file name :D
from tkinter import *
from subprocess import call
root=Tk()
root.geometry('200x100')
frame = Frame(root)
frame.pack(pady=20,padx=20)
def Open():
call(["python", "YOUR-FILE-NAME.py"])
btn=Button(frame,text='Open File',command=Open)
btn.pack()
root.mainloop()
What it will look like:
I hope this works for you :D
I tried to execute the following code in my system, and the window does not respond if the submit button is clicked
import Tkinter as tk
from Tkinter import *
top=Tk()
text=Text(top)
def onsubmit():
a=v.get()
ea.textbox(text=a)
v=StringVar()
t=Entry(top, textvariable=v)
submit=Button(top,text="SUBMIT",command=onsubmit)
t.grid(row=0,column=0)
submit.grid(row=0,column=1)
text.grid(row=1,column=0)
top.mainloop()
If you run your script from terminal or just look at the text output of your program in some other way you will see the following error (just after pressing the button):
NameError: global name 'ea' is not defined
The error is on the second line of onsubmit function. Here is the working version:
def onsubmit():
a=v.get()
text.insert(INSERT, a)