I understand that in tkinter once mainloop() has been run, no code after it will run until the window has been destroyed. I have found that the common solution is to use tk.after to call a function repeatedly at certain intervals. However I am using a for loop, and every time it loops it updates a variable which I want to see change in the GUI.
PB=myProgressBar()
for i in range (0, len(dataset)):
performfunction
PB.update(i)
PB.quit()
The aim is while performing an operation on each item in the dataset, the GUI will show how far the program is.
Within my progress bar class I have tried using a tk.IntVar as my ttk.ProgressBar value and setting the value through the PB.update(i) to update it.
I have also tried using ProgressBar['value']=i in my update method of the progress bar class.
In both cases if i run mainloop() before the loop, the for loop doesn't run (as you'd expect) but I'm not sure how to run mainloop and get update the GUI without a messy tk.after function that would probably have to involve a self.i value (and then do self.i+=1 at the end of the function that replaces the loop).
Is there a clean 'pythonic' way to do this?
Related
I would like to ask how could I add dynamically some widgets in my application one by one and not all at once. Those widgets are added in a for loop which contains the add_widget() command, and is triggered by a button.
So I would like to know if there is a way for the output to be shown gradually, and not all at once, in the end of the execution. Initially I tried to add a delay inside the for loop, but I'm afraid it has to do with the way the output is built each time.
EDIT: Well, it seems that I hadn't understood well the use of Clock.schedule_interval and Clock.schedule_once, so what I had tried with them (or with time.sleep) didn't succeed at all. But obviously, this was the solution to my problem.
Use Clock.schedule_interval or Clock.schedule_once to schedule each iteration of the loop at your desired time spacing.
This question is a follow up to:
Run long process continously using Tkinter (Python 2.7)
In the previous topic i asked about running a funtion continuously until a button is pressed. This funtion takes a long time to complete, and basically i wanted to stop the process (don't repeat the function) if a button was pressed.
I got an answer to my question, but i was wondering if this is possible using the Checkbutton function.
I know i can call a function using the Checkbutton (using command=[funtionname]), but what i need is a way to call a function continuously while the Checkbutton is checked, and stop when it's unchecked. Is this possible using tkinter?
Thanks in advance for any answers,
Harm
You mentioned that the process is a repeated process. You can just check the state of the button at every repeat with var.get() (assuming that the var is the variable of the checkbutton) and break out of the function if it is unchecked.
This is not a really elegant solution, and if a repeat takes a long time, the last "round" will still finish after the uncheck, so it is not immediate.
I have this following Python Tkinter code which redraw the label every 10 second. My question is , to me it seems like it is drawing the new label over and over again over the old label. So, eventually, after a few hours there will be hundreds of drawing overlapping (at least from what i understand). Will this use more memory or cause problem?
import Tkinter as tk
import threading
def Draw():
frame=tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
text=tk.Label(frame,text='HELLO')
text.pack()
def Refresher():
print 'refreshing'
Draw()
threading.Timer(10, Refresher).start()
root=tk.Tk()
Refresher()
root.mainloop()
Here in my example, i am just using a single label.I am aware that i can use textvariable to update the text of the label or even text.config. But what am actually doing is to refresh a grid of label(like a table)+buttons and stuffs to match with the latest data available.
From my beginner understanding, if i wrote this Draw() function as class, i can destroy the frame by using frame.destroy whenever i execute Refresher function. But the code i currently have is written in functions without class ( i don't wish to rewrite the whole code into class).
The other option i can think of is to declare frame in the Draw() as global and use frame.destroy() ( which i reluctant to do as this could cause name conflict if i have too many frames (which i do))
If overdrawing over the old drawing doesn't cause any problem (except that i can't see the old drawing), i can live with that.
These are all just my beginner thoughts. Should i destroy the frame before redraw the updated one? if so, in what way should i destroy it if the code structure is just like in my sample code? Or overdrawing the old label is fine?
EDIT
Someone mentioned that python tkinter is not thread safe and my code will likely to fail randomly.
I actually took this link as a reference to use threading as my solution and i didn't find anything about thread safety in that post.
I am wondering what are the general cases that i should not use threading and what are the general cases i could use threading?
The correct way to run a function or update a label in tkinter is to use the after method. This puts an event on the event queue to be executed at some time in the future. If you have a function that does some work, then puts itself back on the event queue, you have created something that will run forever.
Here's a quick example based on your example:
import Tkinter as tk
import time
def Draw():
global text
frame=tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
text=tk.Label(frame,text='HELLO')
text.pack()
def Refresher():
global text
text.configure(text=time.asctime())
root.after(1000, Refresher) # every second...
root=tk.Tk()
Draw()
Refresher()
root.mainloop()
There are a lot of things I would change about that program from a coding style point of view, but I wanted to keep it as close to your original question as possible. The point is, you can use after to call a function that updates the label without having to create new labels. Plus, that function can arrange for itself to be called again at some interval. In this example I picked one second just so that the effect is easier to see.
You also asked "I am wondering what are the general cases that i should not use threading and what are the general cases i could use threading?"
To put a blunt point on it, you should never use threading if you have to ask a question about when to use threading. Threading is an advanced technique, it is complicated, and it easy to get wrong. It's quite possible for threading to make your program slower rather than faster. It has subtle consequences, such as your programs failing mysteriously if you do things that aren't thread safe.
To be more specific to your situation: you should avoid using threads with tkinter. You can use them, but you can't access widgets from these other threads. If you need to do something with a widget, you must put an instruction in a thread-safe queue, and then in the main thread you need to periodically check that queue to see if there's an instruction to be processed. There are examples of that on this website if you search for them.
If all that sounds complicated, it is. For most of the GUIs you write, you won't need to worry about that. If you can take large processes and break them down into chunks that execute in 100 ms or less, you only need after, and never need threads.
To allow the cleanup with minimal code changes, you could pass previous frames explicitly:
import Tkinter as tk
def Draw(oldframe=None):
frame = tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
tk.Label(frame, text='HELLO').pack()
frame.pack()
if oldframe is not None:
oldframe.destroy() # cleanup
return frame
def Refresher(frame=None):
print 'refreshing'
frame = Draw(frame)
frame.after(10000, Refresher, frame) # refresh in 10 seconds
root = tk.Tk()
Refresher()
root.mainloop()
I am developing a programme using python & wxPython. I have a listbox, and I need for it to be updated live to be used as a log.
I have done this simply with the Append() function, but the text added to the listbox is not shown until the end of the procedure, instead of being shown when the Append command is executed. I know this because after each insertion I print the size of the listbox.
def writeLog(self, text):
self.log.Append(text)
print self.log.GetStrings().__len__()
Right now, for checking purposes, I am calling a script that has the following code:
parent.writeLog("aaaaaa")
sleep(1)
parent.writeLog("aaaaaa")
sleep(1)
parent.writeLog("aaaaaa")
I have tried these answers but I couldn't make them work for me:
Update a ListBox in wxPython
wxPython: Update wx.ListBox list
So, How can I see the listBox updated in the screen right after the writeLog function is called? Is it possible? Thanks!
You have a few options here, the easiest perhaps is to call wx.Yield() when you want the ui to be updated, so after your Append calls
Another solution would be to to get any text that needs adding in a separate thread, and then send it back to the main thread via a custom event or pubsub which can then Append to the listbox
I am writing a timer program in Python using PyGTK. It is precise to the hundredths place. Right now, I am using a constantly updated label. This is a problem, because if I resize the window while the timer is running, Pango more often than not throws some crazy error and my program terminates. It's not always the same error, but different ones that I assume are some form of failed draw. Also, the label updates slower and slower as I increase the font size.
So, I am wondering if there is a more correct way to display the timer. Is there a more stable method than constantly updating a label?
Updating a label should work perfectly reliably, so I suspect you're doing something else wrong. Are you using threads? What does your code look like? How small can you condense your program (by removing functionality, not by obfuscating the code), without making the problem go away?
I figured out the problem. It was indeed a problem with the threads. I never would've guessed that myself. The trick is to use gobject.timeout_add() to create a timer instead of a threaded loop. Here is some information about gobject.timeout_add():
http://faq.pygtk.org/index.py?req=show&file=faq01.021.htp
Don't forget to have your function return True, or the timer will stop.