Python + Pyside2 or simply “Calculator”

Hello, Habr!

My name is Sasha. I am a junior developer. I work as a software tester. I mainly write tests using Python + Selenium, but Python became so interesting that I wanted to delve into it and learn as many frameworks as possible! I wanted to write a desktop application, ala simple “Calculator”. My choice fell on Pyside2. I do not pretend to have the perfect code or lesson. There is simply a desire to share experience if someone, like me, wants to start fumbling in Python. If I help someone, I have achieved the result.

Let's get started!

I wrote my code in the PyBarm IDE from JetBrains. OS - Windows.

Install PySide2:

pip install PySide2

Where you have the Python root folder, go to it, then to the “Lib” -> “site-packages” -> “Pyside2” folder. You will have the designer program - this is the QtDesigner program, which will allow you to make your own interface for your program. It is worth noting that when you create your file in the directory of your project, it will have the .ui format , which Python will not understand, so we will need to convert it to the .py format , but this is later.

First, create your form.

image

We make our design, call the buttons on the right in the “Object Inspector” subsection. It is worth saying that QtDesigner supports a cascading style sheet, and if it’s easier, then by finding the “styleSheet” parameter in the properties, you have the opportunity to make your own design based on CSS knowledge.

image

Next, we need to convert our .ui file to a format so that Python understands it. Go to the command line and write

pyside2-uic "you_file.ui" -o "your_file.py"

What does the pyside2-uic command do? It converts your .ui file into a python .py file and creates a Python class from it. Perhaps knowledgeable people will say that a ui file can be connected without conversion to a project, but I will be neat and do better, as it is written in the Pyside2 manuals.

Next, go to the code.

Open PyCharm, our project directory, and create a file called calc_ui.py or something_ui.py, depending on what program you are doing. The _ui prefix at the end of the file will help us not to get lost in the files. In general terms, it should look like this:

image

Let's start by editing the file that we converted from .ui to .py .

Below we look for this code and copy it, then delete it from this file.

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtGui.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

We create our main file, where the program logic will be written. I called it, like, not surprisingly “calc.py” (above on the screen of the directory it is visible).

Paste our copied code there and start editing it.

import sys we will throw in the beginning as it is a rule of good form.

We import a couple of necessary modules to work with our form and from our file “calc_ui.py” we import the main class Ui_MainWindow .

Next, edit if __name__ . We delete everything that is not needed. You should have this:

if __name__ == '__main__':
    # Новый экземпляр QApplication
    app = QtWidgets.QApplication(sys.argv)
    # Сздание инстанса класса Калькулятор, который мы создадим далее
    calc = Calculator()
    # Запуск
    sys.exit(app.exec_())

I commented on the code clearly hopefully. Let's move on to creating the Calculator class .

class Calculator(QtWidgets.QMainWindow, Ui_MainWindow):
    # Конструктор класса
    def __init__(self):
        super().__init__()
        # Создание формы и Ui (наш дизайн)
        self.setupUi(self)
        # Показать наше окно
        self.show()

Further, our task is to ensure that something happens when we poke the buttons "1", "2", "3", etc.

In the same place, in the class constructor, we declare the button connection to any function:

        # pressed
        self.pushButton.clicked.connect(self.digit_pressed)  # 1
        self.pushButton_2.clicked.connect(self.digit_pressed)  # 2
        self.pushButton_3.clicked.connect(self.digit_pressed)  # 3
        self.pushButton_4.clicked.connect(self.digit_pressed)  # 4
        self.pushButton_5.clicked.connect(self.digit_pressed)  # 5
        self.pushButton_6.clicked.connect(self.digit_pressed)  # 6
        self.pushButton_7.clicked.connect(self.digit_pressed)  # 7
        self.pushButton_8.clicked.connect(self.digit_pressed)  # 8
        self.pushButton_9.clicked.connect(self.digit_pressed)  # 9
        self.pushButton_10.clicked.connect(self.digit_pressed)  # 0
        self.pushButton_add.clicked.connect(self.pressed_equal)  # +
        self.pushButton_ded.clicked.connect(self.pressed_equal)  # -
        self.pushButton_div.clicked.connect(self.pressed_equal)  # /
        self.pushButton_mul.clicked.connect(self.pressed_equal)  # *
        self.pushButton_exp.clicked.connect(self.pressed_equal)  # **
        self.pushButton_log.clicked.connect(self.pressed_equal)  # log
        self.pushButton_procent.clicked.connect(self.pressed_equal)  # %
        self.pushButton_ENTER.clicked.connect(self.function_result)  # =
        self.pushButton_C.clicked.connect(self.function_clear)  # C
        self.pushButton_point.clicked.connect(self.make_fractional)  # .
        self.pushButton_delete.clicked.connect(self.function_delete)  # <
        self.pushButton_open_skob.clicked.connect(self.create_big_example)  # (

