https://habr.com/ru/post/458536/- Python
- Программирование
- Разработка под Windows
Привет, Хабр!
Меня зовут Саша. Я 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-специалистом.