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.
Related
An answer to this question might be either about tkinter or parallel loops. I am not sure which solution will be the best. I need your advice.
I have a while True loop, which goes infinitely. Each loop takes around 5 seconds or more, and I wanted to create a GUI button (skip and stop_skip buttons). If the skip button is clicked by a user, it will skip the current while loop and move on to the next while loop until 'stop_skip' button is clicked. So, the skip button will be working as 'continue' in a loop.
Since each loop takes around 5 seconds, I want it to check the variable frequently in the loop. I found that tk.after() may work in every x milliseconds, and some people mentioned tk.update_idletasks() and tk.update(). I tried them, but they just show the box when the loop begins and freeze for the rest 5 seconds. If nothing can work like this (performing a function repetitively with intervals in a while loop), I will probably put if conditions multiple times during the while loop.
The script I want may look like..
show_tk_gui()
while True:
check_tk_if_skip_button_clicked(interval==1000) #if clicked, continue to the next while loop until 'stop_skip' button is clicked.
performing_5_sec_calculation()
Is there any way that can make this possible? Not using GUI option is also acceptable.
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?
Suppose I have a lovely window full of tkinter widgets all set with a function. One of these many widgets is a button. When this button is pressed, I want to 'move on to the next screen'. The next screen is in another function(including all the widgets I want to appear on that screen). I have tried to simply run the next procedure from the button, but If it does run correctly, it only adds the widgets to the existing window, and you end up with both screen#1 and screen#2 jumbled together. I have a feeling I need to use destroy, but I'm not sure how to do such, as the only way I could come up with was to group all the widgets in window 1 together in a frame, and destroy it, but I cant get access to destroy the frame from within function #2, as its a variable only within function/window #1. Sorry if that's confusing, The other option is the source, but there's a ton of widgets and other windows in progress which leads me to believe that would be even more confusing.
The simplest thing is to have your function create a single frame, and then place all of the widgets in that frame. The frame can then be placed in the main window such that it fills the whole window. Then, to delete everything you simply need to delete that one frame.
Another way to "move on to the next screen" is to use this same method, but create all of the frames ahead of time. You can stack these frames on top of each other, and use lift and/or lower to determine which one is on top. The one on top will obscure the ones below.
For an example of stacking, see Switch between two frames in tkinter
As for the problem of frame2 not knowing how to destroy frame1, you simply need to pass in a reference to the existing frame when creating a new frame, or pass in a reference to a "controller" - a function that knows about all the frames. You then ask the controller to delete the current frame, and the controller will know what the current frame is.
A button calling a function that deletes all existing frames and rebuilds another sounds like a design flaw. The propensity for errors (forgetting to delete certain elements in some places of the code etc) is pretty large.
If you don't have an insane number of UI elements, I suggest creating them all at once, and hiding/showing various elements as necessary.
Take a look at this SO answer for how you might go about creating GUI elements that can be shown/hidden, and how the callback function might look.
Edit: If you really need to do it based on these functions, then I guess an alternative approach might be this:
Say 'top_frame' is the frame that includes all your widgets which you want to destroy when you run function #2. Change all of your GUI elements in function #1 so that when you create them, you explicitly pass them top_frame so that they have a link to it (self.top_frame = top_frame). This means your button will also have an attribute self.top_frame. You pass that as one of the arguments to function #2, and function #2 now can refer to top_frame and destroy it.
But definitely prone to error and probably slower due to all the creation/destruction of GUI elements. I recommend going through the code in the answer above when you have the time, it really is a much better solution.
I've done a few searches but I couldn't find anything about this topic. Perhaps because it is common programmer knowledge (I'm not a programmer, I've learned from necessity), or because I'm going about it the wrong way.
I would like ideas/suggestions on how to manage button states for a GUI. For example, if I have a program which allows the user to import and process data, then certain functions should be inaccessible until the data has been imported successfully, or if they want to graph certain data, they need to select which data to graph before hitting the 'graph' or 'export' button. Even in the simple programs I've built these relationships seems to get complicated quickly. It seems simple to say "User shouldn't be able to hit button 'A' until 'B' and 'C' have been completed, then 'A' should be disabled if button 'D' or the 'Cancel' button. But that's a lot to track for one button. Thus far, I've tried two things:
Changing/Checking button states in the callback functions for the button. So in the above example, I would have code in buttons B's and C's callback to check if A should be enabled. And in buttons D's and Cancel's callbacks I would have code to disable button A. This gets complicated quickly and is difficult to maintain as code changes.
Setting boolean variables in every buttons callback (or just checking the states later using cget()) and checking the variables in a polling function to determine which buttons should be enabled or disabled.
I'm just not sure about this. I would like to make code as short and easy to understand as possible (and easy to edit later), but I don't like the idea of polling all the button states every few hundred milliseconds just for button 'management'. You can extend the same idea to check boxes, menu items, etc... but I'd like to here what others have done and why they do it the way they do.
You are only changing button states based on events, right? There is no reason to 'poll' to see if a button state has changed. What you can do is build a function which does all of the calling for you, then call it with something like disable_buttons([okButton, graphButton, printButton]). When an event takes place that modifies the appropriate user interface options (such as importing data), have another function that turns them on: enable_buttons([graphButton]). You could do this with each object's methods, of course, but making a wrapper allows you to be consistent throughout your application.
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.