Running a script via a GUI in Python, part 1
Andrew Cutts
Consultant | Freelancer in Earth Observation | Geospatial | Python | GIS | Data Analytics
I wanted to put a brief guide together to show how simple it can be to run a script in Python via a Graphical User Interface (GUI). I have thought about this idea for a while; I've wondered whether non-coders see coding as building 'windows' type graphics. Other than writing some simple scripts for QGIS plugins in 2018, I don't think I built a single GUI for any Python script I wrote. I mainly write scripts.
Some thoughts on why generally it is not worth building a GUI
- The beauty of a Python script to do a quick task is that you can call it at the command line, perhaps parse in arguments and off you go.
- If you get any errors you can open up the code, change it and run it again.
- It is pretty simple to share a piece of code, for a simple script is just one file.
- A script should be easier to run in the 'cloud'
- If only one person is going to run the code, is the extra time taken to build a GUI worth it?
- GUIs can quickly get super-complicated and over-confuse the user. Have a look at the cartoon on this page hopefully you get the idea.
Some thoughts on why it might be a good idea to build a GUI
- A GUI tends to look more like a 'windows' type program (I use windows in the sense of windows of different apps rather than Windows OS)
- Users might prefer to click a button rather than type commands.
- It might encourage you to build a logger (more in part 2).
- It's more potentially a more fun way to present code.
A simple guide to building a GUI in Python
- using a WindowsOS and a OSGeo4W based QGIS 3.4 (LTR) install
If you have installed QGIS via OSGeo4W you may (probably) have installed Qt Designer. If so, great. This is what I will be using. If not, go ahead and download a copy of PyQT5 from here: https://riverbankcomputing.com/software/pyqt/download5
I am going to assume that you have Qt Designer at this point. There are other ways to build GUIs in Python - check out this link if you'd like to explore more options.
From the pop up 'New Form window', select 'Dialog without Buttons' and click on create.
Your screen should look similar to below, with some tools, a Widgets Bar on the left and Object Inspector, Property Editor and Resource Browser on the right.
We are just going to create one button, which will, when clicked, call a simple print script which will print an output to the command prompt. There is no real reason for doing this, it is only to show how it works. You could replace this with a 'Geospatial script' that did some geoprocessing of some type.
From the Widget Box, click and drag the Push Button to the Dialog box you are creating.
Once there you can resize it, if needed. Double click on the button and rename it to 'Click me'.
The last thing I am going to do is change the name of the button. Currently it is called pushButton. In the Property Editor | Object Name I am going to change it to btnRun. This property is shown in blue below:
That's it - we are done for this very simple example. You can obviously add more controls and adjust properties as needed, but for this simple example this is enough to get basic principals. Save the file. In my example I have called the file blog_gui.ui.
If you open this ui (user interface) file in a text editor it should look similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QPushButton" name="btnRun">
<property name="geometry">
<rect>
<x>150</x>
<y>100</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Click me</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
So basically, it's just an xml file. The next step is to convert this file to a Python .py file. We do this using the PyQt5 library. In Anaconda you can install it using this script:
conda install -c dsdale24 pyqt5
You could also install pip if you are not using Anaconda. Use this command:
pip3 install PyQt5
or read more about it here.
With PyQt5 installed we can run the following command on our .ui file:
python -m PyQt5.uic.pyuic -x [FILENAME].ui -o [FILENAME].py
In my case it will look like this:
python -m PyQt5.uic.pyuic -x blog_gui.ui -o blog_script.py
I now have a Python file called blog_script.py (not a great name, I know). If you run this script with the call...
python blog_script.py
...you should get a Python GUI!
Sadly, though you can click on this button as many times as you like, nothing will happen. Let's get the button to print to the command prompt on user click.
Open the blog_script.py in a text editor.
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'blog_gui.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.btnRun = QtWidgets.QPushButton(Dialog)
self.btnRun.setGeometry(QtCore.QRect(150, 100, 75, 23))
self.btnRun.setObjectName("btnRun")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btnRun.setText(_translate("Dialog", "Click me"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
That is all the code. We just need to add a button click event and something to happen on that click. Firstly, create a new function under the class. In that function we will just print 'Clicked'. This function will be called print_something and it takes self as an argument.
def print_something(self):
print ("Clicked")
Now we need to add a line to call this method once the button is clicked. Add this line of code beneath the line 'self.btn.setObjectName("btnrun")
self.btnRun.setObjectName("btnRun") self.btnRun.clicked.connect(self.print_something) ### this is the new line
The whole script will look like this:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'blog_gui.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def print_something(self):
print ("Clicked")
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.btnRun = QtWidgets.QPushButton(Dialog)
self.btnRun.setGeometry(QtCore.QRect(150, 100, 75, 23))
self.btnRun.setObjectName("btnRun")
self.btnRun.clicked.connect(self.print_something)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btnRun.setText(_translate("Dialog", "Click me"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
Save and run again and click on the button. The command prompt should return 'Clicked'.
Obviously this is a super basic example. Hopefully though' it will give you an idea of what is possible.
Finally, let's call a standalone function called test that I have written. Here it is below:
def test():
x = 0
print ("another go")
print ('script running')
while x < 10:
print ('calculating')
x += 1
I have saved this as some_code.py in the same location as my blog_script.py file. Now, in the blog_script.py file I will import this file and instead of printing 'Clicked' in the print_something function I will call the test function in the some_code.py script. The code looks like this (I have commented where the changes are made):
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'blog_gui.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
import some_code ### added!
class Ui_Dialog(object):
def print_something(self):
#print ("Clicked")
some_code.test() ##### added!
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.btnRun = QtWidgets.QPushButton(Dialog)
self.btnRun.setGeometry(QtCore.QRect(150, 100, 75, 23))
self.btnRun.setObjectName("btnRun")
self.btnRun.clicked.connect(self.print_something)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btnRun.setText(_translate("Dialog", "Click me"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
Now when I run this blog_script.py file and click the button, this is what is printed to the command prompt:
Hopefully this makes some sense. If you rebuild your .ui file then you will need to remember to re-add the code described above. There is heaps more functionality in Qt Designer. Have a look and explore what you can do.
Recap
This was quite a heavy post so there is a fair amount to recap on. In summary:
- We looked at the reasons for and against building a GUI
- We built a simple GUI with one button in Qt designer and saved it
- We converted this .ui to a Python script using PyQt5
- We adapted this script to with a function called do_something to print to the command line when clicked, which we later adapted to...
- Call a standalone script that called a function to print to the command line
I really hope this has been useful. It was great for me to refresh on this topic after a long time without building GUIs. In part 2 I'll take a look at loggers. Programming is for everyone.
---------------------------------------------------------------------------------------------------------------I am a freelancer able to help you with your projects. I offer consultancy, training and writing. I’d be delighted to hear from you. Please check out the books I have written on QGIS 3.4
I have grouped all my previous blogs (technical stuff / tutorials / opinions / ideas) at https://gis.acgeospatial.co.uk.
Feel free to connect or follow me; I am always keen to talk about Earth Observation.
Image credit https://unsplash.com/photos/ylu3bpKHEXE
Research Scientist at IBM
5 年Thanks Andrew
senior specialist flood protection and geotechnics | developer | trainer | innovator
5 年Funny, just completed my data importer using Qt and QGis :-) Great tools but debugging can be a pain in the ... :-)
Technical Director - Data & AI
5 年Thanks for this - really nice to have a thorough tutorial to work though. I'm using this idea to enable non-python users to set/play with different input values and run code to see how the output varies, and it's a nice way of doing so without showing them any code.? Just for anyone else with difficulties: I had various DLL errors when trying to run `python -m PyQt5.uic.pyuic -x blog_gui.ui -o blog_script.py` but was remedied by installing pyqt from `conda install -c anaconda pyqt` instead which is a later version as far as I can tell. Thanks again!