PyQt4 - Custom widget class structure?
Asked Answered
I

2

6

During regular intervals of my program, a block (of 3 stacked) widgets need to be added to a horizontal layout. Since the widgets within each block are important to eachother, I wish to encapsulate each stack as it's own widget (making the layout adding business much easier).

I'm having trouble getting PyQt4 to recognise my 'stack' as a widget.

I made the widget stack in Qt Designer (as form: widget) and converted it to a .py via
'pyuic4 DesignerFile.ui > ClassFile.py'.

Now I can't seem to add this 'stack' (parent widget of 3 child widgets) to the layout via .addWidget( Class ).

I tried constructing a super class of the stack class (because I need to add more functionality to the stack) but the instance of the class is either...

  • Not recognised as a widget
  • Invisible
  • defective because I've no idea on how to structure the super class.

Here's what I'm failing with at the moment (though it's about the 8th class structure I've tried):

from ClassFile import ClassCode

class Stack(ClassCode):
    def __init__(self,parent= None):
        QtGui.QWidget.__init__(self,parent)

Could somebody help me structure this or lead me to some good examples?
(I've mimicked the code in both the following sources but with no avail!!
http://lateral.netmanagers.com.ar/stories/27.html#what-you-need-to-follow-the-tutorial
http://zetcode.com/tutorials/pyqt4/customwidgets/ )

Thanks!

Specs:
python 2.7.2
PyQt4
Windows 7

Idem answered 17/12, 2011 at 12:9 Comment(0)
W
5

When you compile a python module from a ui file with the default options, it will (amongst other things) generate a simple "setup" class. In outline, the setup class will look like this:

class Ui_ClassCode(object):
    def setupUi(self, ClassCode):
        ClassCode.setObjectName("ClassCode")
        # bunch of boiler-plate ui code goes here
        self.retranslateUi(ClassCode)
        QtCore.QMetaObject.connectSlotsByName(ClassCode)

    def retranslateUi(self, ClassCode):
        pass

There are a couple of issues to notice here that are relevant to the question.

Firstly, the setup class is designed to be used as a mixin rather than as a direct subclass. It's task is to "inject" ui into a host widget that is passed to the setupUI method.

Secondly, the setup class is given an ugly, unpythonic identifier that is created by prepending "Ui_" to the objectName property that was set in Designer.

Fortunately, pyuic4 provides a way to bypass these two issues. All that's required is to use the -w option when compiling the python module from the ui file:

pyuic4 -w designerfile.ui > classfile.py

This will add a wrapper class that (1) can be easily subclassed, and (2) has the class-name that you damn well gave it in Qt Designer.

The wrapper class will look something like this:

class ClassCode(QtGui.QWidget, Ui_ClassCode):
    def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
        QtGui.QWidget.__init__(self, parent, f)

        self.setupUi(self)

As you can see, it doesn't do anything special: you could easily replicate what it does in your own code. But, IMO, it does make the compiled modules much more intuitive to use.

For example:

from PyQt4 import QtGui, QtCore
from classfile import ClassCode

class Stack(ClassCode):
    def __init__(self, parent=None):
        ClassCode.__init__(self, parent)

class Window(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.stack = Stack(self)
        self.setCentralWidget(self.stack)
Wilton answered 17/12, 2011 at 19:2 Comment(2)
Is it possible to be able to pass a string to the constructor (when instantiation the widget) to be used via the widget? Eg: x = Stack('cat')Idem
@AntiEarth. Sure: you can design your Stack subclass in whatever way you like - just like subclassing any other Qt widget. So the signature of Stack.__init__ could be changed to e.g. __init__(self, name, parent=None).Wilton
M
1

First, it's more appropriate to call the parent __init__ with the use of super. That will ensure the method in the proper super class is invoked. Second, when using a class constructed with pyuic, you need to call self.setupUi(self) from your __init__ method. And lastly, you need to make sure and multiple inherit from both the proper Qt class and the pyuic generated class (which is really more of a mixin).

So, something like this:

from ClassFile import ClassCode

class Stack(QtGui.QWidget, ClassCode):
    def __init__(self,parent= None):
        super(Stack, self).__init__(parent)
        self.setupUi(self)
Melodist answered 17/12, 2011 at 17:36 Comment(1)
He actually should be inheriting first from the desired QWidget, and secondly from the ClassCode that was generated by pyuicAlmaraz

© 2022 - 2024 — McMap. All rights reserved.