The four preceding figures are screenshots of the PyQt 5 window, which will refresh itself every second.
For simple examples, designing the GUI in the Python code can be good enough, but for more complex applications, this solution does not scale.
There are some tools to help you design the GUI for Qt, and one of the most commonly used tools was QT Designer. In the first edition of this book, this section was about GUI making with QT Designer. Since the late QT4 development, QT Designer has merged with QT Creator. In the following example, we would learn how to open the secret QT Designer in QT Creator and create a UI file.
Similar to Glade, we can design the user interface part of the application using the on-screen form and drag-and-drop interface. Then we can connect the widgets with the backend code, where we develop the logic of the application.
First of all, let us show you how to open QT Designer in QT Creator. When you open QT Creator, you will see the following interface:
Select Qt in Files and Classes and Qt Designer Form in the middle panel:
There is a range of template selections, such as Widget or Main Window. In our case, we pick Main Window and simply follow through the remaining steps:
And eventually, we will reach the QT Designer interface. Everything you work on here will be put in your desired folder as a UI file:
To quickly demonstrate how to embed a Matplotlib figure in Qt 5 using QT Creator, let's use the former example and combine it with the scripts generated by QT Creator.
First of all, adjust the Geometry of the MainWindow at the lower right panel; change the Width and Height to 300x300:
After that, drag a Widget from Container in the left panel to the MainWindow in the middle. Resize it until it fits well in the MainWindow:
That is it for the basic design! Now save it as a UI file. When you view the UI file, it should display something like this:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>261</width>
<height>221</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
This file is in XML format, and we need to convert it to a Python file. This can be done simply by using this command:
pyuic5 mainwindow.ui > mainwindow.py
And now we will have a Python file like this:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(300, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(20, 10, 261, 221))
self.widget.setObjectName("widget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
Note that this is just the framework for the GUI; we still have to add some things in order to make it work.
We must add init() to initialize the UiMainWindow, as well as link the DynamicCanvas to the widget in the middle of the MainWindow. Here it goes:
#Replace object to QtWidgets.QMainWindow
class Ui_MainWindow(QtWidgets.QMainWindow):
#***Instantiation!
def __init__(self):
# Initialize and display the user interface
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(300, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(20, 10, 261, 221))
self.widget.setObjectName("widget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
#***Putting DynamicCanvas into the widget, and show the window!
dc = DynamicCanvas(self.widget, width=5, height=4, dpi=100)
self.show()
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
We have added only five lines of code in here. We can simply replace the class ApplicationWindow with this, and here is the outcome:
And here is the complete code generating the preceding figure:
#Importing essential libraries
import sys, os, random, matplotlib, matplotlib.cm as cm
from numpy import arange, sin, pi, random, linspace
#Python Qt5 bindings for GUI objects
from PyQt5 import QtCore, QtGui, QtWidgets
# import the Qt5Agg FigureCanvas object, that binds Figure to
# Qt5Agg backend.
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
#The class DynamicCanvas contains all the functions required to draw and update the figure
#It contains a canvas that updates itself every second with newly randomized vecotrs
class DynamicCanvas(FigureCanvas):
#Invoke upon instantiation, here are the arguments parsing along
def __init__(self, parent=None, width=5, height=5, dpi=100):
#Creating a figure with the requested width, height and dpi
fig = Figure(figsize=(width,height), dpi=dpi)
#The axes element, here we indicate we are creating 1x1 grid and putting the subplot in the only cell
#Also we are creating a polar plot, therefore we set projection as 'polar
self.axes = fig.add_subplot(111, projection='polar')
#Here we invoke the function "compute_initial_figure" to create the first figure
self.compute_initial_figure()
#Creating a FigureCanvas object and putting the figure into it
FigureCanvas.__init__(self, fig)
#Setting this figurecanvas parent as None
self.setParent(parent)
#Here we are using the Qtimer function
#As you can imagine, it functions as a timer and will emit a signal every N milliseconds
#N is defined by the function QTimer.start(N), in this case - 1000 milliseconds = 1 second
#For every second, this function will emit a signal and invoke the update_figure() function defined below
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(1000)
#For drawing the first figure
def compute_initial_figure(self):
#Here, we borrow one example shown in the matplotlib gtk3 cookbook
#and show a beautiful bar plot on a circular coordinate system
self.theta = linspace(0.0, 2 * pi, 30, endpoint=False)
self.radii = 10 * random.rand(30)
self.plot_width = pi / 4 * random.rand(30)
self.bars = self.axes.bar(self.theta, self.radii, width=self.plot_width, bottom=0.0)
#Here defines the color of the bar, as well as setting it to be transparent
for r, bar in zip(self.radii, self.bars):
bar.set_facecolor(cm.jet(r / 10.))
bar.set_alpha(0.5)
#Here we generate the figure
self.axes.plot()
#This function will be invoke every second by the timeout signal from QTimer
def update_figure(self):
#Clear figure and get ready for the new plot
self.axes.cla()
#Identical to the code above
self.theta = linspace(0.0, 2 * pi, 30, endpoint=False)
self.radii = 10 * random.rand(30)
self.plot_width = pi / 4 * random.rand(30)
self.bars = self.axes.bar(self.theta, self.radii, width=self.plot_width, bottom=0.0)
#Here defines the color of the bar, as well as setting it to be transparent
for r, bar in zip(self.radii, self.bars):
bar.set_facecolor(cm.jet(r / 10.))
bar.set_alpha(0.5)
#Here we generate the figure
self.axes.plot()
self.draw()
#Created by Qt Creator!
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self):
# Initialize and display the user interface
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(550, 550)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(20, 10, 800, 800))
self.widget.setObjectName("widget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
dc = DynamicCanvas(self.widget, width=5, height=5, dpi=100)
#self.centralwidget.setFocus()
#self.setCentralWidget(self.centralwidget)
self.show()
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
#Creating the GUI application
qApp = QtWidgets.QApplication(sys.argv)
#Instantiating the ApplicationWindow widget
aw = Ui_MainWindow()
#Start the Qt main loop , and sys.exit() ensure clean exit when closing the window
sys.exit(qApp.exec_())