Оптимизация кода калькулятора
Есть код. В классе App прописана форма и калькулятор. Решил вынести часть кода калькулятора #логика в отдельный класс, и оставить часть кода, которая просто создаёт кнопки через цикл. Но при попытки сделать это столкнулся с множественными ошибками. Выходит, или я делаю то, что не должен, или не понимаю как правильно пользоваться этим.
Как я пробовал сделать:
Создаём отдельный класс типа class Scal:. Пробовал также добавить @staticmethod, но это никак не помогло. Вставляем в него "логику", кнопки оставляем в форме где они и должны быть. Поскольку calc_entry остаётся в классе App, то дописываю везде App()., так же как и перед tab3.
В эту же строку добавлял cmd = lambda x=i: >>Scal().<<calc(x)
Вопрос думаю понятен: как разделить данный код и можно ли? Так же буду признателен за какую-либо иную оптимизацию кода<3
Делюсь исходно рабочим кодом:
import numpy as np
import pandas as pd
from tkinter import *
import tkinter as tk
from tkinter import ttk
import tkinter.messagebox as mb
class App(tk.Tk):
def __init__(self):
super().__init__()
#self = Tk()
self.title("Матрицы для детей")
self.geometry('745x250')
tab_control = ttk.Notebook(self)
tab1 = ttk.Frame(tab_control)
tab2 = ttk.Frame(tab_control)
tab3 = ttk.Frame(tab_control)
tab_control.add(tab1, text='Матрицы')
tab_control.add(tab2, text='Вектора')
tab_control.add(tab3, text='Скаляры')
tab_control.pack(expand=1, fill='both')
self.spin0 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin0.grid(column=0, padx=25, row=0)
self.spin1 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin1.grid(column=1, padx=50, row=0)
self.spin2 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin2.grid(column=2, padx=70, row=0)
#self.spin0_0 = Spinbox(tab2, from_=0, to=9, width=5)
#self.spin0_0.grid(column=1, padx=0, row=0)
#self.spin0_1 = Spinbox(tab2, from_=0, to=9, width=5)
#self.spin0_1.grid(column=2, padx=25, row=0)
### матрицы
btn = Button(tab1, text='Матричное произведение', width=25, command=Matrix().martix_multiply)
btn.grid(row=2, column=0, padx=0, pady=5)
btn = Button(tab1, text='Сложение', width=25, command=Matrix().summ)
btn.grid(row=2, column=1, padx=0, pady=5)
btn = Button(tab1, text='Матрица на скаляр', width=25, command=Matrix().on_scal)
btn.grid(row=2, column=2, padx=0, pady=5)
btn = Button(tab1, text='Вектор на матрицу', width=25, command=Matrix().vector)
btn.grid(row=2, column=3, padx=0, pady=5)
btn = Button(tab1, text='Поэлементное произведение', width=25, command=Matrix().multiply)
btn.grid(row=3, column=0, padx=0, pady=5)
btn = Button(tab1, text='След и определитель', width=25, command=Matrix().opredelitel)
btn.grid(row=3, column=1, padx=0, pady=5)
btn = Button(tab1, text='Обратная матрица', width=25, command=Matrix().inv_matrix)
btn.grid(row=3, column=2, padx=0, pady=5)
btn = Button(tab1, text='Транспонирование', width=25, command=Matrix().transposition)
btn.grid(row=3, column=3, padx=0, pady=5)
### вектора
btn = Button(tab2, text='Вектор на скаляр', width=25, command=Vector().vector_on_scal)
btn.grid(row=2, column=0, padx=0, pady=5)
btn = Button(tab2, text='Поэлементное Сложение', width=25, command=Vector().summ_vector)
btn.grid(row=2, column=1, padx=0, pady=5)
btn = Button(tab2, text='Поэлементное Умножение', width=25, command=Vector().mul_vector)
btn.grid(row=2, column=2, padx=0, pady=5)
btn = Button(tab2, text='Вектор на матрицу', width=25, command=Matrix().vector)
btn.grid(row=2, column=3, padx=0, pady=5)
btn = Button(tab2, text='Скалярное произведение', width=25, command=Vector().scal_mul)
btn.grid(row=3, column=0, padx=0, pady=5)
btn = Button(tab2, text='Векторное произведение', width=25, command=Vector().mull_vect)
btn.grid(row=3, column=1, padx=0, pady=5)
btn = Button(tab2, text='Длина вектора', width=25, command=Vector().length_vect)
btn.grid(row=3, column=2, padx=0, pady=5)
btn = Button(tab2, text='Проверка сонаправленности', width=25, command=0)
btn.grid(row=3, column=3, padx=0, pady=5)
btn = Button(tab2, text='Проверка на ортогональность', width=25, command=0)
btn.grid(row=4, column=0, padx=0, pady=5)
# логика
def calc(key):
global memory
if key == "=":
# исключение написания слов
str1 = "-+0123456789.*/)("
if calc_entry.get()[0] not in str1:
calc_entry.insert(END, "First symbol is not number!")
mb.showerror("Error!", "You did not enter the number!")
# исчисления
try:
result = eval(calc_entry.get())
calc_entry.insert(END, "=" + str(result))
except:
calc_entry.insert(END, "Error!")
mb.showerror("Error!", "Check the correctness of data")
# очищение поля ввода
elif key == "C":
calc_entry.delete(0, END)
elif key == "±":
if "=" in calc_entry.get():
calc_entry.delete(0, END)
try:
if calc_entry.get()[0] == "-":
calc_entry.delete(0)
else:
calc_entry.insert(0, "-")
except IndexError:
pass
elif key == "π":
calc_entry.insert(END, math.pi)
elif key == "Exit":
tab3.after(1, App.tab3.destroy)
sys.exit
elif key == "xⁿ":
calc_entry.insert(END, "**")
elif key == "sin":
calc_entry.insert(END, "=" + str(math.sin(int(calc_entry.get()))))
elif key == "cos":
calc_entry.insert(END, "=" + str(math.cos(int(calc_entry.get()))))
elif key == "(":
calc_entry.insert(END, "(")
elif key == ")":
calc_entry.insert(END, ")")
elif key == "n!":
calc_entry.insert(END, "=" + str(math.factorial(int(calc_entry.get()))))
elif key == "√2":
calc_entry.insert(END, "=" + str(math.sqrt(int(calc_entry.get()))))
else:
if "=" in calc_entry.get():
calc_entry.delete(0, END)
calc_entry.insert(END, key)
### кнопки
bttn_list = [
"7", "8", "9", "+", "*",
"4", "5", "6", "-", "/",
"1", "2", "3", "=", "xⁿ",
"0", ".", "±", "C",
"Exit", "π", "sin", "cos",
"(", ")", "n!", "√2", ]
r = 1
c = 0
for i in bttn_list:
rel = ""
cmd = lambda x=i: calc(x)
ttk.Button(tab3, text=i, command=cmd, width=10).grid(row=r, column=c)
c += 1
if c > 4:
c = 0
r += 1
calc_entry = Entry(tab3, width=33)
calc_entry.grid(row=0, column=0, columnspan=5)
if __name__ == "__main__":
read_a = pd.read_csv('first.csv', sep=";", header=None)
read_b = pd.read_csv('second.csv', sep=";", header=None)
app = App()
app.mainloop()
Ответы (1 шт):
Первый очевидный шаг - вытащить функцию
calcиз__init__, сделать для начала отдельным методом того же класса, чтобы в__init__осталось только создание графического интерфейса.Вложенными удобно делать небольшие функции, но такие огромные функции вложенными лучше не делать: получается, что, текст метода
__init__внезапно прерывается огромной функциейcalc, а после нее идет еще достаточно большой кусок метода. Читать такой код очень неудобно.Можно пообъединять if-ы с
calc_entry.insert(END, какой-то символ), чтобы получилось примерно так:elif key in {"(", ")", ...}: calc_entry.insert(END, key)Для функций можно сделать словарь, где набору символов соответствует конкретная функция, по нажатой клавише выбирать нужную и вычислять выражение:
funcs = {"sin": math.sin, "cos": math.cos, ...} ... elif key in funcs: calc_entry.insert(END, "=" + str(funcs[key](int(calc_entry.get()))))Создание кнопок для матриц и векторов можно делать циклами - по аналогии с созданием кнопок из
bttn_list, только в этом же списке можно хранить надпись кнопки и ее обработчик, например('Матричное произведение', Matrix().martix_multiply)Если перешли к ООП, то не используйте
global- записываете значения в поля объектаМетод
calcможно попробовать абстрагировать от графического интерфейса (чтобы можно было вытащить из классаApp), например передавать в него текст из текстового поля, и возвращать из него новый текст. Обработку нажатия на кнопку выхода либо вытащить изcalc, либо бросать исключение какого-то своего типа, снаружи отлавливать и обрабатывать.Создание и заполнение каждого отдельного таба можно выделить в отдельные классы
Пример с вынесенным наружу calc (куски кода, касающиеся заполнения первых двух табов, вырезал, чтобы не мешали пониманию):
from tkinter import *
import tkinter as tk
from tkinter import ttk
import tkinter.messagebox as mb
import math
functions = {
"sin": math.sin,
"cos": math.cos,
"n!": math.factorial,
"√2": math.sqrt
}
# логика
def calc(key, text):
if key == "=":
# исчисления
result = eval(text)
return text + "=" + str(result)
# очищение поля ввода
elif key == "C":
return ""
elif key == "±":
if "=" in text:
return ""
try:
if text[0] == "-":
return text[1:]
else:
return "-" + text
except IndexError:
pass
elif key == "π":
return text + math.pi
elif key == "xⁿ":
return text + "**"
elif key in functions:
return text + "=" + str(functions[key](int(text)))
# elif key in {"(", ")"}: # Будет обработано в else
# return text + key
else:
if "=" in text:
text = ""
return text + key
class App(tk.Tk):
def __init__(self):
super().__init__()
#self = Tk()
self.title("Матрицы для детей")
self.geometry('745x250')
tab_control = ttk.Notebook(self)
tab1 = ttk.Frame(tab_control)
tab2 = ttk.Frame(tab_control)
tab3 = ttk.Frame(tab_control)
tab_control.add(tab1, text='Матрицы')
tab_control.add(tab2, text='Вектора')
tab_control.add(tab3, text='Скаляры')
tab_control.pack(expand=1, fill='both')
self.spin0 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin0.grid(column=0, padx=25, row=0)
self.spin1 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin1.grid(column=1, padx=50, row=0)
self.spin2 = Spinbox(tab1, from_=0, to=9, width=5)
self.spin2.grid(column=2, padx=70, row=0)
calc_entry = Entry(tab3, width=33)
calc_entry.grid(row=0, column=0, columnspan=5)
### кнопки
bttn_list = [
"7", "8", "9", "+", "*",
"4", "5", "6", "-", "/",
"1", "2", "3", "=", "xⁿ",
"0", ".", "±", "C",
"Exit", "π", "sin", "cos",
"(", ")", "n!", "√2", ]
r = 1
c = 0
for i in bttn_list:
rel = ""
def command(key=i):
if key == "Exit":
tab3.after(1, App.tab3.destroy)
sys.exit(0)
try:
result = calc(key, calc_entry.get())
except Exception as ex:
mb.showerror("Error!", "Check the correctness of data")
raise
else:
calc_entry.delete(0, tk.END)
calc_entry.insert(0, result)
ttk.Button(tab3, text=i, command=command, width=10).grid(row=r, column=c)
c += 1
if c > 4:
c = 0
r += 1
if __name__ == "__main__":
app = App()
app.mainloop()