Welcome, Python programmer!
Python is a great language for system administration, data analysis, web services, and command-line programs; most likely you've already found Python useful in at least one of those areas. However, there is something truly satisfying about building the kind of GUI-driven application that an end user can readily identify as a program, and this skill should be in the toolbox of any master software developer. In this book, you're going to learn how you can use Python and the Qt framework to develop amazing applications—from simple data-entry forms to powerful multimedia tools.
We'll start our tour of these powerful technologies with the following topics:
- Introducing Qt and PyQt
- Creating Hello Qt – our first window
- Creating a PyQt application template
- Introducing Qt Designer
For this chapter, and most of the rest of the book, you're going to need the following:
- A PC running Microsoft Windows, Apple macOS, or a 64-bit flavor of GNU/Linux.
- Python 3, available from http://www.python.org. The code in this book requires Python 3.7 or later.
- PyQt 5.12, which you can install from the Python Package Index using this command:
$ pip install --user PyQt5
- Linux users may also wish to install PyQt5 from their distribution's package repositories.
- Qt Designer 4.9, a WYSIWYG GUI building tool available from https://www.qt.io. See the following section for installation instructions.
- The example code from https://github.com/PacktPublishing/Mastering-GUI-Programming-with-Python/tree/master/Chapter01.
Check out the following video to see the code in action: http://bit.ly/2M5OUeg
On Windows or macOS, Qt Designer is part of the Qt Creator IDE from the Qt company. This is a free IDE that you can use for coding, though, at the time of writing, it is mainly aimed at C++ and support for Python is rudimentary. The Qt Designer component can be used regardless of whether you do your coding in Qt Creator or not.
You can download an installer for Qt Creator from https://download.qt.io/official_releases/qtcreator/4.9/4.9.0/.
Although the Qt company offers a similar standalone Qt installer for Linux, most Linux users will prefer to use packages from their distribution's repositories. Some distributions offer Qt Designer as a standalone application, while others include it in their Qt Creator packages.
This table shows the package that will install Qt Designer in several major distributions:
|Ubuntu, Debian, Mint||qttools5-dev-tools|
|Fedora, CentOS, Red Hat, SUSE||qt-creator|
|Arch, Manjaro, Antergos||qt5-tools|
Qt is a cross-platform application framework that was created for use with C++. Available in both commercial and open source licenses (General Public License (GPL) v3 and Lesser General Public License (LGPL) v3, specifically), it is widely used by open source projects such as KDE Plasma and Oracle VirtualBox, commercial software such as Adobe Photoshop Elements and Autodesk Maya, and even embedded software in products from companies such as LG and Panasonic. Qt is currently owned and maintained by the Qt company (https://www.qt.io).
In this book, we're going to be working with the open source release of Qt 5.12. If you're using Windows, macOS, or a major Linux distribution, you should not need to install Qt explicitly; it will be installed automatically when you install PyQt5.
Qt is officially pronounced cute, though many people say, Q T.
PyQt is a Python library that allows the Qt framework to be used in Python code. It was developed by Riverbank Computing under the GPL license, although commercial licenses can be purchased for those wanting to develop proprietary applications. (Note that this is a separate license from the Qt license.) It is currently supported on Windows, Linux, UNIX, Android, macOS, and iOS.
PyQt's bindings are generated automatically by a tool called SIP, so, to a large extent, working with PyQt is just like working with Qt itself, only in Python. In other words, the classes, methods, and other objects are all identical in usage, apart from the language syntax.
The Qt company has recently released Qt for Python (also known as PySide2), their own Python Qt5 library, under the terms of the LGPL. Qt for Python is functionally equivalent to PyQt5, and code can be ported between them with very few changes. This book will cover PyQt5, but what you learn can easily be applied to Qt for Python, should you need an LGPL library.
Qt is much more than a GUI library; it's an application framework. It contains dozens of modules with thousands of classes. It has classes to wrap simple data types such as dates, times, URLs, or color values. It has GUI components such as buttons, text entries, or dialog boxes. It has interfaces for hardware such as cameras or mobile sensors. It has a networking library, a threading library, and a database library. If anything, Qt is truly a second standard library!
Qt is written in C++ and designed around the needs of C++ programmers; it works well with Python, but Python programmers may find some of its concepts slightly foreign at first.
For example, Qt objects usually expect to work with data wrapped in Qt classes. A method that expects a color value won't accept a string or a tuple of RGB values; it wants a QColor object. A method that returns a size won't return a (width, height) tuple; it will return a QSize object. PyQt mitigates this somewhat by automatically converting some common data types (for example, strings, lists, dates, and times) between Qt objects and Python standard library types; however, there are many hundreds of Qt classes that have no analog in the Python standard library.
Qt relies heavily on named constants called enums or flags to represent things such as option settings or configuration values. For example, if you wanted to switch the state of a window between minimized, floating, or maximized, you would need to pass the window a constant that is found in the QtCore.Qt.WindowState enum.
Setting or retrieving values on Qt objects requires the use of accessor methods, sometimes known as setter and getter methods, rather than direct access to the properties.
To the Python programmer, Qt can seem to have an almost maniacal obsession with defining classes and constants, and you'll spend a lot of time early on searching the documentation to locate the item you need to configure your objects. Don't despair! You'll soon become acclimated to the Qt way of working.
For the first six chapters of this book, we'll be working primarily with three Qt modules:
- QtCore, which contains low-level data wrapper classes, utility functions, and non-GUI core functionality
- QtGui, which contains GUI-specific data wrapper classes and utilities
- QtWidgets, which defines GUI widgets, layouts, and other high-level GUI components
Those three modules will be used in nearly any PyQt program we write. Later in the book, we will explore other modules for graphics, networking, web rendering, multimedia, and other advanced capabilities.
Now that you've learned about Qt5 and PyQt5, it's time to dig in and do some coding. Make sure everything is installed, open your favorite Python editor or IDE, and let's begin!
Create a hello_world.py file in your editor, and enter the following:
from PyQt5 import QtWidgets
We begin by importing the QtWidgets module. This module contains the bulk of the widget classes in Qt, as well as some other important components for GUI creation. We won't need QtGui or QtCore for such a simple application.
Next, we need to create a QApplication object, like this:
app = QtWidgets.QApplication()
The QApplication object represents the state of our running application, and one must be created before any other Qt widgets can be created. QApplication is supposed to be passed a list of command-line arguments given to our script, but here we're just passing in an empty list.
Now, let's create our first widget:
window = QtWidgets.QWidget(windowTitle='Hello Qt')
In GUI toolkit terms, a widget refers to the visible components of the GUI, such as buttons, labels, text entries, or blank panels. The most generic widget in Qt is the QWidget object, which is just a blank window or panel. As we create this widget, we're settings its windowTitle to 'Hello Qt'. windowTitle is what is known as property. All Qt objects and widgets have properties, which are used to configure different aspects of the widget. In this case, windowTitle is the name of the program window and appears in the window decorations, on the taskbar or dock, or wherever else your OS and desktop environment choose to use it.
Unlike most Python libraries, Qt properties and methods are named using camelCase rather than snake_case.
The properties available for configuring a Qt object can be set by passing them as constructor arguments or using the appropriate setter method. Typically, this is just set plus the name of the property, so we could have written this:
window = QtWidgets.QWidget() window.setWindowTitle('Hello Qt')
Properties can also be retrieved using the getter method, which is just the property name:
Once a widget is created, we can make it appear by calling show(), as follows:
Calling show() automatically makes window a top-level window of its own. In Chapter 2, Building Forms with Qt Widgets, you'll see how to place widgets inside other widgets, but, for this program, we only need one top-level widget.
The last line is a call to app.exec(), like this:
app.exec() begins the QApplication object event loop. The event loop will run forever until the application quits, processing our user interactions with the GUI. Note that the app object never refers to window, nor window to the app object. These objects are connected automatically in the background; you need only ensure that a QApplication object exists before creating any QWidget objects.
Save the hello_world.py file and run the script from your editor, or from a command line, like so:
When you run this, you should see a blank window whose title text is Hello Qt:
This isn't a terribly exciting application, but it does show us the basic workflow of any PyQt application:
- Create a QApplication object
- Create our main application window
- Display our main application window
- Call QApplication.exec() to start the event loop
If you're experimenting with PyQt in the Python Read-Eval-Print-Loop (REPL), create the QApplication object by passing in a list with a single empty string, like this: QtWidgets.QApplication(['']); otherwise, Qt will crash. Also, you don't need to call QApplication.exec() in the REPL, thanks to some special PyQt magic.
hello_world.py demonstrated the bare minimum of code to get a Qt window on the screen, but it's a bit too simplistic to serve as a model for more complex applications. In this book, we're going to be creating many PyQt applications, so, to make things easier, we're going to compose a basic application template. Future chapters will refer to this template, so make sure to create it exactly as specified.
Open a new file called qt_template.py, and add in these imports:
import sys from PyQt5 import QtWidgets as qtw from PyQt5 import QtGui as qtg from PyQt5 import QtCore as qtc
We'll start with importing sys, so that we can pass QApplication an actual list of script arguments; then we'll import our three main Qt modules. To save some typing, while avoiding star imports, we're going to alias them to abbreviated names. We'll be using these aliases consistently throughout the book as well.
Star imports (also called wildcard imports), such as from PyQt5.QtWidgets import *, are convenient and often seen in tutorials, but, in practice, they are best avoided. Doing this with a PyQt module will fill your namespace with hundreds of classes, functions, and constants, any of which you might accidentally overwrite with a variable name. Avoiding star imports will also help you to learn which modules contain which commonly used classes.
Next, we'll create a MainWindow class, as follows:
class MainWindow(qtw.QWidget): def __init__(self): """MainWindow constructor""" super().__init__() # Main UI code goes here # End main UI code self.show()
To make our MainWindow class, we subclass QWidget, then override the constructor method. Whenever we use this template in future chapters, start adding your code between the commented lines unless otherwise instructed.
Subclassing PyQt classes is a good way to approach GUI building. It allows us to customize and expand on Qt's powerful widget classes without having to reinvent the wheel. In many cases, subclassing is the only way to utilize certain classes or accomplish certain customizations.
Our constructor ends with a call to self.show(), so our MainWindow will take care of showing itself.
Always remember to call super().__init__() inside your child class's constructor, especially with Qt classes. Failing to do so means the parent class isn't properly set up and will undoubtedly cause very frustrating bugs.
We'll finish our template with the main code execution:
if __name__ == '__main__': app = qtw.QApplication(sys.argv) mw = MainWindow() sys.exit(app.exec())
In this code, we're going to create our QApplication object, make our MainWindow object, and then call QApplication.exec(). Although not strictly necessary, it's best practice to create the QApplication object at the global scope (outside of any function or class). This ensures that all Qt objects get properly closed and cleaned up when the application quits.
Notice that we're passing sys.argv into QApplication(); Qt has several default command-line arguments that can be used for debugging or to alter styles and themes. These are processed by the QApplication constructor if you pass in sys.argv.
Also, note that we're calling app.exec() inside a call to sys.exit; this is a small touch that causes the exit code of app.exec() to be passed to sys.exit(), so we pass appropriate exit codes to the OS, if the underlying Qt instance crashes for some reason.
Finally, note that we've wrapped this block in this check:
if __name__ == '__main__':
If you've never seen this before, it's a common Python idiom that simply means: only run this code if this script is called directly. By putting our main execution in this block, we could conceivably import this file into another Python script and be able to reuse our MainWindow class without running any of the code in this block.
If you run your template code, you should see a blank application window. In the following chapters, we'll be filling that window with various widgets and functionality.
Before we wrap up our introduction to Qt, let's look at a free tool offered by the Qt company that can help us create PyQt applications—Qt Designer.
Qt Designer is a graphical WYSIWYG GUI designer for Qt. Using Qt Designer, you can drag and drop GUI components into an application and configure them without having to write any code at all. While it is certainly an optional tool, you may find it useful for prototyping, or preferable to hand-coding a large and complex GUI. While most of the code in this book will be hand-coded, we will be covering the use of Qt Designer with PyQt in Chapter 2, Building Forms with Qt Widgets, and Chapter 3, Handling Events with Signals and Slots.
Let's take a moment to get familiar with how to launch and use Qt Designer:
- Launch Qt Creator
- Select File | New File or Project
- Under Files and Classes, select Qt
- Choose Qt Designer Form
- Under Choose a Template Form, select Widget, then click Next
- Give your form a name and click Next
- Click Finish
You should see something that looks like this:
If you installed Qt Designer as a standalone application on Linux, launch it with the designer command or select it from your program's menu. You shouldn't need the previous steps.
Take a few minutes to test out Qt Designer:
- Drag some widgets from the left pane onto your base widget
- Resize the widgets if you wish, or select one and examine its properties in the lower-right pane
- When you've made several changes, select Tools | Form Editor | Preview, or hit Alt + Shift + R, to preview your GUI
In Chapter 2, Building Forms with Qt Widgets, we'll go into detail on how to use Qt Designer to build a GUI interface; for now, you can find out more information about Qt Designer from the manual at https://doc.qt.io/qt-5/qtdesigner-manual.html.
In this chapter, you learned about the Qt application framework and the PyQt Python bindings for Qt. We wrote a Hello World application and created a template for building larger Qt applications. Finally, we installed and took our first look at Qt Designer, the GUI editor.
In Chapter 2, Building Forms with Qt Widgets, we'll get familiar with some of the basic Qt widgets and learn how to resize and arrange them in a user interface. You'll then apply that knowledge by designing a calendar application in both code and Qt Designer.
Try these questions to test your knowledge from this chapter:
- Qt is written in C++, a language that is very different from Python. What are some of the major differences between the two languages? How might these differences come across as we use Qt in Python?
- GUIs are composed of widgets. Open some GUI applications on your computer and try to identify as many widgets as you can.
- The following program crashes. Figure out why, and fix it so that it shows a window:
from PyQt5.QtWidgets import * app = QWidget() app.show() QApplication().exec()
- The QWidget class has a property called statusTip. Which of these are most likely to be the names of the accessor methods for this property?
- getStatusTip() and setStatusTip()
- statusTip() and setStatusTip()
- get_statusTip() and change_statusTip()
- QDate is a class for wrapping a calendar date. In which of the three main Qt modules would you expect to find it?
- QFont is a class that defines a screen font. In which of the three main Qt modules would you expect to find it?
- Can you recreate hello_world.py using Qt Designer? Make sure to set the windowTitle.
Check out these resources for more information on Qt, PyQt, and Qt Designer:
- The PyQt manual at http://pyqt.sourceforge.net/Docs/PyQt5/ is a handy resource for understanding PyQt's distinct aspects
- The Qt module list at https://doc.qt.io/qt-5/qtmodules.html gives a good rundown of the available modules in Qt
- The QApplication documentation at https://doc.qt.io/qt-5/qapplication.html#QApplication lists all the command-line switches parsed by the QApplication object
- The QWidget documentation at https://doc.qt.io/qt-5/qwidget.html shows the properties and methods available in the QWidget object
- The Qt Designer manual at https://doc.qt.io/qt-5/qtdesigner-manual.html will help you explore the full capabilities of Qt Designer
- If you want to understand more about C++, check out these offerings from Packt https://www.packtpub.com/tech/C-plus-plus