Intro to Desktop Applications with Qt

(and Python, of course)

All examples here will work with either binding

About Me

Amy Boyle

I create Graphical User Interfaces (GUIs) for scientific research

Almost finished developing a new data acquisition system for the electrophysiology rig. I have used PyQt for the GUI

Why Qt?

Cross platform

Large user base

Good graphical designer

Great API and docs

Event Driven Programming

Programming paradigm where the flow of execution is directed by events, such as user clicks

Linear Paradigm

result = func0(inputs)
do_stuff_with_results

result1 = func1(other_inputs)
do_stuff_with_results

def stuff(sdfsd):
    pass

Probably the most common way people are introduced to programming

Event-Driven Paradigm

while not exit_called:

    while not event_queue.is_empty():
        dispatch_next_event()

    wait_for_more_events()
  • Similarity to web servers
  • Non-busy wait

Where Do Events Come From?

In Qt, events are objects

Naming notation : Q-

Event Handlers

Receive events, do something useful

def mousePressEvent(event):
   if event.button == 'left':
      do_that_cool_thing()

Event handlers are the pieces of code which decide what action to take in response to an event

Widgets

A QWidget all by itself is an emtpy window

Hello World

from PyQt4 import QtGui

app = QtGui.QApplication([])

hello_widget = QtGui.QPushButton("Hello world!")
hello_widget.show()

app.exec_()

Any widget without a parent is, by default, a window, so this button shows up as it own window.

Putting it Together

from PyQt4 import QtGui

class MyButton(QtGui.QPushButton):
    nclicks = 0
    def mousePressEvent(self, event):
        super(MyButton, self).mousePressEvent(event)
        self.nclicks += 1
        self.setText("pressed {}".format(self.nclicks))

if __name__ == '__main__':

    app = QtGui.QApplication([])

    hello_widget = MyButton("click me")
    hello_widget.show()

    app.exec_()

The first thing we do is call super, since there may be other handlers that process this event. In this case the super class event handlers cause the button to have the depressed appearance.

Widgets are containers

class MyWidget(QtGui.QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()

        layout = QtGui.QVBoxLayout()

        self.field = QtGui.QLineEdit()
        self.prompt = QtGui.QLabel("Amy says:")
        self.label = QtGui.QLabel("")

        layout.addWidget(self.field)
        layout.addWidget(self.prompt)
        layout.addWidget(self.label)

        self.setLayout(layout)

Any widget that contains another widget is called its "parent", and the widgets inside it are "children"

Using events to edit other widgets

class MyWidget(QtGui.QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()

        layout = QtGui.QVBoxLayout()

        self.field = QtGui.QLineEdit()
        self.prompt = QtGui.QLabel("Amy says:")
        self.label = QtGui.QLabel("")

        layout.addWidget(self.field)
        layout.addWidget(self.prompt)
        layout.addWidget(self.label)

        self.setLayout(layout)

        self.field.keyPressEvent = self.keyPressEvent

    def keyPressEvent(self, event):
        QtGui.QLineEdit.keyPressEvent(self.field, event)
        if event.key() == QtCore.Qt.Key_Backspace:
            self.label.setText(self.label.text()[:-1])
        else:
            self.label.setText(self.label.text() + event.text())

So How do we use events to make changes to other widgets? We could reassign the event handler to the parent widget, while calling super on the static method of the original class... but don't do this, it's a mess and there is a better way.

Signals and Slots

A signal is emitted when a particular event occurs. signals can have parameters that they emit with the signal. Slots can be connected to widgets, so that when a signal is emitted, that slot gets executed. Any method with the correct arguments can serve as a slot.

Much better...

from PyQt4 import QtGui, QtCore

class MyWidget(QtGui.QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()

        layout = QtGui.QVBoxLayout()

        self.field = QtGui.QLineEdit()
        self.prompt = QtGui.QLabel("Amy says:")
        self.label = QtGui.QLabel("")

        layout.addWidget(self.field)
        layout.addWidget(self.prompt)
        layout.addWidget(self.label)

        self.setLayout(layout)

        self.field.textChanged.connect(self.label.setText)

Slots are any function with the appropriate parameters

class MyWidget(QtGui.QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()

        self.field = QtGui.QLineEdit()
        self.prompt = QtGui.QLabel("Amy says:")
        self.label = QtGui.QLabel("")

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.field)
        layout.addWidget(self.prompt)
        layout.addWidget(self.label)
        self.setLayout(layout)

        self.field.textChanged.connect(self.amySays)

    def amySays(self, text):
        self.label.setText(text + ' !!!')

PyQt

PySide

Large user base

Newer, smaller user base

Signals called "pyqtSignal"

Signals called "Signal"

Support for Qt5

No support for Qt5 (yet)

GPL

LGPL

* See my posts on installing in a virtualenv in Linux or Windows

Qt has classes for Web, Databases, and more!

QtWebKit, QtSql, QtNetwork, QtOpenGL...

Bare Bones Browser

import sys
from PySide import QtGui, QtCore, QtWebKit

class Browser(QtGui.QWidget):
    def __init__(self):
        super(Browser, self).__init__()

        self.page = QtWebKit.QWebView()
        self.addressBar = QtGui.QLineEdit("http://www.amyboyle.ninja")

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.addressBar)
        layout.addWidget(self.page)
        self.setLayout(layout)

        self.addressBar.returnPressed.connect(self.loadAddress)

    def loadAddress(self):
        address = self.addressBar.text()
        self.page.load(QtCore.QUrl(address))

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    browser = Browser()
    browser.show()
    sys.exit(app.exec_())

Go Make Cool Things!

Presentation at amyboyle.ninja

Slides + examples on github

SpaceForward
Left, Down, Page DownNext slide
Right, Up, Page UpPrevious slide
POpen presenter console
HToggle this help