Events and Signals

Exclusive offer: get 50% off this eBook here
PySide GUI Application Development

PySide GUI Application Development — Save 50%

Develop more dynamic and robust GUI applications using an open source cross-platform UI framework with this book and ebook

£13.99    £7.00
by Venkateshwaran Loganathan | October 2013 | Open Source

In this article by Venkateshwaran Loganathan, the author of PySide GUI Application Development, 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:

while(application_is_active) { while(event_exists_in_event_queue) process_next_event(); wait_for_more_events(); }

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): QWidget.__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() self.myLabel.setAlignment(Qt.AlignCenter) self.infoLabel.setAlignment(Qt.AlignCenter) self.myLayout.addWidget(self.myLabel) self.myLayout.addWidget(self.infoLabel) self.setLayout(self.myLayout) # Function reimplementing Key Press, Mouse Click and Resize Events def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.close() def mouseDoubleClickEvent(self, event): self.close() def resizeEvent(self, event): self.infoLabel.setText("Window Resized to QSize(%d, %d)" %
(event.size().width(), event.size().height())) if __name__ =='__main__': # Exception Handling try: myApp = QApplication(sys.argv) myWidget = MyWidget() myWidget.show() myApp.exec_() sys.exit(0) except NameError: print("Name Error:", sys.exc_info()[1]) except SystemExit: print("Closing Window...") except Exception: print(sys.exc_info()[1])

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:

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): QWidget.__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() self.myLayout.addWidget(self.myLabel1) self.myLayout.addWidget(self.myLineEdit1) self.myLayout.addWidget(self.myLabel2) self.myLayout.addWidget(self.myLineEdit2) self.myLayout.addWidget(self.myLabel3) self.myLayout.addWidget(self.myLineEdit3) self.setLayout(self.myLayout) # Function reimplementing event() function def event(self, event): if event.type()== QEvent.KeyRelease and event.key()== Qt.Key_Tab: self.myLineEdit3.setFocus() 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:

self.installEventFilter(self)

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 have already seen the concept of connecting a signal to a slot in the earlier chapters while designing the text editor application. Those implementations handle and connect different signals to different objects. 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): QWidget.__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.emiValue.setSegmentStyle(QLCDNumber.Flat) self.emiValue.setFixedSize(QSize(130,30)) self.emiValue.setDigitCount(8) self.amtText = QLineEdit('10000') self.roiSpin = QSpinBox() self.roiSpin.setMinimum(1) self.roiSpin.setMaximum(15) self.yrsSpin = QSpinBox() self.yrsSpin.setMinimum(1) self.yrsSpin.setMaximum(20) self.roiDial = QDial() self.roiDial.setNotchesVisible(True) self.roiDial.setMaximum(15) self.roiDial.setMinimum(1) self.roiDial.setValue(1) self.yrsSlide = QSlider(Qt.Horizontal) self.yrsSlide.setMaximum(20) self.yrsSlide.setMinimum(1) 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.setLayout(self.myGridLayout) 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.roiDial.valueChanged.connect(self.roiSpin.setValue) self.connect(self.roiSpin, SIGNAL("valueChanged(int)"),
self.roiDial.setValue)

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.yrsSlide.valueChanged.connect(self.yrsSpin.setValue) 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.showEMI)

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.emiValue.display(emi) 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:

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): print(text) class CustomSignal(QObject): # create a new signal mySignal = Signal(str) if __name__ == '__main__': try: myObject = CustomSignal() # connect signal and slot myObject.mySignal.connect(printText) # emit signal myObject.mySignal.emit("Hello, Universe!") except Exception: print(sys.exc_info()[1])

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.

PySide GUI Application Development Develop more dynamic and robust GUI applications using an open source cross-platform UI framework with this book and ebook
Published: October 2013
eBook Price: £13.99
Book Price: £21.99
See more
Select your format and quantity:

Drag-and-drop

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:

Class

Description

QDragEnterEvent

Provides an event which is sent to a widget when drag and drop action enters it

QDragLeaveEvent

Provides an event which is sent to a widget when drag and drop action leaves it

QDragMoveEvent

Provides an event which is sent to a widget when drag and drop action is in progress

QDropEvent

Provides an event which is sent to a widget when drag and drop action is completed

QMimeData

Provides a container for data that records information about its MIME type

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 in class MyWidget(QWidget):

class MyWidget(QWidget): def __init__(self): QWidget.__init__(self) self.myListWidget1 = QListWidget() self.myListWidget2 = QListWidget() self.myListWidget2.setViewMode(QListWidget.IconMode) self.myListWidget1.setAcceptDrops(True) self.myListWidget1.setDragEnabled(True) self.myListWidget2.setAcceptDrops(True) self.myListWidget2.setDragEnabled(True) self.setGeometry(300, 350, 500, 150) self.myLayout = QHBoxLayout() self.myLayout.addWidget(self.myListWidget1) 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.myListWidget2) QListWidgetItem(QIcon('brown_pig.png'), "Brown Pig",
self.myListWidget2) QListWidgetItem(QIcon('green_pig.png'), "Green Pig",
self.myListWidget2) self.setWindowTitle('Drag and Drop Example'); self.setLayout(self.myLayout)

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

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.

