I have a wx.ListBox in a python program, and I wan't to change out the list in it on a wx.Timer update. I have the timer working, I just don't know how to change out the list that it displays.
Here's an example for modifying a ListBox.
Generally, it uses the Append and Clear methods of ListBox. You can call those in your timer handler.
Since ListBox derives from ItemContainer, see more item modification methods here.
Related
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 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
My GUI consists of a wx.ListCtrl on the left, with a list of objects to edit, and a set of wx.TextCtrls on the right, for editing the selected object.
My strategy for implementing this was:
On a textbox's wx.EVT_KILL_FOCUS , update the relevant attribute of the currently selected object
On the list's wx.EVT_LIST_ITEM_DESELECTED, either hide the textboxes or blank them out and disable them (needed for when the user clicks the blank space in the list control)
On the list's wx.EVT_LIST_ITEM_SELECTED, populate the text controls with the values of the selected object's attributes
With this setup, there are 3 use cases, and 2 of them work:
When the user is clicking/tabbing between textboxes, the correct wx.EVT_KILL_FOCUSs occur, and the attributes get updated.
When the user clicks from a textbox into the blank space in the list, that's OK as well: first wx.EVT_KILL_FOCUS causes the attribute to update, and then wx.EVT_LIST_ITEM_DESELECTED hides the textboxes.
The problem is when the user clicks directly from a textbox to another object in the list control. The order of events in this case is wx.EVT_LIST_ITEM_DESELECTED, wx.EVT_LIST_ITEM_SELECTED, and then finally wx.EVT_KILL_FOCUS. You can probably see the problem: by the time the method that updates attributes is called, a new object has already been selected and the textboxes have been populated with new values.
So I know exactly what the problem is, but I can't come up with a nice, clean way to fix it. Ideally I'd like to be able to change the order of the wx events (putting wx.EVT_KILL_FOCUS at the front), but I doubt that's possible. Is there some other obvious solution I'm missing?
wx.EVT_LIST_ITEM_DESELECTED will only fire when the user changes the selected object in the list box. This serves the same purpose as losing focus on the text box. Call the update routines from that event as well. To skip the subsequent wx.EVT_KILL_FOCUS from the text box set a "isDirty" attribute in the parent object after you update the attributes. You can check the isDirty value anytime to confirm there are changes to commit. This attribute would have to be reset when you populate the text boxes for the new selection and then set during other textbox events.
It looks to me like you are trying to re-implement from scratch the functionality of wxListbook. It seems like a lot of work, perhaps you can use wxListbook
to do what you need.
http://docs.wxwidgets.org/2.9.4/classwx_listbook.html
I'm new to Python and I'm trying to create a simple GUI using Tkinter.
So often in many user interfaces, hitting the tab button will change the focus from one Text widget to another. Whenever I'm in a Text widget, tab only indents the text cursor.
Does anyone know if this is configurable?
This is very easy to do with Tkinter.
There are a couple of things that have to happen to make this work. First, you need to make sure that the standard behavior doesn't happen. That is, you don't want tab to both insert a tab and move focus to the next widget. By default events are processed by a specific widget prior to where the standard behavior occurs (typically in class bindings). Tk has a simple built-in mechanism to stop events from further processing.
Second, you need to make sure you send focus to the appropriate widget. There is built-in support for determining what the next widget is.
For example:
def focus_next_window(event):
event.widget.tk_focusNext().focus()
return("break")
text_widget=Text(...)
text_widget.bind("<Tab>", focus_next_window)
Important points about this code:
The method tk_focusNext() returns the next widget in the keyboard traversal hierarchy.
the method focus() sets the focus to that widget
returning "break" is critical in that it prevents the class binding from firing. It is this class binding that inserts the tab character, which you don't want.
If you want this behavior for all text widgets in an application you can use the bind_class() method instead of bind() to make this binding affect all text widgets.
You can also have the binding send focus to a very specific widget but I recommend sticking with the default traversal order, then make sure the traversal order is correct.
It is really simple in PyQt4 simply use this one single line below and you will be able to change focus by pressing tab button:
self.textEdit.setTabChangesFocus(True)
The focus traversal is somewhat customizable, usually letting the X windows manager handle it (with focus follows mouse, or click). According to the manual it should be possible to bind an event to the key press event, for tab presses, and triggering a focusNext event in those cases.