:: choice ::
go green!  go red!  go blue!
random good link:
Linux
random evil link:
National Rifle Association
random quote:
"Natural selection won't matter soon, not anywhere as much as concious selection. We will civilize and alter ourselves to suit our ideas of what we can be. Within one more human lifespan, we will have changed ourselves unrecognizably."
Greg Bear
visit kde.org! visit debian.org!

programming Qt applications in python

Step 1: simple hello world
Step 2: a button
Step 3: a more structured approach
Step 4: using Qt designer

This tutorial aims to provide a hands-on guide to learn the basics of building a small Qt application in python.

To follow this tutorial, you should have basic python knowledge, knowledge of Qt, however, is not necessary. I'm using Linux in these examples and am assuming you already have a working installation of python and pyqt. To test that, open a python shell by simply typing python in a console to start the interactive interpreter and type
>>> import qt
If this doesn't yield an error message, you should be ready to roll. The examples in this tutorial are kept as easy as possible, showing useful ways to write and structure your program. It is important that you read the source code of the example files, most of the stuff that is done is explained in the code. Use the examples and try to change things, play around with them. This is the best way to get comfortable with it.

Hello, world!

Let's start easy. Popping up a window and displaying something. The following small program will popup a window showing "Hello world!", obviously.
#!/usr/bin/env python

import sys
from qt import *

# We instantiate a QApplication passing the arguments of the script to it:
a = QApplication(sys.argv)

# Add a basic widget to this application:
# The first argument is the text we want this QWidget to show, the second
# one is the parent widget. Since Our "hello" is the only thing we use (the 
# so-called "MainWidget", it does not have a parent.
hello = QLabel("Hello world!",None)

# We have to let the application know which widget is its MainWidget ...
a.setMainWidget(hello)

# ... and that it should be shown.
hello.show()

# Now we can start it.
a.exec_loop()
download source of hello.py
About 7 lines of code, and that's about as easy as it can get.

A button

Let's add some interaction! We'll replace the label saying "Hello, World!" with a button and assign an action to it. This assignment is done by connecting a signal, an event which is sent out when the button is pushed to a slot, which is an action, normally a function that is run in the case of that event.
#!/usr/bin/env python

import sys
from qt import *

a = QApplication(sys.argv)

# Our function to call when the button is clicked
def sayHello():
	print "Hello, World!"

# Instantiate the button
hellobutton = QPushButton("Say 'Hello world!'",None)

# And connect the action "sayHello" to the event "button has been clicked"
a.connect(hellobutton, SIGNAL("clicked()"), sayHello)

# The rest is known already...
a.setMainWidget(hellobutton)
hellobutton.show()
a.exec_loop()
download source of hellobutton.py

Urgh, that looks like a crappy approach

You can imagine that coding this way is not scalable nor the way you'll want to continue working. So let's make that stuff pythonic, adding structure and actually using object-orientation in it. We create our own application class, derived from a QApplication and put the customization of the application into its methods: One method to build up the widgets and a slot which contains the code that's executed when a signal is received.
#!/usr/bin/env python

import sys
from qt import *

class HelloApplication(QApplication):
    
    def __init__(self, args):
        """ In the constructor we're doing everything to get our application
            started, which is basically constructing a basic QApplication by 
            its __init__ method, then adding our widgets and finally starting 
            the exec_loop."""
        QApplication.__init__(self, args)
        self.addWidgets()
        self.exec_loop()        
        
    def addWidgets(self):
        """ In this method, we're adding widgets and connecting signals from 
            these widgets to methods of our class, the so-called "slots" 
        """
        self.hellobutton = QPushButton("Say 'Hello world!'",None)
        self.connect(self.hellobutton, SIGNAL("clicked()"), self.slotSayHello)
        self.setMainWidget(self.hellobutton)
        self.hellobutton.show()
        
    def slotSayHello(self):
        """ This is an example slot, a method that gets called when a signal is 
            emitted """
        print "Hello, World!"

# Only actually do something if this script is run standalone, so we can test our 
# application, but we're also able to import this program without actually running
# any code.
if __name__ == "__main__":
    app = HelloApplication(sys.argv)