Drawing

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:

Function

Description

drawPoint()

Draws a single point at the given position

drawText()

Draws the given text within the defined rectangle

drawLine()

Draws a line between two point pairs

drawRect()

Draws a rectangle by the given rectangle

drawRoundedRect()

Draws a rectangle with rounded edges or corners

drawEllipse()

Draws an ellipse defined by the given rectangle

drawArc()

Draws an arc defined by the given rectangle

drawPie()

Draws a pie defined by the given rectangle

drawChord()

Draws a chord defined by the given rectangle

drawPolyline()

Draws a polyline defined by the given points

drawPolygon()

Draws a polygon defined by the given points

drawConvexPolygon()

Draws a convex polygon defined by the given polygon

drawImage()

Draws the given Image into the given rectangle

drawPath()

Draws the given painter path defined by the QPainterPath

drawPicture()

Draws the given picture

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() painter.begin(self) painter.setPen(self.pen) painter.setBrush(self.brush) if self.shape == PaintArea.Line: painter.drawLine(rect.bottomLeft(), rect.topRight()) elif self.shape == PaintArea.Points: painter.drawPoints(PaintArea.points) elif self.shape == PaintArea.Polyline: painter.drawPolyline(PaintArea.points) elif self.shape == PaintArea.Polygon: painter.drawPolygon(PaintArea.points) elif self.shape == PaintArea.Rect: painter.drawRect(rect) elif self.shape == PaintArea.RoundRect: painter.drawRoundRect(rect) elif self.shape == PaintArea.Ellipse: painter.drawEllipse(rect) 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: painter.drawPath(path) elif self.shape == PaintArea.Text: painter.drawText(rect, QtCore.Qt.AlignCenter,
"Basic Drawing Widget") painter.end()

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:

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): QGraphicsView.__init__(self) self.myScene = QGraphicsScene(self) self.myItem = QGraphicsEllipseItem(-20, -10, 50, 20) self.myScene.addItem(self.myItem) self.setScene(self.myScene) self.timeLine = QTimeLine(1000) self.timeLine.setFrameRange(0, 100) self.animate = QGraphicsItemAnimation() self.animate.setItem(self.myItem) self.animate.setTimeLine(self.timeLine) self.animate.setPosAt(0, QPointF(0, -10)) self.animate.setRotationAt(1, 360) self.setWindowTitle("A Simple Animation") self.timeLine.start()

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 article. 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) self.effect.setBlurRadius(8) self.myItem.setGraphicsEffect(self.effect) self.myItem.setZValue(1)

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.

Summary

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 article.

Resources for Article:


Further resources on this subject:


PySide GUI Application Development Develop more dynamic and robust GUI applications using an open source cross-platform UI framework with this book and ebook
Published: October 2013
eBook Price: £13.99
Book Price: £21.99
See more
Select your format and quantity:

About the Author :


Venkateshwaran Loganathan

Venkateshwaran Loganathan is an eminent software developer who has been involved in the design, development, and testing of software products for more than five years. He was introduced to computer programming at the age of 11 with FoxPro and then started to learn and master various computer languages like C, C++, Perl, Python, node.js, and Unix shell scripting. Fascinated by open source development, he has contributed to various open source technologies. He is now working for Cognizant Technology Solutions as an Associate in Technology, where he has involved himself in research and development for Internet of Things domain. He is now actively involved in using RFID devices for evolving the Future of Technology concepts. Before joining Cognizant, he worked at Infosys, Virtusa, and NuVeda. Starting his career as a Network Developer, he has gained expertise in various domains like Networking, e-learning, and HealthCare. He has won various awards and accolades for his work.

He holds a bachelor's degree in Computer Science and Engineering from Anna University and currently pursuing M.S in software systems from BITS, Pilani. Apart from programming, he is actively involved in handling various technical and soft skills classes for the budding engineers and college students. His hobbies are singing and trekking. He likes to get involved with social media. Visit him online at http://www.venkateshwaranloganathan.com and write to him at anandvenkat4@gmail.com.

Books From Packt


Practical Maya Programming with Python
Practical Maya Programming with Python

Python Multimedia
Python Multimedia

Expert Python Programming
Expert Python Programming

Spring Python 1.1
Spring Python 1.1

Python Data Visualization Cookbook
Python Data Visualization Cookbook

Learning Geospatial Analysis with Python
Learning Geospatial Analysis with Python

MySQL for Python
MySQL for Python

Tkinter GUI Application Development Hotshot
Tkinter GUI Application Development Hotshot


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software