The self.digit_pressed function is already indicated in the code on the buttons , let's take a look at it, what does it do if the user clicks on the button with numbers:

    # lineEdit - белое поле, в котором будут транслироваться все цифры и операции
    # text() - возвращает текст, который написан на нашей кнопке
    # setText() - кладет текст в объект от которого мы вызываем его
    # sender() - функция, которая возвращает отправителя сигнала (какая кнопка была нажата, от какой идет сигнал)
    def digit_pressed(self):
        button = self.sender()
        if self.lineEdit.text() == '0':
            # Если у нас в нашем поле результата "0", то заменяем его на текст, который написан на кнопке
            self.lineEdit.setText(button.text())
        else:
            if self.result == self.lineEdit.text():
                self.lineEdit.setText(button.text())
            else:
                self.lineEdit.setText(self.lineEdit.text() + button.text())
        self.result = 0

Comments on the code are also present.

Now consider a function that will respond to pressing operations "+" , "-" , etc.

    clear() - отчищает строку, которую от которой мы его вызываем
    def pressed_equal(self):
        button = self.sender()
        self.first_value = float(self.lineEdit.text())
        self.lineEdit.clear()
        self.label.setText(str(self.first_value) + button.text())
        self.equal = button.text()

We write the first value to the self.first_value variable and clear our field in order to enter the value of the operation, and then output to lineEdit (our main input and result field) all together with the number and operation.

Why float ? I can answer so that I decided to do everything float , and when outputting the value to the result block, if the result has “.0” at the end, delete this part so that the number is integer. I didn’t think of a better one.

We now have functions for pressing numbers and for pressing operations, what's next? Next we need to display the result, this is the button = (ENTER) .

    def function_result(self):
        if self.equal == '+':
            self.function_addition()
        elif self.equal == '-':
            self.function_subtraction()
        elif self.equal == "/":
            self.function_divison()
        elif self.equal == '*':
            self.function_multiply()
        elif self.equal == "^":
            self.exponentiation()
        elif self.equal == "%":
            self.function_percent()
        elif self.equal == "log":
            self.function_log()

Let me remind you that the variable self.first_value is now exactly the first variable that we enter, and self.equal contains the operation that we pressed. After we enter the second number and press = , we try to find out what the operation is, and then we determine the 2nd variable.

We move on to the functions of operations. I got it like this:

    def function_addition(self):
        self.determinate_second_value()
        self.result = float(self.first_value + self.second_value)
        self.form_result()
    def function_subtraction(self):
        self.determinate_second_value()
        self.result = float(self.first_value - self.second_value)
        self.form_result()
    def function_divison(self):
        self.determinate_second_value()
        self.result = float(self.first_value / self.second_value)
        self.form_result()
    def function_multiply(self):
        self.determinate_second_value()
        self.result = float(self.first_value * self.second_value)
        self.form_result()
    def function_exponentiation(self):
        self.determinate_second_value()
        self.result = float(self.first_value ** self.second_value)
        self.form_result()
    def function_percent(self):
        self.determinate_second_value()
        self.result = float(self.first_value * (self.second_value / 100))
        self.form_result()
    def function_log(self):
        self.determinate_second_value()
        self.result = float(math.log(self.first_value, self.second_value))
        self.form_result()

The self.determinate_second_value () function determines the second value of the variable that we entered. It’s not a little logical and crooked, but as it is, I’ll try to take into account all the errors later in the comments when smart people say how to.

A little refreshment.

  • self.first_value - has the value of the first number entered
  • self.equal - has str the variable of the operation we pressed
  • self.second_value - has the value of the second variable

Next, in each of the operations functions, we call self.form_result () , which, strangely enough, forms our result. We keep the result in the variable self.result .

    def form_result(self):
        self.result = str(self.result)
        if self.result[-2:] == '.0':
            self.result = self.result[:-2]
        self.lineEdit.setText(str(self.result))
        self.label.clear()

I will explain for self.result [-2:] . [-2:] means that we are comparing the last 2 characters of the string with ".0".

So our result is displayed in the main lineEdit block, congratulations.

I’ll also attach here a code that removes one character from a string or the whole number or adds "." (dot) to create a fractional number:

    def make_fractional(self):
        value = self.lineEdit.text()
        if '.' not in value:
            self.lineEdit.setText(value + '.')
    def function_delete(self):
        value = self.lineEdit.text()
        self.lineEdit.setText(value[:-1])
    def function_clear(self):
        self.lineEdit.setText('0')

All code under the spoiler:

