Events and Signals

In this article by Venkateshwaran Loganathan and Gopinath Jaganmohan, authors of the book Pyside GUI Application Development- Second Edition, we will look into some of the internal implementation working concepts of those functions. Being an event-driven toolkit, events and event delivery play an important role in the Qt architecture. We will start this article by discussing events and signals, their implementation, and will go on to discuss handling drag-and-drop events, and drawing functionalities.

(For more resources related to this topic, see here.)

Event management

An event in Qt is an object inherited from the abstract QEvent class which is a notification of something significant that has happened. Events become more useful in creating custom widgets on our own. An event can happen either within an application or as a result of an outside activity that the application needs to know about. When an event occurs, Qt creates an event object and notifies to the instance of an QObject class or one of its subclasses through their event() function. Events can be generated from both inside and outside the application. For instance, the QKeyEvent and QMouseEvent object represent some kind of keyboard and mouse interaction and they come from the window manager; the QTimerEvent objects are sent to QObject when one of its timers fires, and they usually come from the operating system; the QChildEvent objects are sent to QObject when a child is added or removed and they come from inside of your Qt application.

The users of PySide usually get confused with events and signals. Events and signals are two parallel mechanisms used to accomplish the same thing. As a general difference, signals are useful when using a widget, whereas events are useful when implementing the widget. For example, when we are using a widget like QPushButton, we are more interested in its clicked() signal than in the low-level mouse press or key press events that caused the signal to be emitted. But if we are implementing the QPushButton class, we are more interested in the implementation of code for mouse and key events. Also, we usually handle events but get notified by signal emissions.

Event loop

All the events in Qt will go through an event loop. One main key concept to be noted here is that the events are not delivered as soon as they are generated; instead they're queued up in an event queue and processed later one-by-one. The event dispatcher will loop through this queue and dispatch these events to the target QObject and hence it is called an event loop . Qt's main event loop dispatcher, QCoreApplication.exec() will fetch the native window system events from the event queue and will process them, convert them into the QEvent objects, and send it to their respective target QObject.

A simple event loop can be explained as described in the following pseudocode:


The Qt's main event loop starts with the QCoreApplication::exec() call and this gets blocked until QCoreApplication::exit() or QCoreApplication::quit() is called to terminate the loop. The wait_for_more_events() function blocks until some event is generated. This blocking is not a busy wait blocking and will not burn the CPU resources. Generally the event loop can be awaken by a window manager activity, socket activity, timers, or event posted by other threads. All these activities require a running event loop. It is more important not to block the event loop because when it is struck, widgets will not update themselves, timers won't fire, networking communications will slow down and stop. In short, your application will not respond to any external or internal events and hence it is advised to quickly react to events and return to the event loop as soon as possible.

Event processing

Qt offers five methods to do event processing. They are:

  • By re-implementing a specific event handler like keyPressEvent(), paintEvent()
  • By re-implementing the QObject::event() class
  • Installing an event filter on a single QObject
  • Installing an event filter on the QApplication object
  • Subclassing QApplication and re-implementing notify()

Generally, this can be broadly divided into re-implementing event handlers and installing event filters. We will see each of them in little detail.

Reimplementing event handlers

We can implement the task at hand or control a widget by reimplementing the virtual event handling functions. The following example will explain how to reimplement a few most commonly used events, a key press event, a mouse double-click event, and a window resize event. We will have a look at the code first and defer the explanation after the code:

# Import necessary modules
import sys
from PySide.QtGui import *
from PySide.QtCore import *
# Our main widget class
class MyWidget(QWidget):
  # Constructor function
  def __init__(self):
    self.setWindowTitle("Reimplementing Events")
    self.setGeometry(300, 250, 300, 100)
    self.myLayout = QVBoxLayout()
    self.myLabel = QLabel("Press 'Esc' to close this App")
    self.infoLabel = QLabel()
# Function reimplementing Key Press, Mouse Click and Resize Events
  def keyPressEvent(self, event):
    if event.key() == Qt.Key_Escape:
  def mouseDoubleClickEvent(self, event):
  def resizeEvent(self, event):
    self.infoLabel.setText("Window Resized to QSize(%d, %d)" % (event.
size().width(), event.size().height()))
if __name__ =='__main__':
  # Exception Handling
    myApp = QApplication(sys.argv)
    myWidget = MyWidget()
  except NameError:
    print("Name Error:", sys.exc_info()[1])
  except SystemExit:
    print("Closing Window...")
  except Exception:

