Get a layout's widgets in PyQT - python

I have a QVBoxLayout that I've added a few widgets to, via addWidget(). I need to now delete those widgets, and it seems I need to use removeWidget() (which takes in a widget to be removed) to do that.
I thought that calling children() or findChildren(QWidget) on my layout would return a list of the widgets I've added into it; I'm in the debugger, though, and am just receiving empty lists.
Am I terribly misunderstanding something? I've just started doing PyQT this last week and have mostly been learning through trial and error with the API docs.

To get a widget from a QLayout, you have to call its itemAt(index) method.
As the name of this method implies, it will return an item instead of a widget. Calling widget() on the result will finally give you the widget:
myWidget = self.myLayout.itemAt(index).widget()
To remove a widget, set the parent widget to None:
myWidget.setParent(None)
Also really helpfull is the QLayout count() method. To find and delete all contents of a layout:
index = myLayout.count()
while(index >= 0):
myWidget = myLayout.itemAt(index).widget()
myWidget.setParent(None)
index -=1

That's odd. My understanding is that adding widgets via addWidget transfers ownership to the layout so calling children() ought to work.
However, as an alternative you could loop over the layout items by using count() and itemAt(int) to supply a QLayoutItem to removeItem(QLayoutItem*).
Edit:
I've just tried addWidget with a straight C++ test app. and it doesn't transfer QObject ownership to the layout so children() is indeed an empty list. The docs clearly say that ownership is transferred though...
Edit 2:
Okay, it looks as though it transfers ownership to the widget that has that layout (which is not what the docs said). That makes the items in the layout siblings of the layout itself in the QObject hierarchy! It's therefore easier to stick with count and itemAt.

Related

Trouble with object-based QSignalMapper

I am trying to set up a signal-slot arrangement in PyQt where the signal transmits a lot of information. I want to use a QObject as a carrier by setting the various information I want to transmit as attributes of the QObject. In theory, this should work - there is a QSignalMapper.setMapping() overload which takes a sender and a QObject as arguments.
Here's the reduced code:
self.mapper = QtCore.QSignalMapper()
self.timeline.finished.connect(self.mapper.map)
carrier = QtCore.QObject()
carrier.contents = (item1, item2)
self.mapper.setMapping(self.timeline, carrier)
self.portalMapper.mapped.connect(self.report)
def report(self, carrierObject):
print 'Triggered'
Unfortunately it doesn't work. I've traced the problem to the setMapping function by process of elimination.
This same scheme will work just fine if I switch out the QObject with an int. It also doesn't have anything to do with the attributes I added to the QObject - using a fresh-out-of-the-box QObject causes the same issue.
It seems like there is something going on here with this specific overload of the setMapping function. Any ideas about what the issue is here?
Thanks to #ekhumoro's suggestion to skip the QSignalMapper approach entirely and just create a custom signal. Worked like a charm.

PySide get QObject's parent anywhere in its hierarchy

Consider I have a class with this kind of structure (CustomClass may/may not be on top of the hierarchy):
CustomClass
.. QTabWidget
.... QWidget
...... QTreeView
In QTreeView I have a function that is trying to refer back to CustomClass. Right now in order to do this I need to do: self.parent().parent().parent().
Although this works it just feels very sloppy, and if I ever need to change the structure this will fail. Is there some other way to get CustomClass? Usually I would pass an instance of it during its constructor which I can call directly, but wondering what's the best practice to go about this.
The question title leads to a very direct answer. The window() method on QWidget returns the ancestor widget that has (or could have) a window-system frame: typically the "top-level" widget that you want to find. The docs give changing the window title as a canonical use case:
self.window().setWindowTitle(newTitle)
It returns self if the Qwidget is a window itself.
However, the text of your question and your own answer give an alternative interpretation: you might alternatively want to find the ancestor that is of a particular type even if it is not the top level widget. In this case, iterating up through the ancestors is typically the right solution, pretty much as you have written for yourself. So that would be something like:
customClassInst = self.parent()
while customClassInst is not None and not isinstance(customClassInst,CustomClass):
customClassInst = customClassInst.parent()
Note that you should usually use isinstance rather than type() == because the former correctly handles sub-classes.
Also note that this code will return None if no CustomClass is found which may or may not be what you want ...
This feels like a decent procedural way to get it:
customClassInst = self.parent()
while customClassInst is not None and type(customClassInst) != CustomClass:
customClassInst = customClassInst.parent()
Any other answers are still welcome :)

pygtk: overriding gtk.Paned.compute_position

I have a HPaned that one of its children changes its size requirement frequently, since its text changes. The result is that the pane moves every time the text is changed. I'd like to override the Paned.compute_position method so that the size of the child will not decrease, just increase. However, I can't find a way to override it. Even
class MyHPaned(gtk.HPaned):
def do_compute_position(self, allocation, child1_req, child2_req):
print "Hi"
return gtk.HPaned.compute_position(self, allocation, child1_req, child2_req)
gobject.type_register(MyHPaned)
doesn't work. Do you have a suggestion? Thanks!
Overriding gtk_paned_compute_position is not possible, since that function is not virtual in GTK itself. Also, gtk_paned_compute_position is marked as internal and deprecated and is not called anywhere from GTK+-2.24.x sources. I suspect it was only exported so that you could find out the position of the separator, not to affect it through overriding.
Instead of attempting to override HPaned.compute_position, you should place into the paned a single-child container (e.g. a child of gtk.Bin) that implements the desired resizing policy by hooking into the size-allocate signal and calling set_size_request with the desired size. This will be automatically respected by HPaned.

How to change the attributes for all objects inside a list in python(3)?

I am new to python and unfamiliar with manipulating python lists. I have a list of tkinter widgets, more specifically buttons, which have been added to my list btnList by using:
btnList.append(btn1)
btnList.append(btn2)
Normally to change an object's attribute value (the state attribute for example) one would use:
btn1.configure(state='disabled')
btn2.configure(state='disabled')
which would set the state attribute of both buttons to disabled,
Is there a way to change attributes for all of the objects contained in the list?
for example setting the state of each button to disabled?
There are a fair number of ways to express this, including some one-liners, but I think a simple for loop is the cleanest:
for button in btnList:
button.configure(state='disabled')
Simply loop over your list:
for button in btnList:
button.configure(state='disabled')
button will be assigned each button from your list in turn letting you call the configure() method on it.

Getting all items of QComboBox - PyQt4 (Python)

I have A LOT of QComboBoxes, and at a certain point, I need to fetch every item of a particular QComboBox to iterate through.
Although I could just have a list of items that correspond to the items in the QComboBox, I'd rather get them straight from the widget itself (there are a huge amount of QComboBoxes with many items each).
Is there any functions / methods that will do this for me?
(Eg:
QComboBoxName.allItems()
)
I've looked through the class reference but couldn't find anything relevant.
I've thought of a few messy methods, but I don't like them.
(Like iterating through the QComboBox by changing the index and getting the item, etc).
Python 2.7.1
IDLE 1.8
Windows 7
PyQt4
As far as I can tell, you can just reference an item using .itemText():
AllItems = [QComboBoxName.itemText(i) for i in range(QComboBoxName.count())]
Building on the accepted answer, you can actually give you combobox a method callable using combo_box.allItems(), by doing this:
setattr(combo_box, "allItems", lambda: [combo_box.itemText(i) for i in range(self.ui.combo_box.count())])
print(combo_box.allItems()) # Works just fine!
I believe it has to be done in the scope where combo_box was born, otherwise setattr fails.
Tested in PyQt5 and Python 3.7.

Categories