This book has tried to take a practical approach to creating GUIs. As you use PyQt5 and Python more and more, you will find yourself learning about other modules and classes that you will need in your applications. Each chapter set up an idea and worked hard to break those projects down into their fundamental parts so that you could learn new ideas along the way.
PyQt5 has quite a few modules for a variety of purposes, and the chapters in this book only scratched the surface of the many possibilities for designing GUIs.
In Chapter 12, we will take a look at a few extra examples to give you ideas for other projects or to help you in creating new types of user interfaces. These projects will not go into great lengths of detail, but rather focus on explaining the key points of each new program and leave it up to you to research the details that you are unsure about, either by finding the answers in a different chapter or by searching online for help.
Displaying directories and files using the QFileSystemModel class
Working with multiple-document interface (MDI) applications and the QCamera class
Creating a simple clock GUI with QDate and QTime
Exploring the QCalendarWidget class
Building Hangman with QPainter and other PyQt classes
Building the framework for a web browser using the QtWebEngineWidgets module
Project 12.1 – Directory Viewer GUI
For every operating system, there needs to be some method for a user to access the data and files located in it. These files are stored in a hierarchical file system, displaying drives, directories, and files in groups so that you only view the files that you are interested in seeing.
Whether you use a command-line interface or a graphical user interface, there needs to be some way to create, remove, and rename files and directories. However, if you are already interacting with one interface, it may be more convenient to locate files or directories that you need in your current application rather than opening new windows or other programs.
Code for directory viewer GUI
Explanation
Begin by importing the necessary modules for this GUI. For this project, we will need to use the model/view paradigm to view the data on your computer. For more information about model/view programming, refer to Chapter 10.
The QFileSystemModel class provides the model we need to access data on the local file system. While not included in this project, you could also use QFileSystemModel to rename or remove files and directories, create new directories, or use it with other display widgets as part of a browser.
The QTreeView class will be used to display the contents of the model in a hierarchical tree view.
For this GUI, we will create a Directories menu with actions that will either let the user view a specific directory or return back to the root directory. The menu system can be seen in Figure 12-2.
Project 12.2 – Camera GUI
When creating GUIs, there are a number of ways to tackle the issue of interfaces with multiple windows. You could use stacked or tabbed widgets, but these methods only allow for one window to be displayed at a time. Another option is to use dock widgets and allow windows to be floatable or used as secondary windows.
For this project, you will see how to set up a multiple-windowed GUI using the QMdiArea class. QMdiArea provides the area for displaying MDI windows. Multiple-document interface (MDI) is a type of interface that allows users to work with multiple windows at the same time. MDI applications require less memory resources and make the process of laying out subwindows much simpler.
Example code to show how to create MDI applications
Explanation
For this project, we are going to import some new classes. From the QtWidgets module, the QMdiArea and QMdiSubWindow classes are used to create the MDI windows.
The QtMultimedia module provides access to a number of multimedia tools including audio, video, and camera capabilities. The QCamera class provides the interface to work with camera devices. QCameraInfo supplies information about available cameras. QCameraImageCapture is used for recording media.
From the QtMultimediaWidgets module, the QCameraViewfinder class sets up the camera viewfinder widget. In photography, the viewfinder is used for focusing and viewing the subject being photographed.
This application contains two subwindows, one for displaying the viewfinder and the other for listing the available cameras that you can choose from in a QListWidget object. In the setupWindows() method , the labels, list widget, and push button are arranged inside of a QGroupBox widget. The user can select a camera from the list. The Select Camera button emits a signal that is connected to the selectCamera() slot. Next, the QMdiArea object, mdi_area, that is used as a container for the subwindows is created. This will be the central widget for the main window.
A menubar could also be added to the main window that controls the subwindows. For example, subwindows could be set as checkable in order to close or reopen them. Or a menu item could allow the user to switch between tiled or cascaded windows.
If the user clicks the push button and an available camera is selected, then the setupCamera() method is called. Refer to the comments in the code to learn how to set up the viewfinder. This method is adapted from the Qt document web site.1
Using QCameraImageCapture(), the user is also able to take pictures of the viewfinder. Image capturing is handled by the keyPressEvent(). When the spacebar is pressed, a picture is taken and saved to the "images/" folder. The folder will be created if it does not already exist.
Project 12.3 – Simple Clock GUI
PyQt5 also provides classes for dealing with dates, QDate, or time, QTime. The QDateTime class supplies functions for working with both dates and time. All three of these classes include methods for handling time-related features.
There are also other formats, including shorter formats, ISO 8601 format, or UTC format. The toString() method returns the date and time as a string. QDateTime also handles daylight saving time, different time zones, and the manipulation of times and dates such as adding or subtracting months, days, or hours.
Code for the clock GUI
Explanation
Start by importing the necessary modules, including QDate, QTime, and QTimer, from the QtCore module. The QTimer class will be used to create a timer object to keep track of the time that has passed and update the labels that hold the date and time accordingly. The timer is set up in initializeUI(), and its timeout() signal is connected to the updateDateTime() slot. The timeout() signal is emitted every second.
The labels that will display the date and time are then instantiated, styled, and added to the layout. The values of the labels are updated using the updateDateTime() method.
Project 12.4 – Calendar GUI
This project takes a look at how to set up the QCalendarWidget class and use a few of its functions. PyQt makes adding a monthly calendar to your applications rather effortless. The code is provided in Listing 12-4 and the calendar can be seen in Figure 12-5.
The QCalendarWidget class provides a calendar that already has a number of other useful widgets and functions built-in. For example, the calendar already includes a horizontal header that includes widgets for changing the month and the year and a vertical header that displays the week number. The class also includes signals that are emitted whenever the dates, months, and years on the calendar are changed.
The calendar GUI code
Explanation
After importing the modules needed for the calendar GUI, the styles for the QLabel and QGroupBox widgets are prepared using style_sheet.
Whenever a date is selected in the calendar widget, it emits a selectionChanged() signal . This signal is connected to the newDateSelection() slot that updates the date on the current_label and in the current_date_edit widget . Selecting a value in the current_date_edit widget will also change the other values.
The QCalendarWidget class also has a number of functions that allow you to configure its behaviors and appearance. For this project, we create three QDateEdit widgets that will allow the user to change the minimum and maximum values for the date range, as well as the current date selected in the calendar.
When a date is changed in a date edit widget, it generates a dateChanged() signal . Each one of the QDateEdit widgets is connected to a corresponding slot that will update the calendar’s minimum, maximum, or current date values depending upon which date edit widget is changed. The method for changing the dates is adapted from the Qt document web site.2
Finally, the label and date edit widgets are arranged in a QGroupBox.
Project 12.5 – Hangman GUI
PyQt can be used to create a variety of different kinds of applications. Throughout this book, we have looked at quite a few ideas for building GUIs. For this next project, we will be taking a look at how to use QPainter and a few other classes to build a game – Hangman. While Hangman is a simple game to play, it can be used to teach a few of the fundamental concepts for using PyQt to create games. The code is presented in Listing 12-5 and the interface can be seen in Figure 12-6.
For this application, the player can select from one of the twenty-six English letters to guess a letter in an unknown word. As each letter is chosen, they will become disabled in the window. If the letter is correct, it will be revealed to the player. Otherwise, a part of the hangman figure’s body is drawn on the screen. If all of the letters are correctly guessed, then the player wins. There are a total of six turns.
This is the code for the Hangman GUI
Explanation
A variety of classes are used in the Hangman GUI, including different widgets from QtWidgets, as well as classes used for drawing from QtCore and QtGui. We then create a style sheet to set the style properties of the widgets and to handle the situation of how the buttons look when they are pressed.
This program contains two classes, DrawingLabel and Hangman.
Creating the Drawing Class
The DrawingLabel class inherits from QLabel and handles the different paint events that will be drawn on the label object in the main window. The paintEvent() function is called in a class that inherits from QLabel so that way the paint events occur on the label and are not covered up by the main window.
The paintEvent() function sets up QPainter and handles the two painting methods, drawHangmanBackground(), which draws the gallows of the Hangman game onto the label, and drawHangmanBody(), which only draws the body parts if they are contained in the part_list.
Creating the Main Window Class
The Hangman class starts by initializing the GUI window and calling the newGame() method . First, the Hangman board is created as an instance of the DrawingLabel class. Then, setupBoard() selects a random word from the words.txt file. The labels that will represent the letters of the chosen word are replaced with underscore characters, appended to the labels list, and added to the horizontal layout of the word_frame object.
Finally, we need to set up the keyboard push buttons, layouts, and the game logic in setupBoard(). Three rows of push buttons that represent the letters of the alphabet are controlled by one QButtonGroup object, keyboard_bg. When one button is pushed, it generates a signal that calls the buttonPushed slot.
The list of body parts contains the six body part names. If the player guesses an incorrect letter, the name is appended to the part_list and checked for in the drawHangmanBody() function. Using this method ensures that all necessary parts are drawn with their different styles when paintEvent() is called. Otherwise, the labels are updated to display the correct letters in the appropriate positions if the player guesses correctly.
If the player wins or loses, a QMessageBox will appear and allow the user to close the application or continue. If Yes is selected, newGame() is called.
Project 12.6 – Web Browser GUI
A web browser is a graphical user interface that allows access to information on the World Wide Web (Listing 12-6). A user can enter a Uniform Resource Locator (URL) into an address bar and request content for a web site from a web server to be displayed on their local device, including text, image, and video data. URLs are generally prefixed with http, a protocol used for fetching and transmitting requested web pages, or https, for encrypted communication between browsers and web sites.
Qt provides quite a few classes for network communication, WebSockets, support for accessing the World Wide Web, and more. This project introduces PyQt’s classes for web integration into GUIs.
For the following project, we will take a look at QtWebEngine, specifically the QtWebEngineWidgets module for creating widget-based web applications. QtWebEngine provides a web browser engine that can be used to embed web content into your applications. The QtWebEngine module uses Chromium as its back end. Chromium is open source software from Google that can be used to create web browsers.
Ability to open multiple windows and tabs, either by using the application’s menu or shortcut hot keys
A navigation bar that is made up of back, forward, refresh, stop, and home buttons and the address bar for entering URLs
The web engine view widget created using QWebEngineView
A status bar
A progress bar that relays feedback to the user about loading web pages
When you are running this program, if you get an error message stating “No module named: PyQt5.QtWebEngineWidgets”, then you need to install the QtWebEngineWidgets module. To solve this problem, enter the following command into the command line: pip3 install PyQtWebEngine.
Web browser GUI code
Explanation
Two new classes are introduced in this application – QUrl is used for managing and constructing URLs, and QWebEngineView is used for creating the main component for rendering content from the Web, the web engine view (denoted as web_view in the code).
Before calling initializeUI() , we need to instantiate a few lists that will contain the new windows, web pages viewed, and URLs for each tab. This project also calls setWindowIcon() to include an application icon, but it will not be displayed on MacOS due to system guidelines.
There are three main methods that are called in initializeUI(). The first one is createMenu() for setting up the main menu and the status bar. The menu includes actions and shortcuts for creating new windows, new tabs, and closing the application.
The current index of the tab we are viewing is stored in tab_index. The back() method is then called on the web_view object for that current tab. If the tab_index is not 0, then the user can navigate back through previously viewed web pages. The back() method is but one of several functions included in the QWebEngineView class. Other methods for navigation include forward(), reload(), and stop(), and these are also utilized for the other tool_bar buttons.
When the user enters a web address in the QLineEdit widget and presses the return key, we check to see if the URL begins with the correct scheme (such as http, https, or file). If a valid scheme is not present, http is appended to the beginning of the URL. If the URL conforms to standard encoding rules, a request is then sent to load() the web site.
Creating Tabs for the Web Browser
The third method, createTabs(), is used to handle creating the tab widget and the web view objects. First, we need to create the QTabWidget that will display each individual tab’s web view. Refer back to Chapter 6 for more details on setting up tab widgets.
A few of the tab_bar widget’s parameters are changed so that each tab includes a close button, and if only one tab remains, then the tab bar will not be displayed. This helps to make sure that there is always at least one tab in the main window. If a tab is closed, the closeTab() slot is called. The corresponding URLs and web view items for that tab are also removed from the list_of_urls and list_of_web_pages lists.
The first tab, main_tab, is created, added to the tab_bar, and then passed to the setupTab() method . The tab_bar widget is set as the central widget for the main window. When setting up a tab’s page, we first need to create a web view object.
Creating the Web View
Once the web page has loaded, the urlChanged() signal connected to updateUrl() changes the URL displayed in address_line. We can use the loadFinished() signal to tell the current tab to update its title using the updateTabTitle() slot and return the web_view widget.
Next, create the layout to hold the web view widget, append the current tab’s URL and web_page object to the list_of_urls and list_of_web_pages lists, and set the layout for the current tab’s page. The web_page object is the web_view widget that is returned from setupWebView() and displayed in the page of the tab.
Finally, to handle when a user switches between tabs, QTabWidget has the currentChanged() signal . If a different tab is selected, the connected slot, updateUrl(), will change the displayed URL in address_line.
Adding a QProgressBar to the Status Bar
When a page is finished loading, we call removeWidget() to remove the progress bar and the label. An example of the progress bar can be seen at the bottom of Figure 12-7.
Creating a web browser is a very extensive task. There are many topics that are not included in this project, such as accessing HTTP cookies with QtWebEngineCore, working with the browser history with QWebEngineHistory, managing connections and client certificates, proxy support with QNetworkProxy, working with JavaScript, downloading content from web sites, and others. You are definitely encouraged to research these topics if you need to use QtWebEngine for more advanced projects.
Summary
In this chapter, you saw different GUI applications that build the structure for larger projects, such as the camera GUI or the web browser GUI. Other projects introduced components that you may be able to include in other programs, such as the directory viewer GUI, the clock GUI, or the calendar GUI. In the case of the Hangman GUI, a complete program was created so that it can hopefully give you ideas for other programs you may want to design.
We have explored a variety of topics for designing graphical user interfaces using PyQt5 and Python throughout this book – different types of widgets, classes, and layouts. We saw how to stylize your interfaces, how to add menus, and how to make creating an application simpler with Qt Designer.
We covered a few advanced topics such as working with the clipboard, SQL, and multithreaded applications, as well.
Appendix A will fill in more details about the PyQt5 classes used in this book, as well as a few other classes that there was no room to include in the previous chapters.
Appendix B is used to refresh your knowledge on the key Python concepts used in this book.
Your feedback and questions are always welcome. Thank you so much for traveling along with me on this journey to create this guide for you.