I'm writing a python program with a GUI. For the GUI I use tkinter. In my main window is a button and a label. If the button is pressed I want to change the label and start a request (In the code below, the request is represented as the for loop).
Unfortunally, when trying to change the label on the Button press the API/For-Loop has to finish. After they have finished the label changes it's text.
Example code
def changeText():
global label #This is just an example and is not used in the main programm
label.configure(text="Text Updated")
for i in range(0,10000000): #This should represent the API-Call
print("Doing something different here")
if __name__ == '__main__':
root = tk.Tk()
label = tk.Label(root, text="Text")
button = tk.Button(root,
text="Click to change text below",
command=changeText)
button.pack()
label.pack()
root.mainloop()
I've tried to fix the error by myself but I couldn't. I think it has something to do with the focus of the root window. After I start the API-Call/For-Loop I think the window loses its focus and gains it after the call/loop is finished.
Does somebody know how to change the text of a label, when doing something different (like Api/For) afterwards and could maybe tell me if my guess is right or wrong?
Related
I'm making an app in tkinter which uses the ttk.Scale widget to show the process of an mp3 song.
I have a function that I want to add buttons with the names of which (the buttons) should be relied on filenames. Therefore I've made this example:
from tkinter import Tk, Button
from tkinter.filedialog import askopenfilenames
from tkinter.ttk import Scale
from threading import Timer
root = Tk()
slider = Scale(root, from_=0, to=100, orient='horizontal')
slider.pack()
# slider is continuously set to a bigger number so that it keeps going
def update_slider(num):
slider.set(num)
num += 1
root.after(50, update_slider, num)
update_slider(num=0)
# this function creates buttons based on the files opened
def add_buttons():
# the 'X' button of this particular window slows down execution of update_slider function
files = askopenfilenames(title='Add Buttons')
for i in list(files):
Button(root, text=i).pack()
button = Button(root, text='Browse', command=lambda: Timer(0.1, add_buttons).start())
button.pack()
root.mainloop()
The problem I'm facing is that when I open the askopenfilenames dialog box or when I press its 'X' button, my slider which is running continuously in the background gets stuck, and as a result doesn't show the process correctly.
Here is a picture where I hold down the 'X' button and the ttk.Scale stops moving:
I've tried using threading to run the add_buttons function but the behavior of the program remains the same.
Can I edit the askopenfilenames dialog box with something similar like overrideredirect(True) so that I can make my own title bar and 'X' button and the events generated not to slow down my Scale?
Replying to:
I cannot reproduce the issue in Linux, the scale keeps moving no matter what I do with the filedialog window. So this may be an OS specific issue.
I'm aware that this problem doesn't appear on Linux. I faced the same problem with the root's close button and other Toplevels' close button, but I fixed it by replacing the title bar using overrideredirect(True).
Is there anything similar I can do with this askopenfilenames window?
I would like to change the value of label while the widget is open, so I actually see it change after some time.
I've been trying to use time.sleep but the first label won't show. And yeah I know that's because once the program runs the mainloop only takes the last value. Is it somehow possible to show me the first value and then wait 5 seconds and after that the label changes to something else. I've been searching for a solution. I didn't figure it out yet.
Try using root.after.
from Tkinter import *
root = Tk()
label = Label(root, text="this message will self-destruct in three seconds")
label.pack()
def bang():
label.config(text="this message has self-destructed.")
root.after(3000, bang)
root.mainloop()
I am expecting that on the click of button the label should be displayed and then the download should start. But in this below case, the frame gets stuck , say if download takes 10 min it gets stuck, and the label will be displayed only after 10.
def call():
test1()
test2()
def test1():
button.pack_forget()
label.pack()
def test2():
"script to start download which might take 10 min"
frame=Tk()
frame.pack()
button=Button(frame,text="clickme",command=call)
label=Label(frame,text="downloading...Please wait")
button.pack()
frame.mainloop()
.pack_forget() can be a bit fiddly sometimes.
Personally I prefer to pack everything into a sort of sub-master frame, which is the only direct child of the Tk() window and all contents are children of the sub-master frame.
It also seems that you're forgetting to pass the variables to call() and subsequently to test1. Due to your label and button being a local variable not global you won't be able to draw or destroy them without passing them to the routine. To do this within the restraints of the Tkinter button command system you need to use the function lambda: (Demonstrated below).
So in the case of your code, something like this:
from tkinter import *
def Call(frame, mainframe, label):
test1(frame, mainframe, label)
test2()
def test1(frame, mainframe, label)
mainframe.destroy()
mainframe = Frame(frame)
label.pack()
def test2()
print("Only here so the program can be copied straight from SO")
#Blegh, download stuff
frame = Tk()
mainframe = Frame(frame) #This is the sub-master frame
button = Button(mainframe, text="clickme", command=lambda:Call(frame, mainframe, label) #Using lambda here allows you to pass parameters to the definition within Tkinters buttons command restraints
label = Label(mainframe, text="downloading . . . Please wait")
button.pack()
frame.mainloop()
This code would function in a way where pressing the button will wipe the button from the screen and instead pack the label you wanted to pack, should you wish to keep the button for whatever reason you need only modify test1() by adding the line button.pack() and ensuring you also pass the variable as a parameter.
I am trying to create a simple popup text entry for the user in which a user enters a text and hits submit (a button). Upon clicking submit, I want the popup entry box to close off and continue on with the rest of the code. Following is a sample code for display that I borrowed from an old post here:
from Tkinter import *
root = Tk()
nameLabel = Label(root, text="Name")
ent = Entry(root, bd=5)
def getName():
print ent.get()
submit = Button(root, text ="Submit", command = getName)
nameLabel.pack()
ent.pack()
submit.pack(side = BOTTOM)
root.mainloop()
print "Rest of the code goes here"
I don't have much experience with Tkinter so I am not sure where and how exactly to call the appropriate functions for closing the entry box after the user hits 'Submit'. My guess is it would have to be inside the getName() function?
If I understand you correctly, then all you need to do is call the root window's destroy method at the end of the getName function:
def getName():
print ent.get()
root.destroy()
Doing so is equivalent to manually clicking the X button in the corner of the window.
Alternate method:
since there isn't much to your popup you could also eliminate several lines of code in your GUI, save some CPU and get pretty much the same output with this:
submitvariablename=raw_input('Please enter a Name')
same functionality and much faster, cleaner.
Just a thought.
I've really tried to find this out but have only discovered disconnected snippets such as tag_bind. I cannot work out how to use this (if indeed this is the right way forward).
I've got a Text widget into which the user can enter some text (such as a command or question) and I'd like to execute a function that reads that text then responds to it. Obviously I could provide a nearby Button widget which when clicked, reads the text in the Text widget. But I don't want people to have to move their hand from the keyboard to the mouse. I'd just like the callback to be triggered if they hit the Enter key. How do I do it please?
import Tkinter as tk
def on_enter(event):
widget = event.widget
print(widget.get())
root = tk.Tk()
entry = tk.Entry()
entry.pack(padx=5, pady=5)
entry.focus()
entry.bind('<Return>', on_enter)
root.mainloop()