Привет, Хабр!
Меня зовут Саша. Я Junior разработчик. Работаю тестировщиком ПО. В основном я пишу тесты при помощи Python+Selenium, но Python стал настолько интересен, что мне захотелось углубиться в него и выучить как можно больше фреймворков! Я захотел написать десктопное приложение, аля простой «Калькулятор». Мой выбор пал на Pyside2. Я не претендую на идеальный код или урок. Просто есть желание поделиться опытом, если кто-то, как и я, хочет начать шарить в Python. Если кому-то помогу — результата я достиг.
Начнем!
Писал свой код я в IDE от JetBrains «PyCharm». ОС — Windows.
Установка PySide2:
Там, где у вас корневая папка Python, переходим в нее, потом в папку «Lib» -> «site-packages» -> «Pyside2». У вас будет программа designer — это программа QtDesigner, которая позволит вам сделать собственный интерфейс вашей программы. Стоит заметить, что когда вы создадите свой файл в директории вашего проекта, то у него будет формат .ui, что понятное дело Python не поймет, поэтому нам нужно будет преобразоваться его в формат .py, но это позже.
Сначала создаем свою форму.

Делаем свой дизайн, называем кнопки справа в подразделе «Инспектор Объектов». Стоит сказать, что QtDesigner поддерживает каскадную таблицу стилей, а если легче, то найдя в свойствах параметр «styleSheet», у вас есть возможность сделать свой дизайн, опираясь на знания CSS.

Далее нам нужно преобразоваться наш файл .ui в формат, что бы его понимал Python. Переходим в командную строку и пишем
Что делает команда «pyside2-uic»? Она преобразует ваш файл формата .ui в питонячий файл формата .py и создает из него класс Python. Возможно знающие люди скажут, что ui файл можно подключить и без конвертации в проект, но я буду аккуратен и лучше сделаю, как написано в мануалах по Pyside2.
Далее переходим к коду.
Открываем PyCharm, нашу директорию с проектом и создаем файл «calc_ui.py» или «something_ui.py» в зависимости от того, какую программу вы делайте. Приставка _ui В конце файла поможет нам не запутаться в файлах. В общем виде, должно выглядеть так:

Начнем с редактирования файла, который мы преобразовали из .ui в .py.
Внизу ищем данный код и копируем его, после удаляем из этого файла.
Создаем наш основной файл, где будет прописана логика программы. Я назвал его, как, не удивительно «calc.py» (выше на скрине директории это видно).
Вставляем туда наш скопированный код и начинаем редактировать его.
import sys кидаем в начало, так как это правило хорошего тона.
Импортируем пару необходимых модулей для работы с нашей формой и из нашего файла «calc_ui.py» импортируем основной класс Ui_MainWindow.
Далее редактируем if __name__. Удаляем все не нужное. У вас должно получиться вот так:
Код я прокомментировал надеюсь внятно. Перейдем к созданию класса Calculator.
Далее наша задача стоит так, что бы что-то происходило, когда мы тыкаем на кнопки «1», «2», «3» и т.д.
Там же в конструкторе класса объявляем подключение кнопки к какой-либо функции:
В коде на кнопках уже указана функция self.digit_pressed, давайте взглянем на нее, что же она делает, если пользователь нажал на кнопку с цифрами:
Комментарии к коду так же присутствуют.
Теперь рассмотрим функцию, которая будет реагировать на нажатие операций "+", "-" и т.д
Мы записываем первое значение в переменную self.first_value и чистим наше поле, для того, что бы ввели значение операции, после чего выводим в lineEdit(наше основное поле ввода и результата) все вместе с числом и операцией.
Почему float? Могу ответить так, что я решил все сделать float, а при выводе значения в блок результата, если результат имеет в конце ".0", удалять эту часть, что бы число было целочисленное. Лучше я не придумал.
У нас есть теперь функции для нажатия цифр и для нажатия операций, что дальше? Дальше нам надо выводить результат, это кнопка = (ENTER).
Напомню, что переменная self.first_value сейчас ровна первой переменной, которую мы вводим, а self.equal держит в себе операцию, которую мы нажали. После мы вводим второе число и жмем на =, мы пытаемся узнать, что же это за операция, а потом и определяем 2-ю переменную.
Переходим дальше, к функциям операций. Получилось у меня вот так:
Функция self.determinate_second_value() определяет второе значение переменной, которое мы ввели. Немного не логично и криво, но как есть, постараюсь учесть все ошибки потом в комментариях, когда умные люди скажут как правильно.
Немного освежимся.
Далее в каждой из функции операций мы вызываем self.form_result(), которая, как не странно, формирует наш результат. Результат мы держим в переменной self.result.
Поясню за self.result[-2:]. [-2:] говорит о том, что мы сравниваем последние 2 символа строки с ".0".
Вот и наш результат выводится в главный блок lineEdit, поздравляю.
Так же приложу здесь код, который удаляет один символ из строки или полностью все число или добавляет "." (точку) для создания дробного числа:
Весь код под спойлером:
Да, калькулятор не вычисляет большие функции и выражения, я над этим работаю!
Спасибо вам большое за внимание. Удачи вам и развивайтесь! Это круто.
Так же хотелось бы пригласить вас в свой Telegram-канал JuniorProger, где я рассказываю о жизни Junior программиста и его тернистый, но очень интересный путь становления It-специалистом.
Меня зовут Саша. Я Junior разработчик. Работаю тестировщиком ПО. В основном я пишу тесты при помощи Python+Selenium, но Python стал настолько интересен, что мне захотелось углубиться в него и выучить как можно больше фреймворков! Я захотел написать десктопное приложение, аля простой «Калькулятор». Мой выбор пал на Pyside2. Я не претендую на идеальный код или урок. Просто есть желание поделиться опытом, если кто-то, как и я, хочет начать шарить в Python. Если кому-то помогу — результата я достиг.
Начнем!
Писал свой код я в IDE от JetBrains «PyCharm». ОС — Windows.
Установка PySide2:
pip install PySide2
Там, где у вас корневая папка Python, переходим в нее, потом в папку «Lib» -> «site-packages» -> «Pyside2». У вас будет программа designer — это программа QtDesigner, которая позволит вам сделать собственный интерфейс вашей программы. Стоит заметить, что когда вы создадите свой файл в директории вашего проекта, то у него будет формат .ui, что понятное дело Python не поймет, поэтому нам нужно будет преобразоваться его в формат .py, но это позже.
Сначала создаем свою форму.