download source of helloclass.py (Note that the above code shows exactly the same as the second example, but it's scalable.

gui coding sucks

... so we want to use Qt3 Designer for creating our GUI. In the picture, you can see a simple GUI, with in green letters the names of the widgets. What we are going to do is
  • We compile the .ui file from Qt designer into a python class
  • We subclass that class and use it as our mainWidget
  • This way, we're able to change the user interface afterwards from Qt designer, without having it messing around in the code we added.
    pyuic testapp_ui.ui -o testapp_ui.py
    makes a python file from it which we can work with.

    The way our program works can be described like this:
  • We fill in the lineedit
  • Clicking the add button will be connected to a method that reads the text from the lineedit, makes a listviewitem out of it and adds that to our listview.
  • Clicking the deletebutton will delete the currently selected item from the listview.
  • Here's the heavily commented code:
    #!/usr/bin/env python
    
    from testapp_ui import TestAppUI
    from qt import *
    import sys
    
    class HelloApplication(QApplication):
        
        def __init__(self, args):
            """ In the constructor we're doing everything to get our application
                started, which is basically constructing a basic QApplication by 
                its __init__ method, then adding our widgets and finally starting 
                the exec_loop."""
            QApplication.__init__(self,args)
            
            # We pass None since it's the top-level widget, we could in fact leave 
            # that one out, but this way it's easier to add more dialogs or widgets.
            self.maindialog = TestApp(None)
            
            self.setMainWidget(self.maindialog)
            self.maindialog.show()
            self.exec_loop()     
    
    class TestApp(TestAppUI):
    
        def __init__(self,parent):
            # Run the parent constructor and connect the slots to methods.
            TestAppUI.__init__(self,parent)
            self._connectSlots()
            
            # The listview is initially empty, so the deletebutton will have no effect,
            # we grey it out.
            self.deletebutton.setEnabled(False)
            
        def _connectSlots(self):
            # Connect our two methods to SIGNALS the GUI emits.
            self.connect(self.addbutton,SIGNAL("clicked()"),self._slotAddClicked)
            self.connect(self.deletebutton,SIGNAL("clicked()"),self._slotDeleteClicked)
            
        def _slotAddClicked(self):
            # Read the text from the lineedit,
            text = self.lineedit.text()
            # if the lineedit is not empty,
            if len(text):
                # insert a new listviewitem ...
                lvi = QListViewItem(self.listview)
                # with the text from the lineedit and ...
                lvi.setText(0,text)
                # clear the lineedit.
                self.lineedit.clear()
                
                # The deletebutton might be disabled, since we're sure that there's now
                # at least one item in it, we enable it.
                self.deletebutton.setEnabled(True)
        
        def _slotDeleteClicked(self):
            # Remove the currently selected item from the listview.
            self.listview.takeItem(self.listview.currentItem())
            
            # Check if the list is empty - if yes, disable the deletebutton.
            if self.listview.childCount() == 0:
                self.deletebutton.setEnabled(False)
    
    if __name__ == "__main__":
        app = HelloApplication(sys.argv)
    
    download source of testapp.py download ui file testapp_ui.ui download compiled ui testapp_ui.py

    useful to know

    Creating the GUI in Qt designer does not only make it easier creating the GUI, but it's a great learning tool, too. You can test how a widget looks like, see what's available in Qt and have a look at properties you might want to use.

    The C++ API documentation is also a very useful (read: necessary) tool when working with PyQt. The API is translated pretty straightforward, so after having trained a little, you'll find the developers API docs one of the tools you really need. When working from KDE, konqueror's default shortcut is qt:[widgetname], so [alt]+[F2], "qt:qbutton directly takes you to the right API documentation page. Trolltech's doc section has much more documentation which you might want to have a look at.

    The examples in this tutorial have been created using Qt 3.3. I might update the tutorial when there's a usable version of the Qt bindings for Qt 4 available, but at this moment, using PyQt4 does not make sense.


    This document is published under the GNU Free Documentation License.

    07-12-2005, 19:22 h
    © Sebastian Kügler
    [Parsetime: 0.0021sec]