📄 Page
1
(This page has no text content)
📄 Page
2
Create GUI Applications with Python & Qt5 The hands-on guide to making apps with Python Martin Fitzpatrick Version 4.0, 2020-09-12
📄 Page
3
Table of Contents Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1. A very brief history of the GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2. A bit about Qt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3. Thankyou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4. Copyright. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Basic PySide2 Features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 5. My first Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 6. Signals & Slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 7. Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 8. Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 9. Actions, Toolbars & Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 10. Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 11. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 12. Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 Qt Designer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 13. Installing Qt Designer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 14. Getting started with Qt Designer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 15. The Qt Resource system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Theming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 16. Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 17. Palettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 18. Icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 19. Qt Style Sheets (QSS). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Model View Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 20. The Model View Architecture — Model View Controller . . . . . . . . . . . . . . 260 21. A simple Model View — a Todo List. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 22. Tabular data in ModelViews, with numpy & pandas . . . . . . . . . . . . . . . . . . 279 23. Querying SQL databases with Qt models. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 Further PySide2 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335 24. Extending Signals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 25. Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 26. Working with command-line arguments. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 27. System tray & macOS menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 28. Enums & the Qt Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 Custom Widgets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
📄 Page
4
29. Bitmap Graphics in Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 30. Creating Custom Widgets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 Concurrent Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446 31. Introduction to Threads & Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 32. Using the thread pool. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453 33. Threading examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 34. Running external commands & processes. . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 Plotting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 35. Plotting with PyQtGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 36. Plotting with Matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 Packaging & Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 37. Packaging with fbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 Example applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 38. Mozzarella Ashbadger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598 39. Moonsweeper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Appendix A: Installing PySide2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640 Appendix B: Translating C++ Examples to Python . . . . . . . . . . . . . . . . . . . . . . . . . . 643 Appendix C: PyQt5 and PySide2 — What’s the difference? . . . . . . . . . . . . . . . . . 655 Appendix D: What next?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
📄 Page
5
Introduction If you want to create GUI applications with Python it can be tricky to know where to start. There are a lot of new concepts you need to understand to get anything to work. But, like any coding problem, the first step is learning to approach the problem in the right way. In this book I take you right from the basic principles of GUI development to creating your own, fully functional, desktop apps with PySide2. The first edition of this book was released in 2016. Since then it has been updated 4 times, adding and expanding chapters in response to reader feedback. There are more PySide2 resources available now than when I started, but there is still a shortage of in-depth, practical guides to building complete apps. This book fills that gap! The book is formatted as a series of chapters exploring different aspects of PySide2 in turn. They are arranged to put the simpler chapters toward the beginning, but if you have specific requirements for your project, don’t be afraid to jump around! Each chapter will guide you through learning the fundamental concepts before taking you through a series of coding examples to gradually explore and learn how to apply the ideas yourself. You can download source code and resources for all examples in this book. But don’t be tempted just to read the code and move on — you will learn much more if you experiment along the way. It is not possible to give you a complete overview of the Qt system in a book of this size, so there are links to external resources — both on the LearnPyQt.com website and elsewhere. If you find yourself thinking "I wonder if I can do that?" the best thing you can do is put this book down, then go and find out! Just keep regular backups of your code along the way so you always have something to come back to if you royally mess it up. 1
📄 Page
6
Throughout this book there are boxes like this, giving info, tips and warnings. All of them can be safely skipped over if you are in a hurry, but reading them will give you a deeper and more rounded knowledge of the Qt framework. Finally, this book is written to be compatible with Python 3.4+. Python 3 is the future of the language and if you’re starting out now is where you should be focusing your efforts. However, many of the examples can be changed with minimal effort to work with Python 2.7. 2
📄 Page
7
1. A very brief history of the GUI The Graphical User Interface has a long and venerable history dating back as far as the 1960s. Stanford’s NLS (oN-Line System) introduced the the mouse and windows concept, first demonstrated publicly in 1968. This was followed by the Xerox PARC Smalltalk system GUI 1973, which is the foundation of most modern general purpose GUIs. These early systems already had many of the features we take for granted in modern desktop GUIs, including windows, menus, radio buttons, check boxes and later icons. This combination of features — gave us the early acronym used for these types of interfaces: WIMP (windows, icons, menus, pointing device — a mouse). In 1979 the first commercial system featuring a GUI was released — the PERQ workstation. This spurred a number of other GUI efforts including notably the Apple Lisa (1983), which added the concept of the menu bar and window controls. As well as many other systems from the Atari ST (GEM), Amiga. On UNIX (and later Linux) the X Window System emerged in 1984. The first version of Windows for PC was released in 1985. Figure 1. The desktop on Microsoft Windows 3.1 (1992) and Apple System 7 (1991) Early GUIs were not the instant hit we might assume, due to the lack of compatible software at launch and expensive hardware requirements — particularly for home users. Slowly, but steadily, the GUI 3
📄 Page
8
interface become the preferred way to interact with computers and the WIMP metaphor became firmly established as the standard. That’s not to say there haven’t been attempts to replace the WIMP metaphor on the desktop. Microsoft Bob (1995), for example, was Microsoft’s much maligned attempt to replace the desktop with a house. Figure 2. Microsoft Bob — Discarding the desktop metaphor for a cartoon house. There have been no shortage of other GUIs hailed as revolutionary in their time, from the launch of Windows 95 (1995) through to Mac OS X (2001), GNOME Shell (2011) and Windows 10 (2015). Each of these overhauled the UIs of their respective desktop systems, often with much fanfare. But fundamentally nothing really changed. These new UIs are still very much WIMP systems and function in exactly the same way as GUIs have since the 1980s. When the revolution came, it was mobile — the mouse has been replaced by touch, and windows by full-screen apps. But even in a world where we all walk around with smartphones in our pocket, a huge amount of daily work is still done on desktop computers. WIMP has survived 40 years of innovation and looks to survive many more. 4
📄 Page
9
2. A bit about Qt Qt is a free and open-source widget toolkit for creating cross-platform GUI applications, allowing applications to target multiple platforms from Windows, macOS, Linux and Android with a single codebase. But Qt is much more than a widget toolkit and features built in support for multimedia, databases, vector graphics and MVC interfaces, it is more accurate to think of it as an application development framework. Qt was started by Eirik Chambe-Eng and Haavard Nord in 1991, founding the first Qt company Trolltech in 1994. Qt is currently developed by The Qt Company and continues to be regularly updated, adding features and extending mobile and cross-platform support. Qt and PySide2 PySide2, also known as Qt for Python is a Python binding of the Qt toolkit, currently developed by The Qt Company. When you write applications using PySide2 what you area really doing is writing applications in Qt. The PySide2 library is simply.[1] a wrapper around the C++ Qt library, to allow it to be used in Python. Because this is a Python interface to a C++ library the naming conventions used within PySide2 do not adhere to PEP8 standards. Most notably functions and variables are named using mixedCase rather than snake_case. Whether you adhere to this standard in your own applications is entirely up to you, however I find it helped to follow Python standards for my own code, to help clarify where the PySide2 code ends and your own begins. Lastly, while there is PySide2 specific documentation available, you will often find yourself reading the Qt documentation itself as it is more complete. If you do you will need to translate object syntax and some methods containing Python-reserved function names as follows: 5
📄 Page
10
Qt PySide2 Qt::SomeValue Qt.SomeValue object.exec() object.exec_() object.print() object.print_() If you need to convert an entire Qt code example to Python, take a look at Translating C++ Examples to Python. This book is written to work with the latest version of Qt (and PySide2). As of writing this is Qt 5.14. However, many of the examples will work fine with earlier and later versions of Qt. [1] Not really that simple. 6
📄 Page
11
3. Thankyou This book continues to be expanded and updated in response to reader feedback. Thankyou to the following readers for their contributions, which helped make this edition what it is! • James Battat, Associate Professor of Physics, Wellesley College • Andries Broekema • Richard Hohlfield • Olivier Girard • Gajendra Khanna • Bing Xiao Liu • Alex Lombardi • Cody Jackson, Mentor, Code-a-Mom • John E Kadwell • Jeffrey R Kennedy • Juan Pablo Donayre Quintana • Guido Tognan If you have feedback or suggestions for future editions, just let me know. 7
📄 Page
12
4. Copyright This book is licensed under the Creative Commons Attribution Share-alike Non-commercial license (CC BY-NC-SA) ©2020 Martin Fitzpatrick. • You are free to share unaltered copies of this book with anyone you choose. • If you modify this book and distribute your altered version it must be distributed under the same license. • You are not permitted to sell this book or derivatives in any format. • If you would like to support the author you can legally purchase a copy direct from the author(s). Contributions and corrections from readers (CC BY-NC-SA) are most welcome. 8
📄 Page
13
Basic PySide2 Features It’s time to take your first steps in creating GUI applications with PySide2! In this chapter you will be introduced to the basics of PySide2 that are the foundations of any application you create. We will develop a simple windowed application on your desktop. We’ll add widgets, arrange them using layouts and connect these widgets to functions, allowing you to trigger application behavior from your GUI. Use the provided code as your guide, but always feel free to experiment. That’s the best way to learn how things work. Before you get started, you need a working installation of PySide2. If you don’t have one yet, check out Installing PySide2. Don’t forget to download the source code that accompanies this book. 9
📄 Page
14
5. My first Application Let’s create our first application! To start create a new Python file — you can call it whatever you like (e.g. myapp.py) and save it somewhere accessible. We’ll write our simple app in this file. We’ll be editing within this file as we go along, and you may want to come back to earlier versions of your code, so remember to keep regular backups. Creating your App The source code for your very first application is shown below. Type it in verbatim, and be careful not to make mistakes. If you do mess up, Python will let you know what’s wrong. If you don’t feel like typing it all in, the file is included in the source code with this book. 10
📄 Page
15
Listing 1. basic/creating_a_window_1.py from PySide2.QtWidgets import QApplication, QWidget # Only needed for access to command line arguments import sys # You need one (and only one) QApplication instance per application. # Pass in sys.argv to allow command line arguments for your app. # If you know you won't use command line arguments QApplication([]) works too. app = QApplication(sys.argv) # Create a Qt widget, which will be our window. window = QWidget() window.show() # IMPORTANT!!!!! Windows are hidden by default. # Start the event loop. app.exec_() # Your application won't reach here until you exit and the event # loop has stopped. First, launch your application. You can run it from the command line like any other Python script, for example — python MyApp.py Or, for Python 3 — python3 MyApp.py From now on, you’ll see the following box as a hint to run your application and test it out, along with an indication of what you’ll see. 11
📄 Page
16
Run it! You will now see your window. Qt automatically creates a window with the normal window decorations and you can drag it around and resize it like any window. What you’ll see will depend on what platform you’re running this example on. The image below shows the window as displayed on Windows, macOS and Linux (Ubuntu). Figure 3. Our window, as seen on Windows, macOS and Linux. Stepping through the code Let’s step through the code line by line, so we understand exactly what is happening. First, we import the PySide2 classes that we need for the application. Here we’re importing QApplication, the application handler and QWidget, a basic empty GUI widget, both from the QtWidgets module. from PySide2.QtWidgets import QApplication, QWidget The main modules for Qt are QtWidgets, QtGui and QtCore. You could do from <module> import * but this kind of global import is generally frowned upon in Python, so we’ll avoid it here. Next we create an instance of QApplication, passing in sys.arg, which is 12
📄 Page
17
Python list containing the command line arguments passed to the application. app = QApplication(sys.argv) If you know you won’t be using command line arguments to control Qt you can pass in an empty list instead, e.g. app = QApplication([]) Next we create an instance of a QWidget using the variable name window. window = QWidget() window.show() In Qt all top level widgets are windows — that is, they don’t have a parent and are not nested within another widget or layout. This means you can technically create a window using any widget you like. I can’t see my window! Widgets without a parent are invisible by default. So, after creating the window object, we must always call .show() to make it visible. You can remove the .show() and run the app, but you’ll have no way to quit it! What is a window? • Holds the user-interface of your application • Every application needs at least one (…but can have more) • Application will (by default) exit when last window is closed Finally, we call app.exec_() to start up the event loop. 13
📄 Page
18
What’s the event loop? Before getting the window on the screen, there are a few key concepts to introduce about how applications are organised in the Qt world. If you’re already familiar with event loops you can safely skip to the next section. The core of every Qt Applications is the QApplication class. Every application needs one — and only one — QApplication object to function. This object holds the event loop of your application — the core loop which governs all user interaction with the GUI. Figure 4. The event loop in Qt. Each interaction with your application — whether a press of a key, click of a mouse, or mouse movement — generates an event which is placed on the event queue. In the event loop, the queue is checked on each iteration and if a waiting event is found, the event and control is passed to the specific event handler for the event. The event handler deals with the event, then passes control back to the event loop to wait for more events. There is only one running event loop per application. 14
📄 Page
19
The QApplication class • QApplication holds the Qt event loop • One QApplication instance required • You application sits waiting in the event loop until an action is taken • There is only one event loop at any time The underscore is there because exec was a reserved word in Python 2.7. PySide2 handles this by appending an underscore to the name used in the C++ library. You’ll also see .print_() methods on widgets for example. QMainWindow As we discovered in the last part, in Qt any widgets can be windows. For example, if you replace QtWidget with QPushButton. In the example below, you would get a window with a single push-able button in it. Listing 2. basic/creating_a_window_2.py from PySide2.QtWidgets import QApplication, QPushButton window = QPushButton("Push Me") window.show() This is neat, but not really very useful — it’s rare that you need a UI that consists of only a single control! But, as we’ll discover later, the ability to nest widgets within other widgets using layouts means you can construct complex UIs inside an empty QWidget. But, Qt already has a solution for you — the QMainWindow. This is a pre-made widget which provides a lot of standard window features you’ll make use of in your apps, including toolbars, menus, a statusbar, dockable widgets and more. We’ll look at these advanced features later, but for now, we’ll add a simple empty QMainWindow to our application. 15
📄 Page
20
Listing 3. basic/creating_a_window_3.py from PySide2.QtWidgets import QApplication, QMainWindow import sys app = QApplication(sys.argv) window = QMainWindow() window.show() # IMPORTANT!!!!! Windows are hidden by default. # Start the event loop. app.exec_() Run it! You will now see your main window. It looks exactly the same as before! So our QMainWindow isn’t very interesting at the moment. We can fix that by adding some content. If you want to create a custom window, the best approach is to subclass QMainWindow and then include the setup for the window in the __init__ block. This allows the window behavior to be self contained. We can add our own subclass of QMainWindow — call it MainWindow to keep things simple. 16