Gui 1
Gui 1
Today, we’re going to begin looking at how we can create GUIs (Graphical User
Interfaces) in Python.
So far, every application we’ve built has been either console-based or a web
application. If we want to create a user-friendly standalone application, we really
must create a nice interface for it.
First, we’ll discuss some of the mechanics of GUI development. Afterwards, we could
spend tons of class time learning about PyQt’s ~1000 classes but we won’t. What we’ll
do is build an application together and get familiar with the common parts of a PyQt
application.
As a note, we’ll be using PyQt5 in this lecture. A PyQt4 tutorial is included in the old
lecture but the details aren’t very different.
GETTING PYQT5
1. Go to https://www.riverbankcomputing.com/software/sip/download and grab the
latest version of SIP.
2. Run the following commands to build and install SIP (version numbers may be
different).
$ gunzip sip-4.18.tar.gz
$ tar -xvf sip-4.18.tar
$ cd sip-4.18/
$ python configure.py
$ make
$ sudo make install
GETTING PYQT5
1. Now go to https://www.riverbankcomputing.com/software/pyqt/download5 and grab the
latest version of PyQt5.
2. Run the following commands to build and install PyQt5 (version numbers may be
different).
$ gunzip PyQt5_gpl-5.6.tar.gz
$ tar -xvf PyQt5_gpl-5.6.tar
$ sudo apt-get install qt5-default
$ cd PyQt5_gpl-5.6
$ python configure.py
$ make go get some coffee, this one will take a while!
$ sudo make install
What is different is that GUI programming involves the use of a Toolkit and GUI
developers must follow the pattern of program design specified by the toolkit.
As a developer, you should be familiar with the API and design rules of at least one
toolkit – preferably one that is multiplatform with bindings in many languages!
GUI PROGRAMMING IN PYQT
GUI programming necessarily means object-oriented programming with an event-
driven framework. This should make sense: your job as a GUI developer is to
create an application that responds to events.
For instance, when a user clicks their mouse on a certain button, the program should
do X. When the user presses enter in a text field, the program should do Y. You’re
defining the behavior of the program as a response to external events.
BASIC PYQT
Let’s dive right in by looking at an embarrassingly basic PyQt program.
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv) Calling exec_() on our QApplication
main_window = QtWidgets.QWidget() instance will start our main event loop.
main_window.show()
app.exec_()
BASIC PYQT
Let’s dive right in by looking at an embarrassingly basic PyQt program.
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = QtWidgets.QWidget()
main_window.show()
app.exec_()
BASIC PYQT Rather than the procedural approach
we took before, we should try to
define our interface in an object-
oriented manner.
from PyQt5 import QtWidgets
class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setGeometry(200, 200, 400, 200)
self.setWindowTitle('Triangle Peg Game')
self.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = PegGameWindow()
app.exec_()
QWIDGET
So, we can see that a parentless QWidget instance gives us a window, but the
QWidget class is actually the base class for all UI elements. Some of the classes that
inherit and extend QWidget include:
• QProgressBar
• QPushButton
• QCheckBox
• QScrollBar
• and many, many more!
QWIDGET
The QWidget class also defines some of the basic functionality common to all
widgets.
• QWidget.geometry() and Qwidget.setgeometry(x, y, w, h)
• QWidget.resize(w, h)
• QWidget.setParent(parent)
• QWidget.setToolTip(str), QWidget.setStatusTip(str)
• QWidget.setPalette(palette)
Check here for an exhaustive list. Not only is this not all of the methods defined – we’re not even including
all the different ways you can call these methods!
BASIC PYQT
class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setGeometry(200, 200, 400, 200)
self.setWindowTitle('Triangle Peg Game')
self.setToolTip("Play the triangle peg game!")
self.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = PegGameWindow()
app.exec_()
BASIC PYQT
Let’s make some buttons!
Class StartNewGameBtn(QtWidgets.QPushButton):
def __init__(self, parent):
QtWidgets.QPushButton.__init__(self, parent)
self.setText("Start New Game")
self.move(20,160)
class QuitBtn(QtWidgets.QPushButton):
def __init__(self, parent):
QtWidgets.QPushButton.__init__(self, parent)
self.setText("Quit")
self.move(150,160)
BASIC PYQT
class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setup()
def setup(self):
self.setGeometry(200, 200, 400, 200)
self.setWindowTitle('Triangle Peg Game')
self.setToolTip("Play the triangle peg game!")
self.new_button = StartNewGameBtn(self)
self.quit_button = QuitBtn(self)
self.show()
BASIC PYQT
Alternatively….
class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setup()
def setup(self):
self.setGeometry(200, 200, 400, 200)
self.setWindowTitle('Triangle Peg Game')
self.setToolTip("Play the triangle peg game!")
self.new_button = QtWidgets.QPushButton("Start New Game", self)
self.new_button.move(20, 160)
self.quit_button = QtWidgets.QPushButton("Quit", self)
self.quit_button.move(150, 160)
self.show()
SIGNALS AND SLOTS
PyQt5 makes use of a signal/slot mechanism for specifying the actions that should
be taken when an event happens.
A signal is emitted when a particular event occurs. Widgets already have many
predefined signals, but you could also create custom signals on subclassed widgets.
Some common signals of QPushButton are:
• QPushButton.clicked – signal activated when button is pressed and then
released while mouse is on button.
• QPushButton.pressed – signal activated when button is pressed.
• QPushButton.released – signal activated when button is released.
SIGNALS AND SLOTS
The example buttons we created earlier can be clicked, but nothing will happen.
That’s because we need to assign a slot to the signal.
QWidgets.qApp.quit()
class QuitBtn(QtWidgets.QPushButton):
def __init__(self, parent): Clicking quit now causes
QtWidgets.QPushButton.__init__(self, parent) our window to close!
self.setText("Quit")
self.move(150,160)
self.clicked.connect(QtWidgets.qApp.quit)
self.setToolTip("Close the triangle peg game.")
SIGNALS AND SLOTS
class QuitBtn(QtWidgets.QPushButton):
def __init__(self, parent): Clicking quit now causes
QtWidgets.QPushButton.__init__(self, parent) our window to close!
self.setText("Quit")
self.move(150,160)
self.clicked.connect(QtWidgets.qApp.quit)
self.setToolTip("Close the triangle peg game.")
BASIC PYQT class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
...
def setup(self):
...
def closeEvent(self, event):
We are also free to define the behavior of
reply = QuitMessage().exec_()
our application by overriding built-in
if reply == QtWidgets.QMessageBox.Yes:
methods.
event.accept()
else:
For example, QtWidgets.QWidget has
event.ignore()
a method called closeEvent() which is
called automatically with an instance of a
window close request. By default, we just class QuitMessage(QtWidgets.QMessageBox):
accept the request and close the window. def __init__(self):
Here, we’ll override the function to ask if QtWidgets.QMessageBox.__init__(self)
they’re sure. self.setText("Do you really want to quit?")
self.addButton(self.No)
self.addButton(self.Yes)
BASIC PYQT class PegGameWindow(QtWidgets.QWidget):
def __init__(self):
...
def setup(self):
...
def closeEvent(self, event):
reply = QuitMessage().exec_()
if reply == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
class QuitMessage(QtWidgets.QMessageBox):
def __init__(self):
QtWidgets.QMessageBox.__init__(self)
self.setText("Do you really want to quit?")
self.addButton(self.No)
self.addButton(self.Yes)
QT MAIN WINDOW
The QtWidgets.QMainWindow class provides a main application window.
QMainWindow has its own layout as opposed to QWidget (but it inherits from
QWidget).
QMainWindow automatically makes room for:
• QToolBar
• QDockWidget
• QMenuBar
• QStatusBar
• Any widget can occupy Central Widget.
BASIC PYQT class PegGameWindow(QtWidgets.QMainWindow):
def __init__(self):
...
def setup(self):
...
self.central_widget = QtWidgets.QWidget(self)
We’re going to add a menu bar with self.new_button =
StartNewGameBtn(self.central_widget)
a File > Exit action.
self.quit_button = QuitBtn(self.central_widget)
self.setCentralWidget(self.central_widget)
First, we create a QAction instance for
exiting the application, for which the text exit_action = QtWidgets.QAction('Exit', self)
is “Exit”. exit_action.triggered.connect(QtWidgets.qApp.quit)
self.show()
BASIC PYQT class PegGameWindow(QtWidgets.QMainWindow):
def __init__(self):
...
def setup(self):
...
self.central_widget = QtWidgets.QWidget(self)
We reference the menu bar for our main new_button = StartNewGameBtn(self.central_widget)
window by calling menuBar(). This will quit_button = QuitBtn(self.central_widget)
self.setCentralWidget(self.central_widget)
also create and return an empty menu bar
if there is none. exit_action = QtWidgets.QAction('Exit', self)
exit_action.triggered.connect(QtWidgets.qApp.quit)
Then we add a new menu with the text
“File”. Then we add the Exit action to
the File menu. menu_bar = self.menuBar()
file_menu = menu_bar.addMenu('File')
file_menu.addAction(exit_action)
self.show()
BASIC PYQT
Where’s our menu bar!?
BASIC PYQT
Where’s our menu bar!?
menu_bar = self.menuBar()
menu_bar.setNativeMenuBar(False)
file_menu = menu_bar.addMenu('File')
file_menu.addAction(exit_action)