Return to site

Pyqt Signals And Slots

broken image


  • PyQt5 - Signal and slot function The signal and slot operation are used to handle events and signals of the objects or widgets at the python app development level. It will also enable communication between some designed objects. The following steps are needed for creating a Python signal and slot operations.
  • In PyQt, interactivity is implemented using signals and slots. An event is an action performed by a user in the GUI (like clicking a button). A signal is raised by the corresponding widget when an event occurs on it. A slot is a function that is connected to the signal and executes when the signal is raised.
  • This post is about devising your own Signals and Slots mechanism to work with PyQt in a more dynamic fashion. The legendary Signals and Slots in Qt are not so difficult to understand, and once you understand it not so difficult to implement. Here is the class we will be talking about in this post.

Build complex application behaviours using signals and slots, and override widget event handling with custom events.

As part of our PyQt Tutorial series, we've gone through some basic layout management in addition to a conversation about some interface design but now when I click buttons I want things to happen! In order to achieve that goal, we're going to have to learn about signals and slots. Let me let you in on a little secret. Signals and slots?

As already described, every interaction the user has with a Qt application causes an Event. There are multiple types of event, each representing a difference type of interaction — e.g. mouse or keyboard events.

Events that occur are passed to the event-specific handler on the widget where the interaction occurred. For example, clicking on a widget will cause a QMouseEvent to be sent to the .mousePressEvent event handler on the widget. This handler can interrogate the event to find out information, such as what triggered the event and where specifically it occurred.

You can intercept events by subclassing and overriding the handler function on the class, as you would for any other function. You can choose to filter, modify, or ignore events, passing them through to the normal handler for the event by calling the parent class function with super().

However, imagine you want to catch an event on 20 different buttons. Subclassing like this now becomes an incredibly tedious way of catching, interpreting and handling these events.

python

Thankfully Qt offers a neater approach to receiving notification of things happening in your application: Signals.

Signals

Instead of intercepting raw events, signals allow you to 'listen' for notifications of specific occurrences within your application. While these can be similar to events — a click on a button — they can also be more nuanced — updated text in a box. Data can also be sent alongside a signal - so as well as being notified of the updated text you can also receive it.

The receivers of signals are called Slots in Qt terminology. A number of standard slots are provided on Qt classes to allow you to wire together different parts of your application. However, you can also use any Python function as a slot, and therefore receive the message yourself.

Load up a fresh copy of `MyApp_window.py` and save it under a new name for this section. The code is copied below if you don't have it yet.

Basic signals

First, let's look at the signals available for our QMainWindow. You can find this information in the Qt documentation. Scroll down to the Signals section to see the signals implemented for this class.

Qt 5 Documentation — QMainWindow Signals

As you can see, alongside the two QMainWindow signals, there are 4 signals inherited from QWidget and 2 signals inherited from Object. If you click through to the QWidget signal documentation you can see a .windowTitleChanged signal implemented here. Next we'll demonstrate that signal within our application.

Qt 5 Documentation — Widget Signals

The code below gives a few examples of using the windowTitleChanged signal.

python

Try commenting out the different signals and seeing the effect on what the slot prints.

We start by creating a function that will behave as a ‘slot' for our signals.

Then we use .connect on the .windowTitleChanged signal. We pass the function that we want to be called with the signal data. In this case the signal sends a string, containing the new window title.

If we run that, we see that we receive the notification that the window title has changed.

Events

Next, let's take a quick look at events. Thanks to signals, for most purposes you can happily avoid using events in Qt, but it's important to understand how they work for when they are necessary.

As an example, we're going to intercept the .contextMenuEvent on QMainWindow. This event is fired whenever a context menu is about to be shown, and is passed a single value event of type QContextMenuEvent.

Gardens

To intercept the event, we simply override the object method with our new method of the same name. So in this case we can create a method on our MainWindow subclass with the name contextMenuEvent and it will receive all events of this type.

If you add the above method to your MainWindow class and run your program you will discover that right-clicking in your window now displays the message in the print statement.

Sometimes you may wish to intercept an event, yet still trigger the default (parent) event handler. You can do this by calling the event handler on the parent class using super as normal for Python class methods.

python

