In this post we are going to create a small PyQt calculator application. Creating a calculator is always fun. So let’s start.
First of all I have used QT Designer to create the GUI that we need. Here below is the elements included in the GUI:
As you can see we are using a gridLayout and adding 5 X 4 matrix of buttons for the very basic operations.
The button of Zero is called “p0”, the button 1 is called “p1” and so on.
The only control that is not a button is the “screen” that is in fact is a QPlainTextEdit called…..screen.
It is important to notice that the window is called calc.
Let’s save the file a mycalc.ui.
Now that we have a QT GUI file (mycalc.ui) we need to run from terminal the command to translate to Python code:
pyuic5 -x mycalc.ui -o mycalc.py
After running this command you will have a new file “mycalc.py”
If you open the file in the terminal you will see that it contains the main components in Python as show below :
The object name is “calc”. Buttons are listed together with the other control.
The file mycalc.py however does not do much : if you run it you will see the GUI application appearing on your screen however the application will not respond to the user inputs.If you press a button nothing will happen.
In order to have a real application responding to the user inputs we need to write another Python file. Of course the Python file is “interfaced” with the mycalc.py file. In other words one has all the controls in our GUI (mycalc.py) while the second one will determine what happens when one of these controls is activated (for example by clicking a button)
So let’s start coding a brand new open file.
We can start with a new class “MainWindow”.
class MainWindow(QMainWindow): def __init__(self,va1): super(MainWindow, self).__init__() self.ui = Ui_calc() self.ui.setupUi(self) self.va1=va1
Apart from the va1 addition the rest of the line above are start for a MainWindow Application .
The class MainWindow will retrieve the elements of the UI_calc() class.
Other important part of the template is the bottom part which will launch the application:
app = QApplication(sys.argv) w = MainWindow("") w.show() sys.exit(app.exec_())
Now that the template is set up we can actually start coding our application.
The first thing that we want to achieve is to connect each button with an event that will call in turn a function to show something (a number) in the screen: in other words if the user press the button p0 we want to display zero on the screen and therefore we will need to create a function to do so.
One way of doing this is by coding each button and associate buttons to an event (clicked) and connect it with a function specific function as shown below:
One alternative approach is to use a for loop cycle to cycle through each button, associate it with an event clicked and connect it with the function.
In order to loop in the Main Window class we need to call a button that is part of the Ui_calc class.
Therefore we need to open the mycalc.py and add two list of buttons as shown below (one list for buttons 0-9 , the other one for all other buttons (+,-*,/, etc)
The class Ui_calc has already each of these buttons as element of the class. The only thing we are doing is adding a variable called btn (type list) which included all the button from 0 to 9.
The same thing happens for the button sum,deduct, multiply, backwards,equal, point and cancel which are included a list called o.
So now because these buttons are part of the Ui_calc.py we can use them in our Python file to implement the loop
// This is a list of functions to display number 0-9 self.inputNumber=[self.zero,self.one,self.two,self.three,self.four,self.five,self.six, self.seven,self.eight,self.nine] // This is another list of functions to do math operations self.other[self.sum,self.deduct,self.multiply,self.divide, self.correctme,self.equal,self.addpoint,self.cancelme] //First loop for button 0-9 for x in range(0,10): self.ui.btn[x].clicked.connect(self.inputNumber[x]) self.ui.btn[x].setStyleSheet(open('/home/piemex/Documents/Python/GUI/Calc Project/mystylesheet.css').read()) //Second loop for buttons for math operation for x in range(0,8): self.ui.o[x].clicked.connect(self.other[x]) self.ui.o[x].setStyleSheet(open('/home/piemex/Documents/Python/GUI/Calc Project/mystylesheet.css').read()) //Set up a on/off button to start/shut down application self.ui.ponoff.clicked.connect(lambda:self.close()) self.ui.ponoff.setStyleSheet(open('/home/piemex/Documents/Python/GUI/Calc Project/mystylesheet.css').read()) //Set Up Screen self.ui.screen.setStyleSheet(open('/home/piemex/Documents/Python/GUI/Calc Project/mystylesheet.css').read())
In the code above we define a list (inputNumber) that is a collection of functions that we will use when buttons 0-9 are pressed. Each element of the list will activate its own function. So function zero will output zero on the calculator screen.
If the user press one button between 0-9 a corresponding button will be associate from the btn list of buttons that we have defined before.
Here is a comparison of declaring specfically each single button and looping through the list of buttons
inputNumber[x] is a list of functions : so if a press 4 the number 4 will be passed to the list inputNumber and retrive the funcion associated with that key
With the loop we are much more efficient because all we need is to put a number to the btn list rather than writing each single line.
We repeat the same operation for all buttons associated to a math operation (+,-,*,/ etc) in the second loop.
Let’s check the result once we run the application
Now that we have customize GUI with buttons that call an event we can describe better what happens if we press one button (for example five): in this file the function . So for example let’s say the user wants to multiply 125 * 2. He has already pressed 1 and then 2 and now press the button 5 or key the number five on the keppad. The corresponding function (five) will be called
def five(self): self.findme() self.va1= self.va1 + "5" self.screenme(self.va1)
First the function findme will be called.
This function will look at the string in the screen and store it in the variable x. The the string will be searched for an equal sign. If this string includes the sign “= ” the variable y will be greater than -1. That means that an operation has already done previously and that we need to clear the screen and set out variable va1 equal to empty string.
def findme(self): x=self.ui.screen.toPlainText(); y=x.find("=") if y>-1: self.va1="" self.ui.screen.clear()
Let’s go back to our five function and suppose that we have do not have any equal sign in the string shown in the screen (so we have not yet told the calculator the whole operation) : in that case the current output shown in the screen will be contacted with the char 5. So we we had pressed 1 and 2 and then we press five the screen will show 125.
Now that we have 125 shown in our screen we want to multiply for 2 for example. So first we press the button multiply which will call the function multiply
def multiply(self): self.va1= self.va1 + "*" self.screenme(self.va1)
The logic of each operator function (+,-,* and /) is a bit the same. The current string shown in the screen (va1) will be contacted with the operator sign and the output shown in the screen with the function screenme.
Now what happens when the equal operator is pressed?
Let’s check it out.
def equal(self): result = 0 result = eval(self.va1) self.ui.screen.moveCursor(QTextCursor.End) self.ui.screen.insertPlainText("=" + str(result)) self.ui.screen.moveCursor(QTextCursor.End)
We set a variable result equal to zero. We use the eval function to evaluate the string 125* 2. This will return 250.
We move the cursor of the screen to the end and add the equal sign and then we convert the result to string. Finally we move the cursors to the end of the string again.
So now the screen will show 125*2 = 25
I will not describe each single function as at the bottom of the page you will find the link of the whole program.
One interesting aspect is that we want the calculator to work whether the user press with the mouse the button or key the number in the number keypad of the keyboard.
To do so we need to implement a function that listen to key being pressed on the keyboard
def keyPressEvent(self,e): if e.key()==QtCore.Qt.Key_0: self.zero();
In the above example the code of what happens when the user press zero. As you can see the function zero will be called.
Hope you have enjoyed this little tutorial on Python and QT.
Here is the full program available