Calculator steps - калькулятор по шагам

1. Создание интерфейса

Для начала создадим минимально необходимый интерфейс:

calculator_v01.png

Задание:

  1. Названия переменных должны быть корректными:

    • существующие английские слова;
    • имя переменной всегда начинается с имени существительного отражающее суть/смысл переменной;
    • соединение двух и/или более слов через нижнее подчеркивание: "button_zero".
  2. Создадим виджет Entry():

    • реализовать отображение нуля;
    • с параметром выравнивания текста по правому краю;
    • виджет должен растягиваться по горизонтали на всю ширину окна.
  3. Создадим виджет Entry():

    • реализовать отображение нуля;
    • с параметром выравнивания текста по правому краю;
    • задать стиль шрифта "Helvetica 30 bold";
    • виджет должен растягиваться по горизонтали на всю ширину окна.
  4. Создадим две кнопки обозначающие "0" и "1".

Рекомендации:

  1. Виждеты Entry() лучше растянуть на несколько колоном с запасом, чтобы не исправлять растяжение каждый раз при добавлении новых кнопок. В данном случае было задано заполнение на 15 колонок.

  2. Создаем только интерфейс, реализация функциональности кнопок на данном этапе не требуется.

2. Реализация функциональности двух кнопок

Визуально в интерфейсе измениться лишь заголовок версии калькулятора:

calculator_v02.png

Задание:

  1. Названия функций должны быть корректными:

    • существующие английские слова;
    • название начинается всегда с глагола отражающие суть/смысл функции;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. Создать и подключить функцию для кнопки "1":

    • функция должна допечатывать цифру "1" в оба поля Entry().
    • реализовать проверку при которой не будет происходить набор цифр на табло вида: "011" и т.п.
  3. Создать и подключить функцию для кнопки "0":

    • функция должна допечатывать цифру "0" в оба поля Entry().
    • реализовать проверку при которой не будет происходить набор цифр на табло вида: "000" и т.п.
  4. Проверить работу кнопок: "0" и "1":

    • правильные цифры вида: 1, 101, 1100 и т.п.
    • неправильные цифры вида: 01, 00, 001 и т.п. набираться не должны.
    • не допустимо выведение ошибок в консоли.

Рекомендации:

  1. На начальном этапе создания для проверки факта вызова функции можно использовать команду: print().

  2. Если функцию для кнопки один написана верно, то функцию для нуля можно, написать скопировав функцию один с минимальными изменениями:

    • изменив имя функции;
    • изменив цифру выводимую на экранах.
  3. Не забудьте изменить версию в заголовке программы на "Calc v.02"

3. Clear - добавление кнопки очистки

На следующем шаге добавим кнопку обнуления наших полей:

calculator_v03.png

Задание:

  1. добавить кнопку очистки - "С" и реализовать функцию очистки - обнуления результатов.

4. Plus - добавление кнопки плюс

Добавим кнопку плюс и реализуем функциональной кнопки:

calculator_v04.png

Задание:

  1. Названия функций должны быть корректными:

    • существующие английские слова;
    • название начинается всегда с глагола отражающие суть/смысл функции;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. добавить кнопку плюс - "+" и реализовать функциональность:

    • создать функцию, которая будет допечатывать в виждете выражения, не более одного "+";
    • без реализации вычисления введенного выражения.
  3. доработать функции нуля и единицы. Добавить проверки для корректности набора выражения:

    • допустимые выражения: 1+0+10+11 и т.п.
    • недопустимые выражения: 00++01+001 и т.п.
    • не допустимо выведение ошибок в консоли.

Рекомендации:

  1. Функция плюс только проверяет наличие в конце знака "+":

    • если выражение заканчивается цифрой, то "+" доставляется в конец строки;
    • если выражение заканчивается "+", то стирается "+" в конце исходного выражения, и "+" доставляется в конце строки;
  2. Функции нуля и единицы, лучше дорабатывать добиваясь максимальной идентичности тела функции. На данном шаге различие тел функций можно свести только к разнице доставляемых "0" или "1".

  3. В дальнейшем ещё планируется добавление цифр, поэтому колонка для размещения плюса указана 10.

На данном этапе калькулятор реализует только четыре функции:

  • функция "нажата_очистка";
  • функция "нажата_ноль";
  • функция "нажата_один";
  • функция "нажат_плюс".

5. Eval() - вычисляем выражение

Теперь будут вычисления:

calculator_v05.png

Функция eval() - позволяет вычислить значение примера переданного в виде строки:

answer = eval("3+3*3")
print(answer)           # 12
answer = eval("4-3/2")
print(answer)           # 2.5
1
2
3
4

Задание:

  1. Используя функцию eval(), доработайте функцию плюса:
    • будет браться выражение из верхнего виджета;
    • вычисляться;
    • ответ выводиться в основной цифровой дисплей при нажатии кнопки "+".
    • не допустимо выведение ошибок в консоли.

6. Equal - равно

Добавляем кнопку равно:

