Refreshing panel contents in wxPython - python

What is the approach to update widgets in a wxPanel based on events from other controls on same panel?
Scenario 1 is updating the list of a comboBox based on what has been selected from another comboBox , where both are in same panel.
Scenario 2 is showing a new control/widget in a panel based on an event.
Basically creating new controls is easy but I dont know how to refresh/update my panel so immedialtly shows them.

Scenario 1
To change the choices of a combobox self.cbx you can use any of the following methods:
self.cbx.SetItems(choices) where choices is the full list of choices.
self.cbx.SetString(n, string) that sets the string at position n.
InsertItems(items, pos) Inserts the list of strings in the items argument into the list box before the position in the pos argument.
Note that the method Set(choices) of listboxes does not exist for the list in comboboxes. You must use SetItems(choices) instead (this is not clearly indicated in some textbooks).
If you want these changes to occur as a result of a selection in another combobox self.cbx_1 , just get the event (self.Bind(wx.EVT_COMBOBOX, on_combo_1, self.cbx_1)) of the first combobox, process your data as you like in the corresponding self.on_combo method and use one of the above methods to modify the second combobox.
For example:
def on_combo_1(self, evt):
"append cbx_1 selection to cbx if not already in cbx"
selection = self.cbx_1.GetStringSelection()
cbx_choices = self.cbx.GetItems()
if selection not in cbx_choices:
cbx_choices.append(selection)
self.cbx.SetItems(cbx_choices)
The fact the comboboxes are in the same or different panel is irrelevant for that.
Scenario 2
Normally you put your widgets inside sizers. To hide or made visible elements on the sizer you call the methods Show, Hide or Layout:
Show(self, item, show=True, recursive=false)
Shows or hides an item managed by the sizer. To make a sizer item disappear or reappear, use Show followed by Layout. The item parameter can be either a window, a sizer, or the zero-based index of the item. Use the recursive parameter to show or hide an item in a subsizer. Returns True if the item was found.
Hide(self, item, recursive)
A convenience method for Show (item, False, recursive).
Layout(self)
This method will force the recalculation and layout of the items controlled by the sizer using the current space allocated to the sizer. Normally this is called automatically from the owning window's EVT_SIZE handler, but it is also useful to call it from user code when one of the items in a sizer change size, or items are added or removed.
References: wxPython in Action, Noel Rappin and Robin Dunn

For scenario one, you'd do something like the following (assuming the first combobox is bound to its EVT_COMBOBOX:
value = self.cboOne.GetValue()
if value == "something":
self.cboTwo.SetItems(someList)
For showing a new widget, you could create it and then use Show()/Hide() as necessary. If the widget is in a sizer, then use the Sizer's Append or Insert methods. It also has a Detach method that can be used to hide widgets or you just call Hide itself. See the documentation for more information: http://www.wxpython.org/docs/api/wx.Sizer-class.html

Related

Is it possible to destroy Tkinter widgets by pathname?

I have several widgets created in a loop, each given a sequential name, eg:
for item in itemlist:
myWidget=tk.Widget(root, name=item)
myWidget.pack()
Now I have widgets with pathnames like
.!mainapplication.!itemframe.item1
is it possible to use the pathname to destroy a widget?
I can't use myWidget.destroy() because then I can't target specific widgets.
If it's not possible I can create an array to hold the widgets and deal with them from there, but I was wondering if there was any way to do it this way.
If you save the return value of tk.Widget(...) to a list or dictionary, you can access any of the widgets without having to use the name. This is by far the most common and convenient way to manage widgets created in a loop.
Example:
widgets = []
for item in itemlist:
myWidget=tk.Widget(root, name=item)
myWidget.pack()
widgets.appen(widget)
...
for widget in widgets:
widget.destroy()
If you really need to convert a name to a widget, tkinter provides a method on every widget called nametowidget which accepts the name of a widget and returns the instance.
root.nametowidget('.!mainapplication.!itemframe.item1').destroy()

