Skip to main content

Ralsina.Me — Roberto Alsina's website

Custom widgets using PyQt

Revision: 1.0
Date: March 27, 2004


Introduction

Ev­ery­one who has pro­grammed ap­pli­ca­tions knows that some­times you cre­ate a gad­get that can be reused in oth­er sit­u­a­tion­s, and that code re­use is good.

In the spe­cif­ic case of GUI ap­pli­ca­tion­s, of­ten what you would want to re­use is a wid­get.

For ex­am­ple, you took one of the toolk­it's wid­gets and ex­tend­ed its func­tion­al­i­ty in a way you think has wide ap­pli­ca­tion, and you in­tend to re­use it on fu­ture work.

So, what we will try to do is fig­ure out how we can cre­ate easy-­to-reuse cus­tom wid­gets us­ing PyQt.

Our ex­am­ple cus­tom wid­get will be a Re­struc­tured­Text ed­i­tor. Why? Be­cause I need one ;-)

What you need to follow the tutorial

  • PyQt ver­sion 3.10 or lat­er (old­er ones prob­a­bly OK, too, just untest­ed by me)
  • do­cu­tils

Step I: Drawing the thing

We can use Qt's de­siger to draw the lay­out of our wid­get, if it needs more than one com­po­nen­t.

In this case, I will cre­ate a tabbed wid­get con­tain­ing a edit­ing (Q­TextE­d­it) and a view­ing (Q­TextBrowser) wid­get­s.

  • Open de­sign­er.
  • Tell it you want to cre­ate a wid­get.
  • It will cre­ate an emp­ty for­m, called For­m1, noth­ing very ex­cit­ing ;-)
  • Now lay­out the stuff just the way I want it. The tabbed wid­get is called tab­s, the QTextE­d­it is called ed­i­tor, and the QTextBrows­er is called view­er.
  • Right-click on the for­m, and choose Form Set­tings
  • Class Name: I choose QRestE­d­i­tor­Base This is the name of the gen­er­at­ed class.
  • Com­men­t: De­scribe your class
  • Au­thor: You
  • You can see this in the qreste­d­i­tor­base.ui file
/static/thumb-customwidget1.png

Step II: Slots

A Qt Wid­get has a num­ber of slot­s, which is how the code ac­cess­es its func­tion­al­i­ty.

While the re­al QRestE­d­i­tor is a rather com­plex wid­get, I will ex­plain just a small part of it, so you can see how the gen­er­al case is. Re­peat as need­ed ;-)

You can cre­ate the slots from de­sign­er, by right-click­ing on the form and choos­ing Slots.

Well, cre­ate all the slots you need. I on­ly cre­at­ed one, called ren­der(), which will use the do­cu­tils mod­ule to ren­der the text in the ed­i­tor in­to HTML on the view­er.

Step III: Implementation

While the look of the wid­get is al­ready done, it needs to be fleshed by adding code to it.

My pre­ferred way to do it is cre­at­ing a sub­class of QRestE­d­i­tor­Base, called QRestE­d­i­tor, and im­ple­ment there the de­sired func­tion­al­i­ty.

  • First com­pile qreste­d­i­tor­base.ui us­ing pyuic:

pyuic -p0 -x qreste­d­i­­tor­base.ui > qreste­d­i­­tor­base.py

This will gen­er­ate a qreste­d­i­tor­base.py file which im­ple­ments the QRestE­d­i­tor­Base class.

You can see if it works by do­ing

python qreste­d­i­­tor­base.py

Now here's how the QRestE­d­i­tor class looks like:

im­port   sys
from   qt   im­port   *
from   qreste­d­i­tor­base   im­port   QRestE­d­i­tor­Base
from   do­cu­tils   im­port   core

class   QRestE­d­i­tor   (QRestE­d­i­tor­Base):

        
def   __init__(self,par­ent=   None,name   =   None,fl   =   0):
                
QRestE­d­i­tor­Base.__init__(self,par­ent,name,fl)

        
def   ren­der(self):
               
self.view­er.set­Text(core.pub­lish_string(str(self.ed­i­tor.text()),
                 
writer_­name='htm­l',
                 
set­tings_over­rides={'in­put_en­cod­ing':   'ut­f8'}))

if   __­name__   ==   "__­main__":
        
a   =   QAp­pli­ca­tion(sys.argv)
        
QOb­ject.con­nect(a,SIG­NAL("last­Win­dow­Closed()"),a,SLOT("quit()"))
        
w   =   QRestE­d­i­tor()
        
a.set­Main­Wid­get(w)
        
w.show()
        
a.ex­ec_loop()

As you can see, it sim­ply reim­ple­ments the ren­der() slot.

Now you can just use the wid­get at will. Of course what we did so far is noth­ing un­usu­al :-)

Step IV: Designer's Custom Widgets