calculator_v06.png

Задание:

  1. Названия функций и переменных должны быть корректными:

    • существующие английские слова;
    • название функции всегда начинается с глагола отражающие суть/смысл функции;
    • имя переменной всегда начинается с имени существительного отражающее суть/смысл переменной;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. На основе функции "+" создаем функцию "=" с минимальными изменениями. В идеале они должны отличаться только знаком. При необходимости доработайте обе функции.

  3. Прикрепите функцию "равно" к кнопке "=".

  4. Проверьте корректность работы калькулятора:

    • допустим ввод выражения: 11+10+1= и т.п. и выводом результата в основной цифровой дисплей: 22 и т.п.
    • не допустим ввод выражения: 01++00=+01+= и т.п.
    • не допустимо выведение ошибок в консоли.

На данном этапе калькулятор реализует пять функции:

  • функция "нажата_очистка()";
  • функция "нажата_ноль()";
  • функция "нажата_один()";
  • функция "нажат_плюс()";
  • функция "нажато_равно()".

7. Division - деление

Добавляем кнопку деление:

calculator_v07.png

Задание:

  1. Названия функций и переменных должны быть корректными:

    • существующие английские слова;
    • название функции всегда начинается с глагола отражающие суть/смысл функции;
    • имя переменной всегда начинается с имени существительного отражающее суть/смысл переменной;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. На основе функции "+" или "=" создаем функцию "/" с минимальными изменениями. В идеале они должны отличаться только знаком. При необходимости доработайте функции.

  3. Прикрепите функцию "равно" к кнопке "/".

  4. Проверьте корректность работы калькулятора:

    • допустим ввод выражения: 11+10/1= и т.п. и выводом результата в основной цифровой дисплей: 22 и т.п.
    • не допустим ввод выражения: 01++/00=//01+=10//= и т.п.
    • выведение ошибок в консоли допустимо только при вводе примера с делением на ноль вида: 1/0= и т.п.

На данном этапе калькулятор реализует функции:

  • функция "нажата_очистка()";
  • функция "нажата_ноль()";
  • функция "нажата_один()";
  • функция "нажат_плюс()";
  • функция "нажато_деление()";
  • функция "нажато_равно()".

8. Функция "нажато_число(число)" с параметром

Визуальные изменения будут следующие:

calculator_v08.png

Функция с параметром number имеет вид:

def print_parametr(number):
    print(number)
1
2

Создадим дополнительную пробную программку вида:

example_01.png

Пример печати номера нажатой кнопки может быть следующим:

from tkinter import *
from tkinter.ttk import *

def click_one():
    print(1, end=" ")

def click_two():
    print(2, end=" ")

root = Tk()
Button(text="1", command=click_one).pack(side=LEFT)
Button(text="2", command=click_two).pack(side=LEFT)
root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13

Результат в зависимости последовательности от нажатия кнопок может быть:

1 2 2 1 2
1

Подобное поведение можно реализовать с помощью функции с параметром и lambda-функции:

from tkinter import *
from tkinter.ttk import *

def click(number):
    print(number, end=" ")

root = Tk()
Button(text="1", command=lambda: click(1)).pack(side=LEFT)
Button(text="2", command=lambda: click(2)).pack(side=LEFT)
root.mainloop()
1
2
3
4
5
6
7
8
9
10

Результат поведения будет подобен предыдущему коду:

1 2 2 1 2
1

Задание:

  1. Названия функций и переменных должны быть корректными:

    • существующие английские слова;
    • название функции всегда начинается с глагола отражающие суть/смысл функции;
    • имя переменной всегда начинается с имени существительного отражающее суть/смысл переменной;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. На основе функции "1" создаем функцию с параметром, например: click_number(number). В том месте где явно выводили "1" на печать, заменяем параметром number

  3. Заменяем в кнопках "0" и "1" вызов функции на функцию с параметром добавив в скобках вызова нужное число.

  4. Комментируем функции: "нажата_ноль()" и "нажата_один()"

  5. Проверяем правильность работы калькулятора. Появление ошибок в консоли допустимо только при вводе примера с делением на ноль вида: 1/0= и т.п.

  6. Добавляем кнопку "2" и подключаем к функцию с параметром.

  7. Проверяем ещё раз. Работа нарушаться не должна.

  8. Если все работает верно. Удаляем закомментированный ранее код.

На данном этапе калькулятор реализует функции:

  • функция "нажата_очистка()";
  • функция "нажата_цифра(цифра)";
  • функция "нажат_плюс()";
  • функция "нажато_деление()";
  • функция "нажато_равно()".

9. Функция "нажат_знак(знак)" с параметром

Визуальные изменения будут следующие:

calculator_v09.png