In the preceding code, the keyPressEvent() function reimplements the event generated as a result of pressing a key. We have implemented in such a way that the application closes when the Esc key is pressed. On running this code, we would get a output similar to the one shown in the following screenshot:

Pyside GUI Application Development- Second Edition

The application will be closed if you press the Esc key. The same functionality is implemented on a mouse double-click event. The third event is a resize event. This event gets triggered when you try to resize the widget. The second line of text in the window will show the size of the window in (width, height) format. You could witness the same on resizing the window.

Similar to keyPressEvent(), we could also implement keyReleaseEvent() that would be triggered on release of the key. Normally, we are not very interested in the key release events except for the keys where it is important. The specific keys where the release event holds importance are the modifier keys such as Ctrl, Shift , and Alt. These keys are called modifier keys and can be accessed using QKeyEvent::modifiers. For example, the key press of a Ctrl key can be checked using Qt.ControlModifier. The other modifiers are Qt.ShiftModifier and Qt.AltModifier. For instance, if we want to check the press event of combination of Ctrl + PageDown key, we could have the check as:

if event.key() == Qt.Key_PageDown and
  event.modifiers() == Qt.ControlModifier:
    print("Ctrl+PgDn Key is pressed")

Before any particular key press or mouse click event handler function, say, for example, keyPressEvent() is called, the widget's event() function is called first. The event() method may handle the event itself or may delegate the work to a specific event handler like resizeEvent() or keyPressEvent(). The implementation of the event() function is very helpful in some special cases like the Tab key press event. In most cases, the widget with the keyboard focuses the event() method will call setFocus() on the next widget in the tab order and will not pass the event to any of the specific handlers. So we might have to re-implement any specific functionality for the Tab key press event in the event() function. This behavior of propagating the key press events is the outcome of Qt's Parent-Child hierarchy. The event gets propagated to its parent or its grand-parent and so on if it is not handled at any particular level. If the top-level widget also doesn't handle the event it is safely ignored. The following code shows an example for reimplementing the event() function:

class MyWidget(QWidget):
  # Constructor function
  def __init__(self):
    self.setWindowTitle("Reimplementing Events")
    self.setGeometry(300, 250, 300, 100)
    self.myLayout = QVBoxLayout()
    self.myLabel1 = QLabel("Text 1")
    self.myLineEdit1 = QLineEdit()
    self.myLabel2 = QLabel("Text 2")
    self.myLineEdit2 = QLineEdit()
    self.myLabel3 = QLabel("Text 3")
    self.myLineEdit3 = QLineEdit()
  # Function reimplementing event() function
  def event(self, event):
    if event.type()== QEvent.KeyRelease and event.key()== Qt.Key_Tab:
      return True
    return QWidget.event(self,event)

In the preceding example, we try to mask the default behavior of the Tab key. If you haven't implemented the event() function, pressing the Tab key would have set focus to the next available input widget. You will not be able to detect the Tab key press in the keyPress() function as described in the previous examples, since the key press is never passed to them. Instead, we have to implement it in the event() function. If you execute the preceding code, you would see that every time you press the Tab key the focus will be set into the third QLineEdit widget of the application. Inside the event() function, it is more important to return the value from the function. If we have processed the required operation, True is returned to indicate that the event is handled successfully, else, we pass the event handling to the parent class's event() function.

Installing event filters

One of the interesting and notable features of Qt's event model is to allow a QObject instance to monitor the events of another QObject instance before the latter object is even notified of it. This feature is very useful in constructing custom widgets comprising of various widgets altogether. Consider that you have a requirement to implement a feature in an internal application for a customer such that pressing the Enter key must have to shift the focus to next input widget. One way to approach the problem is to reimplement the keyPressEvent() function for all the widgets present in the custom widget. Instead, this can be achieved by reimplementing the eventFilter() function for the custom widget. If we implement this, the events will first be passed on to the custom widget's eventFilter() function before being passed on to the target widget. An example is implemented as follows:

def eventFilter(self, receiver, event):
    if(event.type() == QEvent.MouseButtonPress):
      QMessageBox.information(None,"Filtered Mouse Press Event!!", 
        'Mouse Press Detected')
      return True
    return super(MyWidget,self).eventFilter(receiver, event)

