--- category: '' date: 2004/03/28 15:52 description: '' link: '' priority: '' slug: '27' tags: kde, programming, python title: Custom widgets using PyQt type: text updated: 2004/03/28 15:52 url_type: '' --- .. raw:: html
Revision: 1.0
Date: March 27, 2004

Contents


Introduction

Everyone who has programmed applications knows that sometimes you create a gadget that can be reused in other situations, and that code reuse is good.

In the specific case of GUI applications, often what you would want to reuse is a widget.

For example, you took one of the toolkit's widgets and extended its functionality in a way you think has wide application, and you intend to reuse it on future work.

So, what we will try to do is figure out how we can create easy-to-reuse custom widgets using PyQt.

Our example custom widget will be a RestructuredText editor. Why? Because I need one ;-)

What you need to follow the tutorial

Step I: Drawing the thing

We can use Qt's desiger to draw the layout of our widget, if it needs more than one component.

In this case, I will create a tabbed widget containing a editing (QTextEdit) and a viewing (QTextBrowser) widgets.

/static/thumb-customwidget1.png

Step II: Slots

A Qt Widget has a number of slots, which is how the code accesses its functionality.

While the real QRestEditor is a rather complex widget, I will explain just a small part of it, so you can see how the general case is. Repeat as needed ;-)

You can create the slots from designer, by right-clicking on the form and choosing Slots.

Well, create all the slots you need. I only created one, called render(), which will use the docutils module to render the text in the editor into HTML on the viewer.

Step III: Implementation

While the look of the widget is already done, it needs to be fleshed by adding code to it.

My preferred way to do it is creating a subclass of QRestEditorBase, called QRestEditor, and implement there the desired functionality.

pyuic -p0 -x qresteditorbase.ui > qresteditorbase.py

This will generate a qresteditorbase.py file which implements the QRestEditorBase class.

You can see if it works by doing

python qresteditorbase.py

Now here's how the QRestEditor class looks like:

import sys
from qt import *
from qresteditorbase import QRestEditorBase
from docutils import core

class QRestEditor (QRestEditorBase):

        
def __init__(self,parent= None,name = None,fl = 0):
                
QRestEditorBase.__init__(self,parent,name,fl)

        
def render(self):
               
self.viewer.setText(core.publish_string(str(self.editor.text()),
                 
writer_name='html',
                 
settings_overrides={'input_encoding': 'utf8'}))

if __name__ == "__main__":
        
a = QApplication(sys.argv)
        
QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()"))
        
w = QRestEditor()
        
a.setMainWidget(w)
        
w.show()
        
a.exec_loop()

As you can see, it simply reimplements the render() slot.

Now you can just use the widget at will. Of course what we did so far is nothing unusual :-)

Step IV: Designer's Custom Widgets

Designer is a nice tool. That's why we used it in Step I, after all. Now, we'll see how we can make it so our new widget can be used in designer, too!

/static/thumb-customwidget3.png

Step V: Using the custom widget in an application

Now, suppose we want to create a dialog containing the custom widget we created, for demostration purposes.

Now, something that doesn't quite work yet:

    pyuic -x -p0  testdialog.ui > testdialog.py
    
    python testdialog.py
    

Sadly, on PyQt 3.10, the connections are not generated correctly, so this will not work right. :-P

On testdialog.py, you will need to change the line

self.connect(self.pushButton1,SIGNAL("clicked()"),self.restEditor1,SLOT("render()"))

to

self.connect(self.pushButton1,SIGNAL("clicked()"),self.restEditor1.render)

This means this is not yet perfect. But hopefully it ill get fixed soon, if possible :-)

However, if you handle all the connections to the custom widget by hand in a subclass of testdialog, it is entirely possible to use this already. That means you can't use designer 100%, but it's close.

/static/thumb-customwidget2.png

Possible improvements

  1. If someone could manage to write a QWidgetPlugin that created arbitrary PyQt-based widgets, which could be used from C++, that would have two very nice effects:
    • It would be possible to use such widgets WYSWIG from designer
    • It would be possible to use them from any C++ Qt applications! (at least to some extent)
  2. Alternative: maybe it is possible to load the class we create, and then automagically generate a .cw file for it. The format of the .cw file is simple enough, and in PyQt every method is a slot, so it would be as simple as picking up a list of methods... that would make things quite a bit easier!

Packaging

There's really no need to package these things. You could just put them in a subfolder of your own project, and use them.

However, if you want to have a system-wide version of the custom widget, a simple distutils-based setup.py can do the work, and is left as exercise for the reader (post it as a comment, you get your grade later ;-)

Final Words

Of course to make a good Restructured Text editor widget, it needs lots more fleshing out than what you see here. In fact, I wrote this article in a more advanced version of the same thing ;-) The purpose was simply to show how you can create widgets maximizing the chance of reuse.

If any magazine or site editor reads this, feels this article has adequate quality, and wants to publish something I write... feel free to contact me and we'll talk about it. I'm not expensive, I have several ideas, and I write quickly ;-)

Important Links