This allows you to propagate events up the object hierarchy, handling only those parts of an event handler that you wish.

However, in Qt there is another type of event hierarchy, constructed around the UI relationships. Widgets that are added to a layout, within another widget, may opt to pass their events to their UI parent. In complex widgets with multiple sub-elements this can allow for delegation of event handling to the containing widget for certain events.

However, if you have dealt with an event and do not want it to propagate in this way you can flag this by calling .accept() on the event.

Alternatively, if you do want it to propagate calling .ignore() will achieve this.

python

In this section we've covered signals, slots and events. We've demonstratedsome simple signals, including how to pass less and more data using lambdas.We've created custom signals, and shown how to intercept events, pass onevent handling and use .accept() and .ignore() to hide/show eventsto the UI-parent widget. In the next section we will go on to takea look at two common features of the GUI — toolbars and menus.

Check it out on Github Last updated: 23/08/2020 10:33:32

QGIS Tutorials

This tutorial is part of our QGIS tutorial series:

This is a brief explanation of the concept of signal and slot in PyQt5, which is the GUI framework for QGIS plugins.

In summary, this very much resembles events and callbacks in JavaScript. It's an asynchronous mechanism to let one part of a program know when another part of a program was updated, which is a most crucial concept in GUI programming. You master signal/slot, you master a whole lot about plugin development in QGIS.

General concept

Generally a signal is a trigger which can be emitted (hence the term signal) and carry an arbitrary amount of information when it is emitted.

The signal can be connected to a slot, which needs to be Python callable (in other words, a method or a class, anything implementing the __call__ magic), which can be any arbitrary function. The slot can accept the information which is emitted by the signal to process it further.

This is useful when one object needs to know about the actions of another object. For instance, if your plugin features a button that should paste the clipboard contents into a text field, then your plugin would need to know which function to call once the button is clicked. This is typically done via signal and slot.

Pyqt

To intercept the event, we simply override the object method with our new method of the same name. So in this case we can create a method on our MainWindow subclass with the name contextMenuEvent and it will receive all events of this type.

If you add the above method to your MainWindow class and run your program you will discover that right-clicking in your window now displays the message in the print statement.

Sometimes you may wish to intercept an event, yet still trigger the default (parent) event handler. You can do this by calling the event handler on the parent class using super as normal for Python class methods.

python

This allows you to propagate events up the object hierarchy, handling only those parts of an event handler that you wish.

However, in Qt there is another type of event hierarchy, constructed around the UI relationships. Widgets that are added to a layout, within another widget, may opt to pass their events to their UI parent. In complex widgets with multiple sub-elements this can allow for delegation of event handling to the containing widget for certain events.

However, if you have dealt with an event and do not want it to propagate in this way you can flag this by calling .accept() on the event.

Alternatively, if you do want it to propagate calling .ignore() will achieve this.

python

In this section we've covered signals, slots and events. We've demonstratedsome simple signals, including how to pass less and more data using lambdas.We've created custom signals, and shown how to intercept events, pass onevent handling and use .accept() and .ignore() to hide/show eventsto the UI-parent widget. In the next section we will go on to takea look at two common features of the GUI — toolbars and menus.

Check it out on Github Last updated: 23/08/2020 10:33:32

QGIS Tutorials

This tutorial is part of our QGIS tutorial series:

This is a brief explanation of the concept of signal and slot in PyQt5, which is the GUI framework for QGIS plugins.

In summary, this very much resembles events and callbacks in JavaScript. It's an asynchronous mechanism to let one part of a program know when another part of a program was updated, which is a most crucial concept in GUI programming. You master signal/slot, you master a whole lot about plugin development in QGIS.

General concept

Generally a signal is a trigger which can be emitted (hence the term signal) and carry an arbitrary amount of information when it is emitted.

The signal can be connected to a slot, which needs to be Python callable (in other words, a method or a class, anything implementing the __call__ magic), which can be any arbitrary function. The slot can accept the information which is emitted by the signal to process it further.

This is useful when one object needs to know about the actions of another object. For instance, if your plugin features a button that should paste the clipboard contents into a text field, then your plugin would need to know which function to call once the button is clicked. This is typically done via signal and slot.

Signal

