HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

GUI Calculator using tkinter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
guicalculatorusingtkinter

Problem

This is my python program for a GUI Calculator made using the tkinter module.
This took me about 2 days to complete. However, I still believe that there is room for improvement. I am missing a few other buttons, and I do plan to add them later on. Also, I was unsure whether I should use the default style of tkinter or to use the ttk style (which looks better in my opinion). Nonetheless, I used to the default style just to test it out anyway, but is this style better or the ttk style better? Please give me any tips and advice you can on this topic.

```
# Calculator made using pythons tkinter module
# Author - Mohamed Akil

from tkinter import *

class Application(Frame):
""" Main class for calculator"""

def __init__(self, master):
""" Initialise the Frame. """
super(Application, self).__init__(master)
self.task = ""
self.UserIn = StringVar()
self.grid()
self.create_widgets()

def create_widgets(self):
""" Create all the buttons for calculator. """
# User input stored as an Entry widget.

self.user_input = Entry(self, bg = "#5BC8AC", bd = 29,
insertwidth = 4, width = 24,
font = ("Verdana", 20, "bold"), textvariable = self.UserIn, justify = RIGHT)
self.user_input.grid(columnspan = 4)

self.user_input.insert(0, "0")

# Button for value 7
self.button1 = Button(self, bg = "#98DBC6", bd = 12,
text = "7", padx = 33, pady = 25, font = ("Helvetica", 20, "bold"),
command = lambda : self.buttonClick(7))
self.button1.grid(row = 2, column = 0, sticky = W)

# Button for value 8
self.button2 = Button(self, bg = "#98DBC6", bd = 12,
text = "8", padx = 35, pady = 25,
command = lambda : self.buttonClick(8), font = ("Helvetica", 20, "bold"))
self.button2.grid(row = 2, column = 1, sticky = W)

# Button for value 9
self.button3 = Button(self, bg = "#98DBC6", bd = 12,

Solution

Ideally, you should have at least 2 classes: one to hold functions performing different actions on the GUI, and the other class to draw the GUI itself. Below, I provide suggestions to improve the second class ONLY because, for a performant calculator, there are much more things to improve.

Avoid wildcard imports

You should use import tkinter as tk instead of from tkinter import *. I know most tutorials teach the opposite, but that is a bad option because it can lead to namespaces problems.

Code duplication

The way you created the different buttons lead you to duplicate code several times. You need to find a way to refactor your code.

My approach to avoid that is to create all the buttons within a sing function:

def create_calculator_buttons_texts(self):
       button_column = 0
       button_row = 1
       text_buttons = ('A', '789+', '456-', '123*', '0=/')
       for button_rows in text_buttons:
           for text_button in button_rows:               
               self.configure_and_place_button(text_button, button_row, button_column)
               button_column += 1
           button_row += 1
           button_column = 0


Where configure_and_place_button() is the function that decorates the buttons:

def configure_and_place_button(self, text_button, button_row, button_column):
       key = list(self.calculator_buttons_texts.keys())[list(self.calculator_buttons_texts.values()).index(text_button)]
       self.calculator_buttons_texts[key] = tk.Button(self.master, text=text_button, bg="#5BC8AC", bd=12)
       self.calculator_buttons_texts[key].config(padx=33, pady=25, font=("Helvetica", 20, "bold"))
       self.calculator_buttons_texts[key].grid(row=button_row,column=button_column)
       self.reconfigure_specific_buttons(key, text_button)


Because all the buttons are designed the same way, we need to rely on an other function to to style the /, AC and = buttons:

def reconfigure_specific_buttons(self, key, text_button):
       self.reconfigure_clear_button(key, text_button)
       self.reconfigure_equals_button(key, text_button)     
       self.reconfigure_divide_button(key, text_button)


Stick the clear button to East and West

For the moment, there is an ugly empty space on the right of AC button:

This happened because you used sticky = W. To remedy to this problem, use sticky=tk.W+tk.E. This will result in:

Avoid spaces when coding tk.Class(options)'s operators

While spacing is a good practice, especially in Python, it should be avoided when writing a tkinter class/function options' operators (+ and =). I mean, something like this:

self.Equalbutton.grid(row = 5, column = 1, sticky = W, columnspan = 2)


Should be written this way:

self.Equalbutton.grid(row=5, column=1, sticky=W, columnspan=2)


Respect the naming conventions

I suggest you to take a look at PEP 8 to read about the naming conventions for variables and functions in Python. For example, the self.Equalbutton() just menioned above, should be written self.equal_button().

Improved GUI class code

Here is a Calculator_GUI() class. As I said in the opening paragraph, I am not incorporating any functionality to this GUI. You should create a different class to handle functions performing the different operations.

First, I create a module to save in some global ugly data:

calculator_buttons_texts ={
   'clear':'A',
   'zero':'0',
   'one':'1',
   'two':'2',
   'three':'3',
   'four':'4',
   'five':'5',
   'six':'6',
   'seven':'7',
   'eight':'8',
   'nine':'9',
   'plus':'+',
   'minus':'-',
   'multiply':'*',
   'divide':'/',
   'equal':'='           
}


It is better to keep this dirty data in a separate module in order not to pollute your class. This will also allow you to add addition buttons in the future if needed.

Here is the GUI class:

```
#!/usr/bin/env python
# -- coding: utf-8 --

import calculator_data
import tkinter as tk

class Calculator_Core:
def __init__(self):
# Do this yourself
pass

class Calculator(tk.Frame):

def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.calculator_buttons_texts = calculator_data.calculator_buttons_texts

self.configure_gui()
self.create_calculator_widgets()

def configure_gui(self):
self.master.title('Calculator')
self.master.resizable(False, False)

def create_calculator_widgets(self):
self.create_calculator_input_field()
self.create_calculator_buttons_texts()

def create_calculator_buttons_texts(self):
button_column = 0
button_row = 1
text_buttons = ('A', '789+', '456-', '123*', '0=/')
for button_rows in text_buttons:
for text_button in button_rows:
self.configure_and_place_button(text_button, button_row, button_column)
button_column += 1
button_row += 1
button_column = 0

Code Snippets

def create_calculator_buttons_texts(self):
       button_column = 0
       button_row = 1
       text_buttons = ('A', '789+', '456-', '123*', '0=/')
       for button_rows in text_buttons:
           for text_button in button_rows:               
               self.configure_and_place_button(text_button, button_row, button_column)
               button_column += 1
           button_row += 1
           button_column = 0
def configure_and_place_button(self, text_button, button_row, button_column):
       key = list(self.calculator_buttons_texts.keys())[list(self.calculator_buttons_texts.values()).index(text_button)]
       self.calculator_buttons_texts[key] = tk.Button(self.master, text=text_button, bg="#5BC8AC", bd=12)
       self.calculator_buttons_texts[key].config(padx=33, pady=25, font=("Helvetica", 20, "bold"))
       self.calculator_buttons_texts[key].grid(row=button_row,column=button_column)
       self.reconfigure_specific_buttons(key, text_button)
def reconfigure_specific_buttons(self, key, text_button):
       self.reconfigure_clear_button(key, text_button)
       self.reconfigure_equals_button(key, text_button)     
       self.reconfigure_divide_button(key, text_button)
self.Equalbutton.grid(row = 5, column = 1, sticky = W, columnspan = 2)
self.Equalbutton.grid(row=5, column=1, sticky=W, columnspan=2)

Context

StackExchange Code Review Q#161569, answer score: 2

Revisions (0)

No revisions yet.