Edit: New version at Python 3 Tkinter Calculator - follow-up
New status: I´ve refactored the code trying to follow the recommendations from the guys who answered this question. The new version is on the link above.
I'm a beginner developer and I chose Python as my initial language to learn. This is my very first project: a calculator using Tkinter for GUI.
I've tried to apply some OOP and modules approach, as an attempt to make it like a real job, instead of simply putting it all on a single file or procedural mode.
I need some feedback about module naming and organization, class naming and organization, PEP-8 style, and structure in general.
Module: window.py
This should be the main module, but I´m facing some circular import issue that I can't figure why yet.
import tkinter as tk import frame_display import frame_botoes root = tk.Tk() root.geometry("640x640") visor = frame_display.DisplayContainer(root) numeros = frame_botoes.ButtonsContainer(root) root.mainloop()
Module: calculadora.py
I made some sort of workaround and the programs runs here:
agregator = "" result = "" def pressNumber(num): global agregator global result agregator = agregator + str(num) result = agregator window.visor.updateTextDisplay(result) def pressEqual(): try: global agregator total = str(eval(agregator)) window.visor.updateTextDisplay(total) agregator = "" except ZeroDivisionError: window.visor.updateTextDisplay("Erro: Divisão por zero") agregator = "" except: window.visor.updateTextDisplay("Error") agregator = "" def pressClear(): global agregator agregator = "" window.visor.updateTextDisplay("Clear") import window
I tried to use separate modules and classes as an attempt to use good practices.
Module: frame_display.py
import tkinter as tk from tkinter import Frame from tkinter import StringVar class DisplayContainer(Frame): def __init__(self, root): Frame.__init__(self, root) self.parent = root self.configure(bg="cyan", height=5) self.text_display = StringVar() # Layout DisplayContainer self.grid(row=0 , column=0 , sticky="nwe") self.parent.columnconfigure(0, weight=1) # Call DisplayContainer widgets creation self.createWidgets() # Create widgets for DisplayContainer def createWidgets(self): self.label_display = tk.Label(self) self.label_display.configure(textvariable=self.text_display) self.label_display["font"] = 15 self.label_display["bg"] = "#bebebe" self.label_display["relief"] = "groove" self.label_display["bd"] = 5 self.label_display["height"] = 5 # Layout widgets for DisplayContainer self.label_display.grid(row=0 , column=0 , sticky="nswe") self.columnconfigure(0, weight=1) def updateTextDisplay(self, text): self.text_display.set(text)
Module: frame_botoes.py
import tkinter as tk from tkinter import Frame import calculadora class ButtonsContainer(Frame): def __init__(self , root): Frame.__init__(self, root) self.parent = root self.configure(bg="yellow") self.parent.bind("<Key>", self.keyHandler) self.parent.bind("<Return>", self.returnKeyHandler) # Layout ButtonsContainer self.grid(row=1 , column=0 , sticky ="nsew") self.parent.rowconfigure(1, weight=1) self.parent.columnconfigure(0, weight=1) # Call ButtonsContainer widgets creation self.createWidgets() # Create widgets for ButtonsContainer def createWidgets(self): button_padx = 15 button_pady = 15 self.button_1 = tk.Button(self, text="1", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(1)) self.button_2 = tk.Button(self, text="2", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(2)) self.button_3 = tk.Button(self, text="3", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(3)) self.button_4 = tk.Button(self, text="4", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(4)) self.button_5 = tk.Button(self, text="5", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(5)) self.button_6 = tk.Button(self, text="6", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(6)) self.button_7 = tk.Button(self, text="7", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(7)) self.button_8 = tk.Button(self, text="8", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(8)) self.button_9 = tk.Button(self, text="9", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(9)) self.button_0 = tk.Button(self, text="0", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(0)) self.button_open_parens = tk.Button(self, text="(", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber("(")) self.button_close_parens = tk.Button(self, text=")", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(")")) self.button_dot = tk.Button(self, text=".", padx= button_padx, pady=button_pady, command=lambda: calculadora.pressNumber(".")) self.button_plus = tk.Button(self, text="+", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber("+")) self.button_minus = tk.Button(self, text="-", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber("-")) self.button_multiply = tk.Button(self, text="*", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber("*")) self.button_divide = tk.Button(self, text="/", padx=button_padx, pady=button_pady, command=lambda: calculadora.pressNumber("/")) self.button_equal = tk.Button(self, text="=", padx=button_padx, pady=button_pady, command=calculadora.pressEqual) self.button_clear = tk.Button(self, text="CLEAR", padx=button_padx, pady=button_pady, command=calculadora.pressClear) # Layout widgets for ButtonsContainer self.button_1.grid(row=0, column=0, sticky="nswe") self.button_2.grid(row=0, column=1, sticky="nswe") self.button_3.grid(row=0, column = 2, sticky="nswe") self.button_4.grid(row=1, column=0, sticky="nswe") self.button_5.grid(row=1, column=1, sticky="nswe") self.button_6.grid(row=1, column=2, sticky="nswe") self.button_7.grid(row=2, column=0, sticky="nswe") self.button_8.grid(row=2, column=1, sticky="nswe") self.button_9.grid(row=2, column=2, sticky="nswe") self.button_open_parens.grid(row=3, column=0, sticky="nswe") self.button_close_parens.grid(row=3, column=2, sticky="nswe") self.button_0.grid(row=3, column=1, sticky="nswe") self.button_dot.grid(row=4, column=2, sticky="nswe") self.button_plus.grid(row=0 , column=3, sticky="nswe") self.button_minus.grid(row=1 , column=3, sticky="nswe") self.button_multiply.grid(row=2 , column=3, sticky="nswe") self.button_divide.grid(row=3 , column=3, sticky="nswe") self.button_equal.grid(row=4 , column=3, sticky="nswe") self.button_clear.grid(row=4 , columnspan=2, sticky="nswe") for x in range(0,5): self.rowconfigure(x, weight=1) for i in range(0, 4): self.columnconfigure(i, weight=1) #Bind keyboard events def keyHandler(self, event): calculadora.pressNumber(event.char) #Bind Return key def returnKeyHandler(self, event): calculadora.pressEqual()