Kivy add widget at the end of layout

In the kivy doc there is the function add_widget(widget, index=0, canvas=None) and we must be able to set an index to position the widget.
index: int, defaults to 0
Index to insert the widget in the list. Notice that the default of 0 means the widget is inserted at the beginning of the list and will
thus appear under the other widgets. For a full discussion on the
index and widget hierarchy, please see the Widgets Programming Guide.
So first either index I try nothing change on the widget order. And then i would like to insert my widget at the end of the list and not at the beginning.
If you have some idea :)
I found the solution:
self.add_widget(your_widget, len(self.children))
If you print self.children you will see the object added at the end of the list and it will reverse the display order on the view.

How to modify tags in Tkinter Canvas by events

I am programmatically adding tag_binds to all objects on my canvas that have the tag "tag":
self.canvas.tag_bind("tag","<Button 2>",self.tag_highlight)
self.canvas contains all the objects that the user has added (all tagged with "tag".
However, in tag_highlight, I would simply like to have a handle on the actual element within the canvas. event.widget doesn't seem to help, its just a tuple of 4 floats that I cannot link to any canvas item. I have tried to use the following, to no avail:
self.canvas.find_closest(event.x,event.y)
What I would like to do, is have a handle like:
t=self.canvas.getitem(event.widget)
so that I can use it for example in:
self.canvas.Move(t,30,20)
print self.canvas.coords(t)
and so forth.
Maybe I am just missing the obvious?
You can use the tag "current", which refers to the "current" object. The current object is described like this in the official tk documentation:
The tag current is managed automatically by Tk; it applies to the
current item, which is the topmost item whose drawn area covers the
position of the mouse cursor (different item types interpret this in
varying ways; see the individual item type documentation for details).
If the mouse is not in the canvas widget or is not over an item, then
no item has the current tag.

PySide - Get list of all visible rows in a table

Given that I have an instance of QTableView (or a subclass thereof), connected to a subclass of QAbstractTableModel (or functionally equivalent model + view), is it possible to get a list of the indexes of all rows currently visible to the user (i.e. those not falling outside the current scroll range)?
It would be great if the solution scales to different window/screen sizes.
You can obtain the item position using QAbstractItemView::visualRect. It is in the viewport coordinates, so we need to check if it is in the viewport rect. Here is an example:
viewport_rect = QRect(QPoint(0, 0), self.view.viewport().size())
for row in range(0, self.model.rowCount()):
rect = self.view.visualRect(self.model.index(row, 0))
is_visible = viewport_rect.intersects(rect)
This example works only with one column, but you can add a for loop for iterate over all columns.
In this code items are considered visible if they are partially visible. If you want to get only items that are completely visible, use contains instead of intersects.

Getting multiple wx widget values with Event Handling

CODE: http://pastebin.com/W4uXmazw
I would like to memorize how to get values from any wx widget with event handling after clicking a wx.Button.
In my program i have two fields, the new filename and the contents.
What are the steps i have to take in order to get the values from each field?
From there, i can use pythons f.open and f.write methods to complete my application.
Thanks!
If you want to get value of a widget, then you need to make that widget accessible throughout the entire class. To do that, you need to make the variable for the widget into an instance variable. So instead of adding the text control directly to the sizer, you'll want to do something like this:
self.newfilename = wx.TextCtrl(panel,-1), 0, wx.TOP, 5)
self.contents = wx.TextCtrl(panel,-1,size=(390,150),style = wx.TE_MULTILINE|wx.TE_PROCESS_TAB)
Then in your button's event handler, you can just do something like this:
valueOne = self.newfilename.GetValue()
contents = self.contents.GetValue()
The other way to do it would be to use your panel. If you use "self.panel", then you could grab all its children via its GetChildren method and then iterate over the list and use Python's "isinstance" builtin to check what kind of widget you're accessing. If you have set the widget's name, you can check that too.

Categories