I was trying to create a simple app for illustration purposes. The idea is as follows:
Create an application which will run script files associated only to the selected courses (radio buttons). So, I create radio buttons which list out subjects (to click on). Once the subjects are selected the user has to hit the Enter button. This should run all the .py files for the selected subjects (execute_script function).
However, when I run my code, I get 4 messageboxes with 'None' written inside. After clicking ok on them, I get a square windows with only the enter button. What can I do to correct this problem?
def check(file_name, relStatus):
radioValue = relStatus.get()
tkMessageBox.showinfo('You checked', radioValue)
been_clicked.append(file_name)
return
def execute_script():
for name in been_cliked:
subprocess.Popen(['python', 'C:\Users\Max\Subjects\{}'.format(name)])
yield
def main():
#Create application
app = Tk()
app.title('Coursework')
app.geometry('450x300+200+200')
#Header
labelText = StringVar()
labelText.set('Select subjects')
#Dictionary with names
product_names = {}
names = []
file_name = []
names = ['Math', 'Science', 'English', 'French']
file_name = ['calc.py', 'physics.py', 'grammar.py', 'livre.py']
product_names = OrderedDict(zip(names, file_name))
#Create radio buttons
global been_clicked
been_clicked = []
relStatus = StringVar()
relStatus.set(None)
for name,file_name in product_names.iteritems():
radio1 = Radiobutton(app, text=name, value=name, \
variable=relStatus, command=check(file_name, relStatus))
button = Button(app, text='Click Here', width=20, command=execute_script())
button.pack(side='bottom', padx=15, pady=15)
app.mainloop()
if __name__ == '__main__': main()
There are a few issues with your script:
1) A typo in your execute_script() function: for name in been_cliked
2) You are actually calling the check() function when you create your radio buttons. That's why you're seeing the windows pop up when you run your program.
You need to change this:
radio1 = Radiobutton(app, text=name, value=name, \
variable=relStatus, command=check(file_name, relStatus))
to this:
radio1 = Radiobutton(app, text=name, value=name, \
variable=relStatus, command=check)
See how check no longer has brackets? That's means you're passing the function name as an argument, instead of actually calling the function. Of course, you'll see an immediate problem is that you can no longer pass arguments to your callback function! That's a bigger issue. Here's a couple links to help get you started:
How to pass an argument to event handler in tkinter?
How can I pass arguments to Tkinter button's callback command?
Here is the solution:
Change this:
command=check(file_name, reStatus)
to this:
command = lambda: check(file_name, relStatus)
3) You don't actually pack() your radio buttons anywhere. Add something like this just after you create your radio buttons in your for loop: radio1.pack(side='top')
4) You have the same problem with your callback for your Click Here button. You need to change your command to not call the function, but just refer to it: command = execute_script
5) In execute_script(), make sure you import subprocessing
6) Are you sure you want yield instead of return in your execute_script() function?
7) In all your functions, you need to make sure that been_clicked is global.
I think if you fix these issues you'll be closer to getting what you're looking for. Good luck.!
Related
When i exit the tk window i get this long library error, and I have no idea how to fix it. the GUI itself and error message are below the code.
def golf_records()->None:
"""
This function prints the information
of golfer names and the score they have
"""
with open("golf.txt", "r") as myfile:
myfile_content = myfile.read()
GUI_print(myfile_content)
def GUI_print(data)->None:
"""
The function takes in a string found in golf.txt file
and prints it out through tkinter GUI
"""
my_label = Label(root,text = str(data),font=("ariel",15)).place(x=250,y=120)
root = Tk()
root.geometry("600x600")
#Lables
header = Label(root,text = "Current Golf Records",font=("ariel",15,"bold")).place(x=150,y=20)
header = Label(root,text = "-----------------------------",font=("ariel",15)).place(x=150,y=50)
header = Label(root,text = "Press enter to list players data: ",font=("ariel",15)).place(x=150,y=80)
#Buttons
enter = Button(root, text="Enter", activebackground = "green", command=golf_records).place(x=440,y=80)
root.mainloop()
if __name__ == "__main__":
golf_records()
Basically imagine like your code is executed till the mainloop() and it pauses there till you break out of the loop, ie, by exiting the application. Then the code after the mainloop() gets executed, ie, the if statements which is true, hence running the function golf_record() which calls the GUI_print() which activates the label.
How to solve this? Im not sure what your trying to do with this code here, but if you can move that root.mainloop() to the end of the if statement, itll execute the function while the code is initially executed. Do let me know if you have more doubts. Or the better way would be to get rid of the function call golf_records() inside the if because you have a button that calls the function anyway.
So hello everyone. I have this problem with a GUI I'm making using Tkinter (Python module). So that is, I assign a command to the button, which is a function containing the things the program should do after clicking that button, and the command is simply to, when the button is pressed, change a variable. Well, that went fine just changing it, but when I tried to concatonate that value to a string to further be written into a file, it simply doesnt work. Here is the code.
import os
from tkinter import *
root = Tk()
gui = ""
_path = ""
def ButtonA_command():
gui = " -w"
print(gui)
def ButtonB_command():
gui = " "
print(gui)
def ButtonC_command():
bat_file = open("fatty.bat", "w")
bat_file.write("pyinstaller --onefile" + gui + " " + EntryA.get())
bat_file.close()
ButtonA = Button(root, text= "GUI", command= ButtonA_command)
ButtonB = Button(root, text= "CMD e GUI", command= ButtonB_command)
ButtonC = Button(root, text= "Criar", command= ButtonC_command)
EntryA = Entry(root)
ButtonA.pack()
ButtonB.pack()
EntryA.pack()
ButtonC.pack()
root.mainloop()
You need to define your functions as following. Otherwise, you are creating a local variable gui inside the scope of the function:
def ButtonA_command():
global gui
gui = " -w"
print(gui)
def ButtonB_command():
global gui
gui = " "
print(gui)
I see what is happening here. When you run ButtonA_command() a new variable called gui appears (so you have two variables called gui - one in your function and one outside of it). Use global gui as the first line of each function and it should work (does work on my pc).
Edit: other commenter was a bit faster :)
You need to write:
global gui
inside any function otherwise, it will treat it as a new local variable of that function.
When I created this module I first made the tkinter window (all of its settings globally) it worked as intended. I could run the module and the window worked, taking the input from the entry field and displaying the welcome or error message. But when I put them into a function, it stopped working correctly, as shown.
How the window looks when created globally, with the button and input working:
https://gyazo.com/ffcb16416b8a971c09bfa60ee9367bbd
How it looks when created inside the function:
https://gyazo.com/c8858a2793befafa41e71d1099f021d3
The error message pops up straight away, then the main window with the entry field but not the button.
Here's the code where I created the window and settings inside a function:
def userSign(userEntry):
userId = userEntry.get()
if userId.isdigit() == True and len(userId) == 4:
welcomeWindow = tkinter.Tk()
welcomeWindow.title("Welcome")
welcomeWindow.geometry("200x50")
welcome = tkinter.Label(master=welcomeWindow, text="Welcome "+userId,font=("Helvetica", 18, "bold"))
welcome.grid()
welcomeWindow.mainloop()
else:
errorWindow = tkinter.Tk()
errorWindow.title("ERROR")
errorWindow.geometry("500x50")
error = tkinter.Label(master=errorWindow, text="ERROR: "+userId +" DOES NOT MEET CRITERIA", font=("Helvetica", 18, "bold"))
error.grid()
userId=""
errorWindow.mainloop()
def show():
window = tkinter.Tk()
window.title("Sign In")
window.geometry("250x100")
signInPrompt = tkinter.Label(master = window, text = "Enter your ID to sign in")
signInPrompt.grid(column=0,row=2)
userEntry = tkinter.Entry(master = window)
userEntry.grid(column=0,row=4)
enterButton = tkinter.Button(master = window, text="Sign in", command=userSign(userEntry))
enterButton.grid(column=0,row=6)
window.mainloop()
How do I get it so that my window works correctly when created inside functions as this module needs to be called by a different, main module.
You are creating two instances of Tk() which is a bad idea. Instead use Toplevel() for additional windows.
When you create variables or widgets inside a function the names are in the local scope and not available outside the function. And whan the function ends they will be garbage colletced.
Also, as #fhdrsdg points out, problems in the button command.
I've got an interesting problem with the tk Entry widget. If I run the following test code,
from Tkinter import *
root =Tk()
def pfunc(self):
print Input.get()
f=Frame(root)
f.pack()
Input=Entry(f)
#Input.bind("<Return>",pfunc)
Input.pack()
root.mainloop()
I can properly enter into the widget and print to console; however the following code, as part of a larger GUI, does not allow me to click in the Entry boxes at all.
self.Tlabel = Label(self.TempFrame, text="Temp")
self.Tlabel.pack( side = LEFT)
self.Tenter = Entry(self.TempFrame,width=10, bd =5)
self.Tenter.bind("<Return>",self.getFlux)
self.Tenter.pack (side=RIGHT)
self.Flabel = Label(self.FluxFrame, text="Flux")
self.Flabel.pack( side = LEFT)
self.Fenter = Entry(self.FluxFrame, width=10, bd =5)
self.Fenter.bind("<Return>",self.getTemp)
self.Fenter.pack(side = RIGHT)
def getFlux(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Fenter.set(fit_data[0]*np.exp(fit_data[1]*int(self.Tenter.get())))
else:
self.Fenter.set("Invalid")
def getTemp(self):
for i in range(len(self.fit_tuples)):
if self.fit_tuples[i][0]==self.currentBFMdate and self.fit_tuples[i][1]==self.cell.get():
fit_data=self.fit_tuples[i][2]
self.Tenter.set(np.log(float(self.Fenter.get())/fit_data[0])/fit_data[1])
else:
self.Tenter.set("Invalid")
Furthermore, if I run both codes on a separate windows PC I have the same problem. The only difference I can possibly think of is that I am using instance variables within a class; but it seems that other widgets are bound and working properly.
Basically, the bind method is passing "Return" as a parameter to getTemp. As another user suggested, just add another parameter to the function.
If you use bind method, callback is called with an event object. You should add event parameter to the method/function. (See Events and Bindings - An Introduction to Tkinter )
So, rpelcae following lines:
def getFlux(self, event):
...
def getTemp(self, event):
...
The first program work unintentionally. Its parameter name should be event, not self.
I don't understand why the entry boxes under rackGUI.py in my code are static/won't allow anything to be entered. I believe all the Entry objects are instantiated correctly. I specified the textvariable as instances of the StringVar(). My gut tells me the problem lies in command argument in create_button instantiation but I'm not really sure why. I thought by setting command = lambda:function the function would not be called.
Upon clicking 'New' in the menu, main.py successfully calls rackGUI.create() which successfully calls input_form(). Clicking the button 'create_button' successfully calls drawRack which prints to the shell 'test'. I also added a test where I printed the type of value for each entry box i.e., print type(rack_name.get()) and this successfully returns type 'str'.
So again the main problem is that the entry box is static.
Below is my code:
config.py
"""
config.py
"""
import Tkinter as tk
import tkMessageBox as tkmb
#setup
root = tk.Tk()
root.title("TLA Database Tool")
frame = tk.Frame(height = 300, width = 250)
frame.pack()
main.py
#main.py
from config import *
import rackGUI
def createRackTemplate():
rackGUI.create()
def loadRackTemplate():
rackGUI.load()
menubar = tk.Menu(root)
filemenu = tk.Menu(menubar)
filemenu.add_command(label = "New", command = createRackTemplate)
filemenu.add_command(label = "Load", command = loadRackTemplate)
menubar.add_cascade(label = "File", menu = filemenu)
tkmb.showinfo("Welcome", "Under File click New to create a new rack template.\n\
Click load to load rack template.")
root.config(menu = menubar)
root.mainloop()
rackGUI.py
"""
rackGUI.py
"""
from config import *
def input_form():
form_frame = tk.Frame(frame)
form_frame.pack()
tk.Label(form_frame, text = "Rack Template Name (e.g., Knox Type 4)").pack()
rack_name = tk.Entry(form_frame, textvariable = tk.StringVar())
rack_name.pack()
tk.Label(form_frame, text = "Dimensions").pack()
tk.Label(form_frame, text = "#rack rows").pack()
num_rack_rows = tk.Entry(form_frame, textvariable = tk.StringVar())
num_rack_rows.pack()
tk.Label(form_frame, text = "#nodes per row").pack()
num_slots = tk.Entry(form_frame, textvariable = tk.StringVar())
num_slots.pack()
create_button = tk.Button(form_frame, text = "Create!",\
command = lambda: drawRack(rack_name, num_rack_rows, num_slots))
create_button.pack()
def drawRack(rack_name, num_rack_rows, num_slots):
print rack_name.get(), num_rack_rows.get(), num_slots.get()
def create():
input_form()
def load():
pass
For anyone who comes here after me, my solution ended up being
root.overrideredirect(True)
Working fine on Windows, but causing this text entry problem on Mac.
I actually found the problem there. The issue seems to be the focus of the windows, since you're using a messagebox.
In my script I just had put a root.update() before opening another window (in my case a filedialog) and everything worked fine. There's an already existing issue for that: https://bugs.python.org/issue42867#msg384785