A signalhas to be a class attribute of a descendant of QObject. Any QGIS widget and almost all GUI classes are descendants of QObject (i.e. have QObject as the very basic parent class) and they all come with predefined signals, such as QgsFilterLineEdit's valueChanged signal, which is triggered when a user changes the text of the widget.

Definition

A signal has the general definition of PyQt5.QtCore.pyqtSignal(types), where types will be the data type(s) a signal can emit:

  • any basic Python data type (int, str, list etc.) or C++ type. In the latter case it needs to be defined as a string, e.g. pyqtSignal(int) or pyqtSignal('QgsPointXY')
  • multiple Python or C++ types, which will emit several values, e.g. pyqtSignal(int, str) will take two arguments
  • multiple sequences, which will create multiple versions of the signal, i.e. signal overloads, e.g. pyqtSignal([int], ['QgsPointXY'])

The first two options are fairly easy to grasp. However, the latter is a little more mysterious. Basically, overloaded signatures are a way to define the same object or class in multiple ways (you might call it Schrödinger's signal). The concept of overloaded class definitions is not really a thing in Python, though it can be done. If you define a signal with overloaded signatures, it's like you're creating the same object multiple times with different arguments, e.g. the example above would translate to:

This method to define a signal is a little more elaborate as we'll see soon, but very handy.

Methods

Pyqt Signals And Slots Across Threads

connect()

This method connects the signal to a slot. I.e. Texas holdem minimum bet. the signal can connect to a function, which takes its arguments and does something with them. For all practical purposes, you'll only need to pass the slot function to connect(). Each signal can connect to an arbitrary amount of slot functions.

disconnect()

Often you want to disconnect a slot from its signal to control whether the slot function should still be executed when the signal is triggered. You can either pass the specific slot function or nothing, in which case all slots for the signal will be disconnected.

emit()

Pyqt Signals And Slots Casino Game

When called, it emits values of the data types you specified when defining the signal (if any). These values have to be in the same order as in the definition, i.e. if pyqtSignal(int, str) was the definition, the signal needs to e.g. emit(4, 'blabla'), not emit('blabla', 4).

Examples

Let's see how this would work with more practical examples. To more relate to QGIS plugins, I'll use a similar (harshly abstracted) barebone structure as in our Interactive QGIS Plugin tutorial to depict the general usage when e.g. defining a new Map Tool (QgsMapTool).

Simple Example

This is only non-working pseudo-code, which will just demonstrate the general usage. A map tool is created which implements a canvasReleaseEvent event emitting a custom signal when triggered. This signal connects to a custom slot function in the main plugin code.

Signals And Slots Pyqt5

So, this hypothetical plugin would capture the point clicked by a user upon releasing the mouse button and print the WKT (Well Known Text) representation of that point to the Python console. Not very useful, I know, but I hope it gets the point across.

Overloaded signal example

Pyqt Signals And Slots No Deposit

Let's get a little fancier and say we want to print the distance of that point to our location when we click the mouse, but the WKT representation when we release the mouse button.

We can achieve this with the exact same signal if we define it with an overloaded signature. Yep, finally seeing how a Schrödinger's signal can work:

We now defined another canvasPressEvent, which will be triggered if the user presses the map canvas while the NewMapTool is active.

Since we defined our canvasClicked event now with the overloaded signature pyqtSignal([int], ['QgsPointXY']), we need to watch out that we only call the right signature for connect() and emit(). If we would omit the specific signature when calling these functions, they would use the first signature they find, which would be int in this case.

We connected both signatures to separate functions. Now, when the user clicks in the map canvas, the distance of the point to 13.413513, 52.491019 will be printed (*), when he releases the mouse button, the point's WKT representation will be printed.

Be aware however, that overloaded signatures have a catch: the Python data types in the pyqtSignal definition are converted to C++ types and some combinations can lead to undesired outcomes. E.g. pyqtSignal(, [dict]) will be converted to the same C++ data type. Calling emit() or connect() on the dict signature will be interpreted as calling the method on the list signature instead.

(*) Note, that those coordinates are in X, Y WGS84. The point captured by the canvasPressEvent depends on the map canvas CRS which is likely different, so you'd need to transform. Even if it were WGS84, the distance would be in degrees.





broken image