2.5.2 Qt
2.5.2 Qt mrs110Qt is a widely used cross-platform library written in C++, modern, and under very active development. In addition to the GUI functionality, the library provides support for internationalization, Unicode, database and network access, XML and JSON code processing, thread management, and more. That’s why it is also called an application framework, not just a GUI library. Qt was originally developed by the company Trolltech and its initial release was in 1995. KDE, one of the early GUIs for the Linux operating system, was based on Qt and that triggered a lot of discussion and changes to the license and organization Qt was published under. Currently, The Qt Company, a successor of Trolltech, develops and maintains Qt and publishes it in four different editions, including the Community edition that is available under different open source licenses GPL 3.0, LGPL 3.0, and LPGL 2.1 with a special Qt exception. Qt is very commonly used for both open source and commercial software, and if you have worked with Qt in one programming language, it is typically relatively easy to learn to use it in a different language. Qt6 was released in 2020 and the current version of Qt at the time of this writing is 6.8.
2.5.2.1 PyQt vs. PySide
2.5.2.1 PyQt vs. PySide mrs110You may wonder why there are two different Python wrappers for Qt and how different they are?
PyQt and PySide are actually very similar, so similar that the code below for a Qt based version of the miles-to-kilometers converter works with both PyQt and PySide. For PySide you only have to replace the import line at the beginning. The short answer lies mainly in license related issues.
PyQt is significantly older than PySide and, partially due to that, has a larger community and is usually ahead when it comes to adopting new developments. It is mainly developed by Riverbank Computing Limited and distributed under GPL v3 and a commercial license. Releases follow a regular schedule and the software is generally considered very robust, mature, and well supported.
PySide is developed by Nokia and had its initial release in 2009, in a time when Nokia was the owner of Qt. As can be read on the PySide web page, PySide has been developed and published in response to a lack of a Qt wrapper for Python that has a suitable license for FOSS and proprietary software development. Without going too much into the details of the different license models involved, if you want to develop a commercial application, PyQt requires you to pay fees for a commercial license, while the LGPL license of PySide permits application in commercial projects.
From an educational perspective, it doesn’t really matter whether you use PySide or PyQt. As we already indicated, the programming interfaces have over the recent years converged to be very similar, at least for the basic GUI based applications we are going to develop in this course. However, we have some specific reasons to continue with PyQt that will be listed at the end of the next section. If you are interested to learn more about the differences between PyQt and PySide and when to pick which of the two options, the following blog post could serve as a starting point:
2.5.2.2 Installing PyQt6
2.5.2.2 Installing PyQt6 mrs110In contrast to tkinter, PyQt6 is not part of the Python standard library and may or may not be available to install through Pro's Package Manager. We can though, use the Package Manager to clone the default environment and use the Python Command Window to install the packages we need.
Create another clone of the default environment, giving it a name such as arcgispro-py3-pyqt and activate it. You can check the "Installed Packages" list to see if "pyqt" is installed at this time, but more likely it will not be installed. If not, go to Add Packages and type "pyqt". It may not be listed as a package to add in this curated list by esri and Pro's version of conda, so we will need to use the Python Command prompt to install it. There are two quick ways to start a command window in your py3-pyqt conda environment:
In your Windows start menu:
![]()
search for "Python Command Prompt" and it should result in a "Best match". After opening, be sure to verify that it opened the environment you want to work in (details below).

Or, you can navigate to it by clicking the "All" to switch to the application list view.

Scroll down the list and expand the ArcGIS folder to list all ArcGIS applications installed.

Scroll down and open the Python Command Prompt.

This is a shortcut to open a command window in the activated python environment. Once opened, you should see the environment name in parentheses followed by the full path to the python environment.

When you change your activated environment in Pro's Package Manager, this shortcut will also be updated to point to that activated environment.
Type 'pip install PyQt6 PyQt6-WebEngine PyQt6-tools' and hit enter.

