Reference Code – https://github.com/Fapiko/pyqt-testwidget
Why make custom widgets?
I’ve been working on a GUI MySQL client for a while now using PyQt for the GUI and, until now, have been at odds with how I want to proceed with separating my view and business logic. I really enjoy using the Qt Designer to build the layouts for me but came very close to abandoning it and building all of my layouts programmatically in order to be able to subclass the Qt GUI elements to stuff view related code directly into them rather than wrapping the classes output from the Qt Designer. Luckily I stumbled across a rather simple way to build custom widgets that can be used in Qt Designer without touching C++.
TestWidget End Game
The widget I created to test this out is simply a QLabel which I added a method to, ‘testMethod’, that takes no arguments and prints a message to standard out. I had some trouble finding a simple piece of example code when Googling around so this one just contains the bare minimum to get up and running.
Writing The Codez
All file paths I reference below will be relative to the root of the GitHub repo linked above.
The code is pretty small – the actual Qt Designer plugin only requires two files. Let’s start with the widget itself: widget/testwidget.py
from PyQt4.QtGui import QLabel class TestWidget(QLabel): def testMethod(self): print 'Just adding a method to our QLabel'
All this file is doing is subclassing the QLabel and adding the testMethod method.
The actual Qt Designer plugin contains a bit more meat. It is basically a bunch of metadata to tell Qt Designer what to display in the interface. I’m only overriding the bare minimum number of methods here to make it work.
from PyQt4.QtDesigner import QPyDesignerCustomWidgetPlugin from PyQt4.QtGui import QIcon from testwidget import TestWidget class TestWidgetPlugin(QPyDesignerCustomWidgetPlugin): def __init__(self, parent=None): QPyDesignerCustomWidgetPlugin.__init__(self) def name(self): return 'TestWidget' def group(self): return 'Fapiko.Com Example Widgets' def icon(self): return QIcon() def isContainer(self): return False def includeFile(self): return 'testwidget' def toolTip(self): return 'Arnold Facepalmer' def whatsThis(self): return 'Facepalm all day' def createWidget(self, parent): return TestWidget(parent)
I’ll briefly describe each of the methods and what they do:
- name – Sets the name of the widget which appears in Qt Designer
- group – Adds a category which widgets will be grouped into in the Qt Designer widget panel
- icon – Sets the icon to display in the Qt Designer for this widget. Returning an empty QIcon as I’m doing here will cause it to display the default icon.
- isContainer – Returning True from this method will allow you to add widgets to this widget
- includeFile – The name of the file containing the actual code for the widget to be imported.
- createWidget – Initialization code used when instantiating the widget.
I’ve included a few helper files in the repo as well. Here’s a brief description of what they do:
- launch_designer.sh – This guy simplifies setting up the PYQTDESIGNERPATH and PYTHONPATH environment variables, which let Qt Designer know where to look for the plugin code, before launching it.
- main_window.ui – This is an example dialog created in Qt Designer which contains our custom widget.
- bakeui.sh – Running this shell script will use pyuic4 (provided by the ‘pyqt4-dev-tools’ package on Ubuntu 13.04) to convert our Qt Designer .ui file into python code.
- main_application.py – A demo script that displays the example dialog and calls the test method to verify that subclassing the QLabel worked.
I had a bit of difficulty getting the Qt Designer to recognize my plugins until I found that I was missing a package. On Ubuntu 13.04 the package I needed was: python-qt4-dev