Remember to return the result of event handling, or pass it on to the parent's eventFilter() function. To invoke eventFilter(), it has to be registered as follows in the constructor function:


The event filters can also be implemented for the QApplication as a whole. This is left as an exercise for you to discover.

Reimplementing the notify() function

The final way of handling events is to reimplement the notify() function of the QApplication class. This is the only way to get all the events before any of the event filters discussed previously are notified. The event gets notified to this function first before it gets passed on to the event filters and specific event functions. The use of notify() and other event filters are generally discouraged unless it is absolutely necessary to implement them because handling them at top level might introduce unwanted results, and we might end up in handling the events that we don't want to. Instead, use the specific event functions to handle events. The following code excerpt shows an example of re-implementing the notify() function:

class MyApplication(QApplication):
  def __init__(self, args):
  super(MyApplication, self).__init__(args)
  def notify(self, receiver, event):
    if (event.type() == QEvent.KeyPress):
      QMessageBox.information(None, "Received Key Release EVent", "You 
Pressed: "+ event.text())
    return super(MyApplication, self).notify(receiver, event)

Signals and slots

The fundamental part of any GUI program is the communication between the objects. Signals and slots provide a mechanism to define this communication between the actions happened and the result proposed for the respective action. Prior to Qt's modern implementation of signal/slot mechanism, older toolkits achieve this kind of communication through callbacks. A callback is a pointer to a function, so if you want a processing function to notify about some event you pass a pointer to another function (the callback) to the processing function. The processing function then calls the callback whenever appropriate. This mechanism does not prove useful in the later advancements due to some flaws in the callback implementation.

A signal is an observable event, or at least notification that the event has happened. A slot is a potential observer, more usually a function that is called. In order to establish communication between them, we connect a signal to a slot to establish the desired action. However, we may have different combinations as defined in the bullet points:

  • One signal can be connected to many slots
  • Many signals can be connected to the same slot
  • A signal can be connected to other signals
  • Connections can be removed

PySide offers various predefined signals and slots such that we can connect a predefined signal to a predefined slot and do nothing else to achieve what we want. However, it is also possible to define our own signals and slots. Whenever a signal is emitted, Qt will simply throw it away. We can define the slot to catch and notice the signal that is being emitted. The first code excerpt that follows this text will be an example for connecting predefined signals to predefined slots and the latter will discuss the custom user defined signals and slots.

The first example is a simple EMI calculator application that takes the Loan Amount, Rate of Interest, and Number of Years as its input, and calculates the EMI per month and displays it to the user. To start with, we set in a layout the components required for the EMI calculator application. The Amount will be a text input from the user. The rate of years will be taken from a spin box input or a dial input. A spin box is a GUI component which has its minimum and maximum value set, and the value can be modified using the up and down arrow buttons present at its side. The dial represents a clock like widget whose values can be changed by dragging the arrow. The Number of Years value is taken by a spin box input or a slider input:

class MyWidget(QWidget):
  def __init__(self):
    self.amtLabel = QLabel('Loan Amount')
    self.roiLabel = QLabel('Rate of Interest')
    self.yrsLabel = QLabel('No. of Years')
    self.emiLabel = QLabel('EMI per month')
    self.emiValue = QLCDNumber()
    self.amtText = QLineEdit('10000')
    self.roiSpin = QSpinBox()
    self.yrsSpin = QSpinBox()
    self.roiDial = QDial()
    self.yrsSlide = QSlider(Qt.Horizontal)
    self.calculateButton = QPushButton('Calculate EMI')
    self.myGridLayout = QGridLayout()
    self.myGridLayout.addWidget(self.amtLabel, 0, 0)
    self.myGridLayout.addWidget(self.roiLabel, 1, 0)
    self.myGridLayout.addWidget(self.yrsLabel, 2, 0)
    self.myGridLayout.addWidget(self.amtText, 0, 1)
    self.myGridLayout.addWidget(self.roiSpin, 1, 1)
    self.myGridLayout.addWidget(self.yrsSpin, 2, 1)
    self.myGridLayout.addWidget(self.roiDial, 1, 2)
    self.myGridLayout.addWidget(self.yrsSlide, 2, 2)
    self.myGridLayout.addWidget(self.calculateButton, 3, 1)
    self.setWindowTitle("A simple EMI calculator")

Until now, we have set the components that are required for the application. Note that, the application layout uses a grid layout option. The next set of code is also defined in the contructor's __init__ function of the MyWidget class which will connect the different signals to slots. There are different ways by which you can use a connect function. The code explains the various options available:

    self.connect(self.roiSpin, SIGNAL("valueChanged(int)"), self.

In the first line of the previous code, we connect the valueChanged() signal of roiDial to call the slot of roiSpin, setValue(). So, if we change the value of roiDial, it emits a signal that connects to the roiSpin's setValue() function and will set the value accordingly. Here, we must note that changing either the spin or dial must change the other value because both represent a single entity. Hence, we induce a second line which calls roiDial's setValue() slot on changing the roiSpin's value. However, it is to be noted that the second form of connecting signals to slots is deprecated. It is given here just for reference and it is strongly discouraged to use this form. The following two lines of code execute the same for the number of years slider and spin:

    self.connect(self.yrsSpin, SIGNAL("valueChanged(int)"), self.
yrsSlide, SLOT("setValue(int)"))

In order to calculate the EMI value, we connect the clicked signal of the push button to a function (slot) which calculates the EMI and displays it to the user:

self.connect(self.calculateButton, SIGNAL("clicked()"), self.

The EMI calculation and display function is given for your reference:

def showEMI(self):
    loanAmount = float(self.amtText.text())
    rateInterest = float( float (self.roiSpin.value() / 12) / 100)
    noMonths = int(self.yrsSpin.value() * 12)
    emi = (loanAmount * rateInterest) * ( ( ( (1 + rateInterest) ** 
noMonths ) / ( ( (1 + rateInterest) ** noMonths ) - 1) ))
    self.myGridLayout.addWidget(self.emiLabel, 4, 0)
    self.myGridLayout.addWidget(self.emiValue, 4, 2)

The sample output of the application is shown in the following screenshot:

Pyside GUI Application Development- Second Edition

The EMI calculator application uses the predefined signals, say, for example, valueChanged(), clicked() and predefined slots, setValue(). However, the application also uses a user-defined slot showEMI() to calculate the EMI. As with slots, it is also possible to create a user-defined signal and emit it when required. The following program is an example for creating and emitting user-defined signals:

import sys
from PySide.QtCore import *
# define a new slot that receives and prints a string
def printText(text):
class CustomSignal(QObject):
  # create a new signal
  mySignal = Signal(str)
if __name__ == '__main__':
    myObject = CustomSignal()
    # connect signal and slot
    # emit signal
    myObject.mySignal.emit("Hello, Universe!")
  except Exception:

This is a very simple example of using custom signals. In the CustomSignal class, we create a signal named mySignal and we emit it in the main function. Also, we define that on emission of the signal mySignal , the printText() slot would be called. Many complex signal emissions can be built this way.


There are various ways in which you can transfer data between two objects or applications. Drag-and-drop is a modern visual technique of transformation of data between objects. It enables the user to copy and paste very intuitively. The drag-and-drop is a combination of two events, namely "Dragging and Dropping". The widgets can serve as drag sites, drop sites, or as both. One of the important factors that we should take care of is the MIME type of the object that we would drag-or-drop. It is to ensure that the information can be transferred safely between applications. The various MIME types supported by Qt include plain text, html text, uri-list text, image data, and color data. We will explore the Qt classes used for this action and shortly test with an example.

The various classes that are involved in drag-and-drop and their necessary MIME encoding and decoding are listed in the following table:

Pyside GUI Application Development- Second Edition

A drag can be initiated by setting the widget's setDragEnabled() with a Boolean True value. The dropping functionality can be implemented by re-implementing the dragMoveEvent() and dropEvent(). As the user drags over the widget, dragMoveEvent() occur and dropEvent() when the drag event is completed. We will now see an example for the drag-and-drop events and the working of the code will be explained inclass MyWidget(QWidget):

def __init__(self):
    self.myListWidget1 = QListWidget()
    self.myListWidget2 = QListWidget()
    self.setGeometry(300, 350, 500, 150)
    self.myLayout = QHBoxLayout()
    self.myLayout.addWidget(self.myListWidget2)    l1 = QListWidgetItem(QIcon('blue_bird.png'),"Angry Bird Blue")
    l2 = QListWidgetItem(QIcon('red_bird.png'),"Angry Bird Red")
    l3 = QListWidgetItem(QIcon('green_bird.png'),"Angry Bird Green")
    l4 = QListWidgetItem(QIcon('black_bird.png'),"Angry Bird Black")
    l5 = QListWidgetItem(QIcon('white_bird.png'),"Angry Bird White")
    self.myListWidget1.insertItem(1, l1)
    self.myListWidget1.insertItem(2, l2)
    self.myListWidget1.insertItem(3, l3)
    self.myListWidget1.insertItem(4, l4)
    self.myListWidget1.insertItem(5, l5)
    QListWidgetItem(QIcon('gray_pig.png'), "Grey Pig", self.
    QListWidgetItem(QIcon('brown_pig.png'), "Brown Pig", self.
    QListWidgetItem(QIcon('green_pig.png'), "Green Pig", self.
    self.setWindowTitle('Drag and Drop Example');

The preceding program on execution will look like the following screenshot: following the code:

Pyside GUI Application Development- Second Edition

Both partition of the application has a QListWidget object with some items added to it. The left side is the default view mode of QListWidget and the right side is set to icon view mode. Both these widgets support dragging mode as they are set with setDragEnabled(True). They also accept dropping functionality, as the setAcceptDrops(True) is set. You can test this by dragging-and-dropping between the widgets. We can control the behavior of the application by re-implementing the aforesaid event handler functions.


The PySide.QtGui.QPainter class performs low-level painting on widgets and other paint devices. The QPainter class provides all the functions for drawing simple lines to more complex shapes. This class also provides settings for rendering quality images and supports clipping. The drawing is usually done within the widget's paintEvent() function. The drawing functionalities are placed in between the begin() and end() functions of the QPainter object. The QPainter object is initialized with the constructor, customized with some set functions, for example, pen style and brush style, and then the draw function is called. The QPainter.isActive() function indicates if the painter is active. The QPainter object is activated when QPainter. begin() is invoked and deactivated on calling QPainter.end().The various drawings are performed using the QPainter's draw functions. The QPainter has three important settings that set the appearance of the drawing. They are:

  • Pen: The pen is used for drawing lines and shapes outlines. It takes various settings for drawing that include color, width, line style, and so on.
  • Brush: The brush is used for pattern filling of geometric shapes. The various settings that a brush can take include color, style, texture, gradient, and so on.
  • Font: The font is mainly used for drawing Unicode text. The font settings include font style, font family, and point size.

The settings for these parameters can be set and modified anytime by calling the setFont(), setBrush(), and setFont() on QPainter with their respective QPen, QBrush, or QFont objects.

In this section, we are going to explore the most commonly used drawing shapes. The following table will give you a gist of the available draw functions of the QPainter object:

Pyside GUI Application Development- Second Edition

Pyside GUI Application Development- Second Edition

All the earlier listed functions take various arguments as their parameters for different drawing functionalities. Also, all these drawing functions use the current pen, brush, and text settings to draw the objects. This section is not enough to cover and discuss all the different types of the drawing functions and hence we would see a sample program that is self-explanatory and exhibits the different styles of the listed functions. The complete version of the basic drawing functionality code can be downloaded from the book's site. Here, we just show the contents of the paintEvent() function with different drawing shapes. The complete code is bundled with event handling that we have discussed in the first section of this. As of now, it is sufficient for you if you could understand the reimplementation of event handlers and drawing functions of the program:

def paintEvent(self, event):
    rect = QRect(10, 20, 80, 60)
    startAngle = 30 * 16
    arcLength = 120 * 16
    painter = QPainter()
    if self.shape == PaintArea.Line:
      painter.drawLine(rect.bottomLeft(), rect.topRight())
    elif self.shape == PaintArea.Points:
    elif self.shape == PaintArea.Polyline:
    elif self.shape == PaintArea.Polygon:
    elif self.shape == PaintArea.Rect:
    elif self.shape == PaintArea.RoundRect:
    elif self.shape == PaintArea.Ellipse:
    elif self.shape == PaintArea.Arc:
      painter.drawArc(rect, startAngle, arcLength)
    elif self.shape == PaintArea.Chord:
      painter.drawChord(rect, startAngle, arcLength)
    elif self.shape == PaintArea.Pie:
      painter.drawPie(rect, startAngle, arcLength)
    elif self.shape == PaintArea.Path:
    elif self.shape == PaintArea.Text:
      painter.drawText(rect, QtCore.Qt.AlignCenter, "Basic Drawing 

This would produce a window as given in the following screenshot. You can select from the combo boxes the choice of your drawing and it would be painted in the application:

 Pyside GUI Application Development- Second Edition

Graphics and effects

We could create any custom graphics we like by creating a custom widget and by reimplementing its paint event. This approach is very helpful when we are trying to create some small graphics like drawing graphs or for drawing basic shapes. In order to create animations and more complex graphics we will take help from the PySide's graphics view classes:

  • QGraphicsScene: This provides a surface for managing a large number of 2D graphical items
  • QGraphicsItem: This serves as the base class for all graphical items like ellipse, line, and so on in a graphics scene
  • QGraphicsView: This provides a widget for displaying the contents of a graphics scene.

The graphic view classes can be used by first creating a scene represented by a QGraphicsScene object. Scenes can be associated with the QGraphicsView object to represent or view on the screen. Items that are represented by the QGraphicsItem object can be added to the scene. A scene is created, items are added, and visualized in that order. QGraphicsView can be triggered to visualize a whole scene or only a part of it by changing the bounding rectangle values. The interactions can happen by using the mouse or a keyboard. The graphics view translates the mouse and key events into scene events represented by QGraphicsSceneEvent and forwarding them to the visualized scene. The custom scene interactions are achieved by re-implementing the mouse and key event handlers. The most interesting feature of the graphics views is that we can apply transformations to them, for example, scaling and rotation. This can be done without changing the original scene's items.

class MyView(QGraphicsView):
  def __init__(self):
    self.myScene = QGraphicsScene(self)
    self.myItem = QGraphicsEllipseItem(-20, -10, 50, 20)
    self.timeLine = QTimeLine(1000)
    self.timeLine.setFrameRange(0, 100)
    self.animate = QGraphicsItemAnimation()
    self.animate.setPosAt(0, QPointF(0, -10))
    self.animate.setRotationAt(1, 360)
    self.setWindowTitle("A Simple Animation")

This program will create an animated ellipse as its output. As discussed, we have created scene, added items to it, and showed it via the view class. The animation is supported by the   QGraphicsAnimationItem() object. Many more complex animations can be built on top of this but explaining those is out of the scope of this book. You can explore by yourself to create more QGrapicsView objects and create complex animations.

The PySide.QtGui.QGraphicsEffect class serves as the base class for the graphical effects on an application. With the help of effects, we can alter the appearance of elements. The graphical effects objects operate between the sources, say, for example, a pix map item and the destination, the viewport, and render the respective effects for the image. The various graphical effects that Qt provides are:

  • QGrpahicsBlurEffect: It blurs the item by a given radius
  • QGraphicsDropShadowEffect: It renders a drop shadow behind the item
  • QGraphicsColorizeEffect: It renders the item in shades of any given color
  • QGraphicsOpacityEffect: It renders the item with an opacity

All these classes are inherited from the base class QGraphicsEffect. The following code excerpt shows an example of adding effects to the items:

self.effect = QGraphicsDropShadowEffect(self)

 When this effect is added, you will notice a shadow in the animated ellipse as shown in the following screenshot. Similarly, other effects can be added to the items.

Pyside GUI Application Development- Second Edition


This article would have been a real roller-coaster ride as we were taken into some internal depth of the PySide programming. We started with discussing event handlers and reimplementation techniques to achieve the task at hand. We also discussed about event filters and re-implementing the notify() function. Unless absolutely necessary the latter forms of re-implementing events should be avoided to make an efficient program.

We then explored the very fundamental mechanism to Qt, signals, and slots. The signals and slots mechanism follow an observer pattern listening and binding to objects when called. We started with implementing the built-in signals and slots. Later in this section, we implemented and emitted our own custom signals and also discussed how to listen to them.

In the latter half, we shifted our focus to diagrams and graphics. Starting with the drag-and-drop functionality usage, we have also seen various types of QPainter draw objects. The article ended with a brief discussion on graphics and effects. The examples that are shown in the latter half of the article are very basic examples to help you understand the basic concepts. Much more complex applications can be designed and it would by itself be a subject matter for a complete book.

Resources for Article:

Further resources on this subject:

You've been reading an excerpt of:

PySide GUI Application Development - Second Edition

Explore Title