Proceed through the prompts and if this does not install, please let your instructor know. When it completes, you probably now have version 6.8.0 or later of pyqt installed. Next, try to run the test code on the next page. Let your instructor know if you are not able to run the application.
2.5.2.3 Miles to kilometers with PyQt
2.5.2.3 Miles to kilometers with PyQt mrs110Here is how the code for our miles-to-kilometers conversion tool looks when using PyQt6 instead of tkinter. You will see that there are some differences, but also looks very similar. We kept the names of the variables the same even though the widgets are named a little differently now. Since you now have PyQt6 installed, you can immediately run the code yourself and check out the resulting GUI. The result should look like the figure below.

Source code:
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QGridLayout, QLineEdit, QPushButton
def convert():
"""Takes miles entered, converts them to km, and displays the result"""
miles = float(entryMiles.text())
entryKm.setText(str(miles * 1.60934))
app = QApplication([])
rootWindow = QWidget()
rootWindow.setWindowTitle("Miles to kilometers")
rootWindow.resize(500, 200)
gridLayout = QGridLayout(rootWindow)
labelMiles = QLabel('Distance in miles:')
gridLayout.addWidget(labelMiles, 0, 0)
labelKm = QLabel('Distance in kilometers:')
gridLayout.addWidget(labelKm, 2, 0)
entryMiles = QLineEdit()
gridLayout.addWidget(entryMiles, 0, 1)
entryKm = QLineEdit()
gridLayout.addWidget(entryKm, 2, 1)
convertButton = QPushButton('Convert')
gridLayout.addWidget(convertButton, 1, 1)
convertButton.clicked.connect(convert)
rootWindow.show()
app.exec()Let’s look at the main differences between this code and the tkinter based code from Section 2.5.1.
We are now importing classes from the module PyQt6.QtWidgets and the widgets are named differently (all starting with ‘Q’).
In tkinter, we only created one object for the application and root window together and then called its mainloop() method to start the execution of the event processing loop. In contract, the application and its main window are two different things in Qt. In line 8, we create the application object and then at the very end we call its exec() method to start the event processing loop. The window is created separately in line 10, and before we call exec(), we invoke its show() method to make sure it is visible on the screen.
The creation of the widgets looks very similar in both versions. However, with tkinter, we didn’t have to create a grid layout explicitly; it was already available after the main window had been created. With PyQt6, we create the grid layout for the root window explicitly in line 14. To add widgets to the grid layout, we call the addWidget(…) method of the layout providing numbers for the row and column as parameters.
In the tkinter version, we had to set up a special variable to change the content of the entryKm line input field. This is not required with PyQt6. We can simply change the text displayed by the corresponding QLineEdit widget by calling its setText(…) method from the convert() function in line 6.
Finally, connecting the “clicked” event of the button with our convert() event handler function happens as a separate command in line 31 rather than via a parameter when creating the button object. By writing "convertButton.clicked.connect(convert)" we are saying, in Qt terminology, that the “clicked” signal of convertButton should be connected to our convert() slot (function).
From a coding perspective, the differences between tkinter and PyQt6 are relatively minor. Sometimes one requires slightly more code than the other, but this is largely due to the simplicity of the example used here, which doesn’t involve complex widgets or layouts.
If you tried both versions of our tool or closely compared the screenshots, you may have also noticed some differences in the layout and behavior of the GUIs. While we didn’t optimize the designs to look identical, it’s possible to do so. That said, based on default settings, PyQt6 tends to produce a more visually appealing layout. The main reasons we’ll continue using Qt6/PyQt6 for the rest of this lesson are:
- Qt6 is a modern and widely used cross-platform and cross-language library; knowledge and skills acquired with Qt can be applied in languages other than Python.
- Qt6 is efficient and smooth because of the compiled core library written in C++.
- Qt6 and PyQt6 provide a large collection of available widgets and can be expected to be under active development for the foreseeable future.
- There exists very good tool support for the combination of Qt6 and PyQt6
- Finally and very importantly: In lesson 4 we will continue with the GUI development started in this lesson in the context of QGIS 3.x. QGIS and its interface for plugins have been developed for PyQt6.
As a final note, if you want to run the converter tool code with PySide, it is very easy to do so. You will first have to install the PySide2 package in the Python Command Window and replace the import line with the following line:
from PySide2.QtWidgets import QApplication, QWidget, QLabel, QGridLayout, QLineEdit, QPushButtonYou will first have to install the PySide2 package in the ArcGIS Pro package manager to be able to run the code.