Whole code
from PySide2 import QtWidgets
from calc_ui import Ui_MainWindow
import sys
import math
class Calculator(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        # Создание формы и Ui (наш дизайн)
        self.setupUi(self)
        self.show()
        self.lineEdit.setText('0')
        self.first_value = None
        self.second_value = None
        self.result = None
        self.example = ""
        self.equal = ""
        # pressed
        self.pushButton.clicked.connect(self.digit_pressed)  # 1
        self.pushButton_2.clicked.connect(self.digit_pressed)  # 2
        self.pushButton_3.clicked.connect(self.digit_pressed)  # 3
        self.pushButton_4.clicked.connect(self.digit_pressed)  # 4
        self.pushButton_5.clicked.connect(self.digit_pressed)  # 5
        self.pushButton_6.clicked.connect(self.digit_pressed)  # 6
        self.pushButton_7.clicked.connect(self.digit_pressed)  # 7
        self.pushButton_8.clicked.connect(self.digit_pressed)  # 8
        self.pushButton_9.clicked.connect(self.digit_pressed)  # 9
        self.pushButton_10.clicked.connect(self.digit_pressed)  # 0
        self.pushButton_add.clicked.connect(self.pressed_equal)  # +
        self.pushButton_ded.clicked.connect(self.pressed_equal)  # -
        self.pushButton_div.clicked.connect(self.pressed_equal)  # /
        self.pushButton_mul.clicked.connect(self.pressed_equal)  # *
        self.pushButton_exp.clicked.connect(self.pressed_equal)  # **
        self.pushButton_log.clicked.connect(self.pressed_equal)  # log
        self.pushButton_procent.clicked.connect(self.pressed_equal)  # %
        self.pushButton_ENTER.clicked.connect(self.function_result)  # =
        self.pushButton_C.clicked.connect(self.function_clear)  # C
        self.pushButton_point.clicked.connect(self.make_fractional)  # .
        self.pushButton_delete.clicked.connect(self.function_delete)  # <
        self.pushButton_open_skob.clicked.connect(self.create_big_example)  # (
    def digit_pressed(self):
        # sender - функция, которая возвращает отправителя сигнала (какая кнопка была нажата, от какой идет сигнал)
        button = self.sender()
        if self.lineEdit.text() == '0':
            self.lineEdit.setText(button.text())
        else:
            if self.result == self.lineEdit.text():
                self.lineEdit.setText(button.text())
            else:
                self.lineEdit.setText(self.lineEdit.text() + button.text())
        self.result = 0
    def form_result(self):
        self.result = str(self.result)
        if self.result[-2:] == '.0':
            self.result = self.result[:-2]
        self.lineEdit.setText(str(self.result))
        self.label.clear()
    def make_fractional(self):
        value = self.lineEdit.text()
        if '.' not in value:
            self.lineEdit.setText(value + '.')
    def function_delete(self):
        value = self.lineEdit.text()
        self.lineEdit.setText(value[:-1])
    def function_clear(self):
        self.lineEdit.setText('0')
    def pressed_equal(self):
        button = self.sender()
        self.first_value = float(self.lineEdit.text())
        self.lineEdit.clear()
        self.label.setText(str(self.first_value) + button.text())
        self.equal = button.text()
    def function_addition(self):
        self.determinate_second_value()
        self.result = float(self.first_value + self.second_value)
        self.form_result()
    def function_subtraction(self):
        self.determinate_second_value()
        self.result = float(self.first_value - self.second_value)
        self.form_result()
    def function_divison(self):
        self.determinate_second_value()
        self.result = float(self.first_value / self.second_value)
        self.form_result()
    def function_multiply(self):
        self.determinate_second_value()
        self.result = float(self.first_value * self.second_value)
        self.form_result()
    def function_exponentiation(self):
        self.determinate_second_value()
        self.result = float(self.first_value ** self.second_value)
        self.form_result()
    def function_percent(self):
        self.determinate_second_value()
        self.result = float(self.first_value * (self.second_value / 100))
        self.form_result()
    def function_log(self):
        self.determinate_second_value()
        self.result = float(math.log(self.first_value, self.second_value))
        self.form_result()
    def determinate_second_value(self):
        self.second_value = float(self.lineEdit.text())
        self.lineEdit.clear()
        self.label.setText(str(self.first_value) + self.equal + str(self.second_value))
    def function_result(self):
        if self.equal == '+':
            self.function_addition()
        elif self.equal == '-':
            self.function_subtraction()
        elif self.equal == "/":
            self.function_divison()
        elif self.equal == '*':
            self.function_multiply()
        elif self.equal == "^":
            self.exponentiation()
        elif self.equal == "%":
            self.function_percent()
        elif self.equal == "log":
            self.function_log()
if __name__ == '__main__':
    # Новый экземпляр QApplication
    app = QtWidgets.QApplication(sys.argv)
    # Сздание инстанса класса
    calc = Calculator()
    # Запуск
    sys.exit(app.exec_())


Yes, the calculator does not calculate large functions and expressions, I'm working on it!

Thank you very much for your attention. Good luck and develop! That's cool.

I would also like to invite you to my JuniorProger Telegram channel , where I talk about the life of a Junior programmer and his thorny, but very interesting way of becoming an It specialist.

Also popular now: