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()