De­sign­er is a nice tool. That's why we used it in Step I, af­ter al­l. Now, we'll see how we can make it so our new wid­get can be used in de­sign­er, too!

  • Open de­sign­er.
  • Go to Tool­s->­­Cus­­tom->Ed­it Cus­­tom Wid­gets
  • Click on New Wid­get
  • In the De­f­i­ni­­tion tab:
    • Class: QRestE­d­i­­tor
    • Head­­er­­file: blank
  • In the Slots tab:
    • Click New Slot
    • Slot: ren­der()
  • You can re­place the Qt lo­go with some pic­ture so that lat­er, when we use the wid­get, it looks some­what WYSI­WYG.
  • Click Save De­scrip­­tions, and save it in an ad­e­quate file.
/static/thumb-customwidget3.png

Step V: Using the custom widget in an application

Now, sup­pose we want to cre­ate a di­a­log con­tain­ing the cus­tom wid­get we cre­at­ed, for de­mostra­tion pur­pos­es.

  • Open de­sign­er

  • Tell it you want to cre­ate a di­a­log

  • Tool­s->­­­Cus­­­tom->QRestE­d­i­­­tor

  • Click any­where so it gets cre­at­ed.

  • Now, you need to tell de­sign­er some­thing, so that the gen­er­at­ed code will im­­port the right Python mod­­ule to cre­ate a QRestE­d­i­­tor wid­get.

  • Right click on the for­m, and choose Form Set­t­ings

  • In the Com­­ments field, add this:

    Python:#Import the qresteditor module
    Python:from qresteditor import *
    

    No­tice that there is no space af­ter Python: be­cause this will be in­sert­ed in the python code!

  • Add a QPush­But­­ton some­where in the di­a­log.

Now, some­thing that does­n't quite work yet:

  • Con­nec­t, us­ing De­sign­er, the clicked() sig­nal of the but­ton to the ren­der() slot in the QRestE­d­i­tor, so that when you click it, it will trig­ger the ren­der­ing.
  • Save the di­a­log to a file. I used test­di­a­log.ui
  • To test it, com­pile it us­ing pyuic and run it:
pyuic -x -p0  testdialog.ui > testdialog.py

python testdialog.py

Sad­ly, on PyQt 3.10, the con­nec­tions are not gen­er­at­ed cor­rect­ly, so this will not work right. :-P

On test­di­a­log.py, you will need to change the line

self­­.­­con­nec­t(­­self.­­push­But­­ton1,SIG­­NAL("clicked()"),­­self.restE­d­i­­tor1,S­LOT("ren­der()"))

to

self­­.­­con­nec­t(­­self.­­push­But­­ton1,SIG­­NAL("clicked()"),­­self.restE­d­i­­tor1.ren­der)

This means this is not yet per­fec­t. But hope­ful­ly it ill get fixed soon, if pos­si­ble :-)

How­ev­er, if you han­dle all the con­nec­tions to the cus­tom wid­get by hand in a sub­class of test­di­alog, it is en­tire­ly pos­si­ble to use this al­ready. That means you can't use de­sign­er 100%, but it's close.

/static/thumb-customwidget2.png

Possible improvements

  1. If some­one could man­age to write a QWid­get­Plu­g­in that cre­at­ed ar­bi­trary PyQt-based wid­get­s, which could be used from C++, that would have two very nice ef­fect­s:
    • It would be pos­si­ble to use such wid­gets WYSWIG from de­sign­er
    • It would be pos­si­ble to use them from any C++ Qt ap­­pli­­ca­­tion­s! (at least to some ex­ten­t)
  2. Al­ter­na­tive: maybe it is pos­si­ble to load the class we cre­ate, and then au­tomag­i­cal­ly gen­er­ate a .cw file for it. The for­mat of the .cw file is sim­ple enough, and in PyQt ev­ery method is a slot, so it would be as sim­ple as pick­ing up a list of meth­od­s... that would make things quite a bit eas­ier!

Packaging

There's re­al­ly no need to pack­age these things. You could just put them in a sub­fold­er of your own pro­jec­t, and use them.

How­ev­er, if you want to have a sys­tem-wide ver­sion of the cus­tom wid­get, a sim­ple dis­tu­til­s-based set­up.py can do the work, and is left as ex­er­cise for the read­er (post it as a com­men­t, you get your grade lat­er ;-)

Final Words

Of course to make a good Re­struc­tured Text ed­i­tor wid­get, it needs lots more flesh­ing out than what you see here. In fac­t, I wrote this ar­ti­cle in a more ad­vanced ver­sion of the same thing ;-) The pur­pose was sim­ply to show how you can cre­ate wid­gets max­i­miz­ing the chance of re­use.

If any mag­a­zine or site ed­i­tor reads this, feels this ar­ti­cle has ad­e­quate qual­i­ty, and wants to pub­lish some­thing I write... feel free to con­tact me and we'll talk about it. I'm not ex­pen­sive, I have sev­er­al ideas, and I write quick­ly ;-)

Roberto Alsina / 2006-04-04 16:29:

Comments for this story are here:

http://www.haloscan.com/com...

employment background check / 2011-12-27 23:23:


Hi very nice article