Задание:

  1. Названия функций и переменных должны быть корректными:

    • существующие английские слова;
    • название функции всегда начинается с глагола отражающие суть/смысл функции;
    • имя переменной всегда начинается с имени существительного отражающее суть/смысл переменной;
    • соединение двух и/или более слов через нижнее подчеркивание: "click_zero".
  2. На основе функции "+" создаем функцию с параметром, например: click_sign(sign). В том месте где явно выводили "+" на печать, заменяем параметром sing.

  3. Заменяем в кнопках "+" и "/" вызов функции на функцию с параметром добавив в скобках вызова нужный символ.

  4. Комментируем функции: "нажат_плюс()" и "нажато_деление()"

  5. Проверяем правильность работы калькулятора. Появление ошибок в консоли допустимо, за исключением примера с делением на ноль вида: 1/0= и т.п.

  6. Исправляем кнопку "=" и подключаем к функцию с параметром.

  7. Проверяем ещё раз. Работа нарушаться не должна.

  8. Если все работает верно. Удаляем закомментированный ранее код.

На данном этапе калькулятор реализует функции:

  • функция "нажата_очистка()";
  • функция "нажата_цифра(цифра)";
  • функция "нажат_знак(знак)".

10. Проверка деления на ноль

Следующая версия:

calculator_v10.png

Задание:

  1. Проверку необходимо реализовывать только для строки кода в которой возможно возникновение ошибки, т.е. для единственной строки где вызывается функция eval().

  2. Реализовать проверку, используя конструкцию: try-except. При делении на ноль можно выводить надпись на цифровом экране: "Cannot divide by zero".

  3. Доработать функцию нажатия цифр, что бы после выведения ошибки деления на ноль можно было корректно проводить новые вычисления.

  4. Тип отлавливаемой ошибки лучше указывать явно. В случае "деления на ноль" - это пит ошибки ZeroDivisionError.

  5. Проверяем правильность работы калькулятора:

    • недопустимые выражения: Cannot divide by zero0, Cannot divide by zero+ и т.п.
    • не допустимо выведение ошибок в консоли.

Рекомендации:

  1. Реализовать проверку "деления на ноль" используя конструкцию if... else, вместо try-except.

На данном этапе калькулятор реализует функции:

  • функция "нажата_очистка()";
  • функция "нажата_цифра(цифра)";
  • функция "нажат_знак(знак)".

11. Генерация кнопок с числами

Создадим дополнительную пробную программку вида:

example_02.png

Используя цикл и подключенную lambda-функции получим следующий код:

from tkinter import *
from tkinter.ttk import *

def click(number):
    print(number, end=" ")

root = Tk()
for i in range(5):
    Button(text=i, command=lambda: click(i)).pack(side=LEFT)
root.mainloop()
1
2
3
4
5
6
7
8
9
10

При нажатии кнопок по порядку в консоль выводиться только "4". Переменная i - динамическая, и цикле изменяет свое значение с 0 до 4. После генерации всех кнопок последнее значение i=4, поэтому после запуска программы, при нажатии на любые сгенерированные кнопки в консоль выводятся только "4":

4 4 4 4 4
1

Для внутреннего сохранения (связывания) значения i момент генерации определенной кнопки, используется lambda-функция с параметром:

from tkinter import *
from tkinter.ttk import *

def click(number):
    print(number, end=" ")

root = Tk()
for i in range(5):
    Button(text=i, command=lambda num=i: click(num)).pack(side=LEFT)
root.mainloop()
1
2
3
4
5
6
7
8
9
10

Результат нажатия кнопок по порядку:

1 2 3 4 5
1

Добавим дополнительный ряд кнопок с числами, преобразуем визуально:

calculator_v11.png

Задание:

  1. Сгенерировать кнопки используя "лямбда-функцию" с параметром.

  2. Проверяем правильность работы калькулятора:

    • выражение и числа должны вводиться и выводиться корректно.
    • появление ошибок в консоли допустимо.

12. Генерация кнопок с математическими знаками

Добавим ряд кнопок со знаками:

calculator_v12.png

Задание:

  1. Сгенерировать базовых математических операций: "+", "-", "*", "/", используя "лямбда-функцию" с параметром.

  2. Реализовать функциональность кнопок, внесите необходимые изменения в функции нажатия цифровых кнопок и кнопок математических операций.

  3. Проверяем правильность работы калькулятора:

    • выражение и числа должны вводиться и выводиться корректно.
    • появление ошибок в консоли допустимо.

13. Дальнейше развитие

Задание:

  1. Добавить кнопку "." и реализовать её функциональность.

  2. Добавить кнопку стирания последней набранной цифры.

  3. Добавить генерацию кнопок расширенных математических операций. Реализовать функциональность кнопок.

  4. Добавить привязку нажатие клавиш на клавиатуре к соответствующим кнопкам калькулятора.

  5. Реализовать полную генерацию разных интерфейсов на массива.

  6. Создать свою функцию вычисления примеров в замен функции eval().

Рекомендации:

  1. Для кнопки "." лучше использовать функцию чисел.

  2. Полную генерацию кнопок интерфейса можно реализовать передав кнопки в виде двумерного массива.

  3. Полностью реализуйте функциональность калькулятора. Программа должна стать законченным проектом.