PyQt: Composite Widgets
So the past few nights I was racking my brain a bit to get multiple widgets adding to a listview. I wanted to see a list of animations, each item in the list needed to have clickable buttons, and special labels.
I scoured the internets, and dusted off my old trusty ‘Rapid GUI Programming with Python and QT‘ book, I got the idea for the above from the ‘Composite Widgets’ chapter subsection, though they don’t use setItemWidget to insert a composite widget into another widget.
Here is what my QtDesigner file looked like:
I wanted to dynamically load a UI file of a custom widget and compile it with the UIC module. I first looked at making a delegate, but I just could not get that working, if you have done this with a delegate, let me know in the comments! (From the docs, it seems delegates cannot be composites of multiple widgets)
In the end I used pyuic4 to compile the above UI file into a python code, I dumped this, minus the form/window code, into a class I derive from QWidget:
class animItemWidget(QtGui.QWidget): def __init__(self, parent=None): super(animItemWidget, self).__init__() self.horizontalLayout_4 = QtGui.QHBoxLayout(self) self.horizontalLayout_4.setSpacing(2) self.horizontalLayout_4.setMargin(3) #blah, blah, blah |
At the bottom of that length UI frenzy of an init, let’s connect a button to a function:
self.connect(self.button02, QtCore.SIGNAL("clicked()"), self.awesome) |
Now define that function, let’s just print that the animation that the widget in the list whose button you clicked is AWESOME:
def awesome(self): print self.label.text() + ' is awesome!' |
This could do anything with the anim name or various data bound to this object, like check out/sync a file from Perforce, load a file in Maya, etc.
Now let’s make our main window. We are going to use setItemWidget to insert our animItemWidget into the QListWidget called ‘list’. Notice that I have access to every UI element in the composite widget.
from PyQt4 import QtGui, QtCore, uic class uiControlTest(QtGui.QMainWindow): def __init__(self): super(uiControlTest, self).__init__() self.ui = uic.loadUi('uiControlTest.ui') self.ui.show() for i in range(0,100): wid = animItemWidget() wid.label_2.setText('Last edited by chrise @2014.06.21:23:17') wid.label.setText('Animation ' + str(i)) wid2 = QtGui.QListWidgetItem() wid2.setSizeHint(QtCore.QSize(100, 40)) self.ui.list.addItem(wid2) self.ui.list.setItemWidget(wid2, wid) |
Now, of course, in my example I just quickly made a bunch of widgets, so their names are all default, but you get the idea. If you have a better way to do this, perhaps something more performant, please let me know in the comments.
Note: It looks like that book is freely available on a college class website, save yourself 50 bucks: http://www.cs.washington.edu/research/projects/urbansim/books/pyqt-book.pdf
While searching for something else, I just came across a video from Marco Giordano showing how to do something similar:
Comment by admin — 2014/05/31 @ 8:55 PM
Hey I just created myself a custom Form ui via designer, compiled it with pysideuic, imported the class and set it up like this:
class xWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(xWidget, self).__init__(parent)
self.ui = Ui_xWidget()
self.ui.setupUi(self)
…
where ‘Ui_xWidget’ is the class imported from the pysideuic generated file. Well this is pretty much the same as you have instead the whole thing is out of Qtdesigner.
It works but I wonder why these generated things always come as (object) wouldn’t it be simpler if they had the intended class already (QtGui.QWidget) in that case?
Comment by ëRiC — 2015/04/01 @ 12:59 PM