Делаем свой дизайн, называем кнопки справа в подразделе «Инспектор Объектов». Стоит сказать, что QtDesigner поддерживает каскадную таблицу стилей, а если легче, то найдя в свойствах параметр «styleSheet», у вас есть возможность сделать свой дизайн, опираясь на знания CSS.

Далее нам нужно преобразоваться наш файл .ui в формат, что бы его понимал Python. Переходим в командную строку и пишем
pyside2-uic "you_file.ui" -o "your_file.py"
Что делает команда «pyside2-uic»? Она преобразует ваш файл формата .ui в питонячий файл формата .py и создает из него класс Python. Возможно знающие люди скажут, что ui файл можно подключить и без конвертации в проект, но я буду аккуратен и лучше сделаю, как написано в мануалах по Pyside2.
Далее переходим к коду.
Открываем PyCharm, нашу директорию с проектом и создаем файл «calc_ui.py» или «something_ui.py» в зависимости от того, какую программу вы делайте. Приставка _ui В конце файла поможет нам не запутаться в файлах. В общем виде, должно выглядеть так:

Начнем с редактирования файла, который мы преобразовали из .ui в .py.
Внизу ищем данный код и копируем его, после удаляем из этого файла.
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_())
Создаем наш основной файл, где будет прописана логика программы. Я назвал его, как, не удивительно «calc.py» (выше на скрине директории это видно).
Вставляем туда наш скопированный код и начинаем редактировать его.
import sys кидаем в начало, так как это правило хорошего тона.
Импортируем пару необходимых модулей для работы с нашей формой и из нашего файла «calc_ui.py» импортируем основной класс Ui_MainWindow.
Далее редактируем if __name__. Удаляем все не нужное. У вас должно получиться вот так:
if __name__ == '__main__': # Новый экземпляр QApplication app = QtWidgets.QApplication(sys.argv) # Сздание инстанса класса Калькулятор, который мы создадим далее calc = Calculator() # Запуск sys.exit(app.exec_())
Код я прокомментировал надеюсь внятно. Перейдем к созданию класса Calculator.
class Calculator(QtWidgets.QMainWindow, Ui_MainWindow): # Конструктор класса def __init__(self): super().__init__() # Создание формы и Ui (наш дизайн) self.setupUi(self) # Показать наше окно self.show()
Далее наша задача стоит так, что бы что-то происходило, когда мы тыкаем на кнопки «1», «2», «3» и т.д.
Там же в конструкторе класса объявляем подключение кнопки к какой-либо функции:
# 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) # (
В коде на кнопках уже указана функция self.digit_pressed, давайте взглянем на нее, что же она делает, если пользователь нажал на кнопку с цифрами:
# 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
Комментарии к коду так же присутствуют.
Теперь рассмотрим функцию, которая будет реагировать на нажатие операций "+", "-" и т.д
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()
Мы записываем первое значение в переменную self.first_value и чистим наше поле, для того, что бы ввели значение операции, после чего выводим в lineEdit(наше основное поле ввода и результата) все вместе с числом и операцией.
Почему float? Могу ответить так, что я решил все сделать float, а при выводе значения в блок результата, если результат имеет в конце ".0", удалять эту часть, что бы число было целочисленное. Лучше я не придумал.
У нас есть теперь функции для нажатия цифр и для нажатия операций, что дальше? Дальше нам надо выводить результат, это кнопка = (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()
Напомню, что переменная self.first_value сейчас ровна первой переменной, которую мы вводим, а self.equal держит в себе операцию, которую мы нажали. После мы вводим второе число и жмем на =, мы пытаемся узнать, что же это за операция, а потом и определяем 2-ю переменную.
Переходим дальше, к функциям операций. Получилось у меня вот так:
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()
Функция self.determinate_second_value() определяет второе значение переменной, которое мы ввели. Немного не логично и криво, но как есть, постараюсь учесть все ошибки потом в комментариях, когда умные люди скажут как правильно.
Немного освежимся.
- self.first_value — имеет значение первого введенного числа
- self.equal — имеет str переменную операции, которую мы нажали
- self.second_value — имеет значение второй переменной
Далее в каждой из функции операций мы вызываем self.form_result(), которая, как не странно, формирует наш результат. Результат мы держим в переменной 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()
Поясню за self.result[-2:]. [-2:] говорит о том, что мы сравниваем последние 2 символа строки с ".0".
Вот и наш результат выводится в главный блок lineEdit, поздравляю.
Так же приложу здесь код, который удаляет один символ из строки или полностью все число или добавляет "." (точку) для создания дробного числа:
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')
Весь код под спойлером:
Весь код
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_())
Да, калькулятор не вычисляет большие функции и выражения, я над этим работаю!
Спасибо вам большое за внимание. Удачи вам и развивайтесь! Это круто.
Так же хотелось бы пригласить вас в свой Telegram-канал JuniorProger, где я рассказываю о жизни Junior программиста и его тернистый, но очень интересный путь становления It-специалистом.
