Skip to content

Commit d491399

Browse files
committed
add calculator app tutorial
1 parent ad2bd2a commit d491399

File tree

5 files changed

+201
-0
lines changed

5 files changed

+201
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,5 +214,6 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
214214
- [How to Make a Button using PyGame in Python](https://www.thepythoncode.com/article/make-a-button-using-pygame-in-python). ([code](gui-programming/button-in-pygame))
215215
- [How to Make a Drawing Program in Python](https://www.thepythoncode.com/article/make-a-drawing-program-with-python). ([code](gui-programming/drawing-tool-in-pygame))
216216
- [How to Make a File Explorer using Tkinter in Python](https://www.thepythoncode.com/article/create-a-simple-file-explorer-using-tkinter-in-python). ([code](gui-programming/file-explorer))
217+
- [How to Make a Calculator with Tkinter in Python](https://www.thepythoncode.com/article/make-a-calculator-app-using-tkinter-in-python). ([code](gui-programming/calculator-app))
217218

218219
For any feedback, please consider pulling requests.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# [How to Make a Calculator with Tkinter in Python](https://www.thepythoncode.com/article/make-a-calculator-app-using-tkinter-in-python)
2+
You can get the original version of the calculator [here](https://github.com/Maximinodotpy/calculator).
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
from tkinter import *
2+
import tkinter.font as font
3+
from functools import partial
4+
import ctypes
5+
import json
6+
import re
7+
8+
# so the functions can be used from the math module can be used in the lineedit.
9+
import math
10+
11+
ctypes.windll.shcore.SetProcessDpiAwareness(1)
12+
13+
# Colors
14+
buttonColor = (255, 255, 255)
15+
historyPanelBackground = (255, 255, 255)
16+
17+
# Tkinter Setup
18+
root = Tk()
19+
root.geometry("550x270")
20+
root.title("Calculator")
21+
22+
# Setting icon for the Application
23+
photo = PhotoImage(file = "icon.png")
24+
root.iconphoto(False, photo)
25+
26+
# Loading Font from font name
27+
myFont = font.Font(family='Consolas', size=12)
28+
29+
# Formula Templates
30+
formulas = [
31+
['Pythagoras->c', '(({a}**2)+({b}**2))**0.5 ? a=5 & b=5'],
32+
['Pythagoras->c**2', '({a}**2)+({b}**2) ? a=5 & b=5'],
33+
['pq->(x1, x2)', '-({p}/2) + sqrt(({p}/2)**2 - ({q})), -({p}/2) - sqrt(({p}/2)**2 - ({q})) ? p=-1 & q=-12'],
34+
['abc->(x1, x2)', 'quadratic_formula({a}, {b}, {c}) ? a=1 & b=5 & c=6'],
35+
['Incline->y', '{m}*{x} + {q} ? m=4 & x=5 & q=6'],
36+
]
37+
38+
# All the history equations are in this list.
39+
history = []
40+
# Where the history file is located.
41+
historyFilePath = 'history.json'
42+
print("Reading history from:", historyFilePath)
43+
# Creating History file if it does not exist.
44+
try:
45+
with open(historyFilePath, 'x') as fp:
46+
pass
47+
print("Created file at:", historyFilePath)
48+
except:
49+
print('File already exists')
50+
51+
# converting RGB values to HEX
52+
def rgb_to_hex(rgb):
53+
return "#%02x%02x%02x" % rgb
54+
55+
# Add something to the current calculation
56+
def addSymbol(event=None, symbol=None):
57+
58+
if symbol == '<':
59+
entryVariable.set(entryVariable.get()[:-1])
60+
else:
61+
entryVariable.set(entryVariable.get()+symbol)
62+
63+
def varChange(*args):
64+
evaluationString = entryVariable.get().replace(' ', '').split('?')[0]
65+
66+
print('Before insertion: ',evaluationString)
67+
68+
if len(entryVariable.get().split('?')) == 2:
69+
70+
parameters = entryVariable.get().replace(' ', '').split('?')[1]
71+
72+
for param in parameters.split('&'):
73+
where, what = param.split('=')
74+
evaluationString = re.sub('{'+where+'}', what, evaluationString)
75+
76+
try:
77+
print('After insertion: ', evaluationString)
78+
resultLabel.config(text=str(eval(evaluationString)))
79+
except:
80+
resultLabel.config(text='Invalid Input')
81+
82+
def saveCurrentInputToHistory(event=None):
83+
if entryVariable.get() in history:
84+
return
85+
86+
history.append(entryVariable.get())
87+
88+
with open(historyFilePath, 'w') as file:
89+
file.write(json.dumps(history))
90+
91+
updateListBox()
92+
93+
def updateListBox(event=None):
94+
global history
95+
96+
historyList.delete(0, END)
97+
98+
try:
99+
with open(historyFilePath, 'r') as file:
100+
history = json.loads(file.read())
101+
except json.decoder.JSONDecodeError:
102+
print('File does not contain JSON')
103+
104+
for index, item in enumerate(history):
105+
historyList.insert(index, item)
106+
107+
def setEntryFromHistory(event=None):
108+
historyItem = historyList.get(historyList.curselection()[0])
109+
entryVariable.set(historyItem)
110+
111+
def addFormula(formula=''):
112+
saveCurrentInputToHistory()
113+
entryVariable.set(formula)
114+
115+
def quadratic_formula(a, b, c):
116+
117+
disc = b**2 - 4 * a * c
118+
119+
x1 = (-b - math.sqrt(disc)) / (2 * a)
120+
x2 = (-b + math.sqrt(disc)) / (2 * a)
121+
122+
return(x1, x2)
123+
124+
# Work with Frames to split the window in two parts: the calculator and the History Panel.
125+
126+
# Calculation Panel
127+
calcSide = Frame(root)
128+
calcSide.pack(side=LEFT, fill=BOTH, expand=1)
129+
130+
# Entry Variable for the calculations
131+
entryVariable = StringVar(root, '4/2**2')
132+
entryVariable.trace('w', varChange)
133+
134+
Entry(calcSide, textvariable=entryVariable, font=myFont, borderwidth=0).pack(fill=X, ipady=10, ipadx=10)
135+
resultLabel = Label(calcSide, text='Result', font=myFont, borderwidth=0,anchor="e")
136+
resultLabel.pack(fill=X, ipady=10)
137+
138+
# History Panel
139+
historySide = Frame(root, bg=rgb_to_hex(historyPanelBackground))
140+
historySide.pack(side=LEFT, fill=BOTH, expand=1)
141+
142+
historyTopBar = Frame(historySide)
143+
historyTopBar.pack(fill=X)
144+
Label(historyTopBar, text='History').pack(side=LEFT)
145+
Button(historyTopBar, text='Save Current Input', bg=rgb_to_hex(buttonColor), borderwidth=0, command=saveCurrentInputToHistory).pack(side=RIGHT)
146+
147+
historyList = Listbox(historySide, borderwidth=0)
148+
historyList.pack(fill=BOTH, expand=True)
149+
historyList.bind("<Double-Button-1>", setEntryFromHistory)
150+
151+
# Insert stuff into the history
152+
updateListBox()
153+
154+
# Button Symbols (and their position)
155+
symbols = [
156+
['1', '2', '3', '+'],
157+
['4', '5', '6', '-'],
158+
['7', '8', '9', '/'],
159+
['0', '.', '<', '*'],
160+
]
161+
162+
for rowList in symbols:
163+
# Make a row
164+
row = Frame(calcSide)
165+
row.pack(fill=BOTH, expand=True)
166+
for symbol in rowList:
167+
# Making and packing the Button
168+
Button(
169+
row, text=symbol, command=partial(addSymbol, symbol=symbol),
170+
font=myFont, bg=rgb_to_hex(buttonColor), borderwidth=0) \
171+
.pack(side=LEFT, fill=BOTH, expand=1)
172+
# Change button color each iteration for gradient.
173+
buttonColor = (buttonColor[0] - 10, buttonColor[1] - 10, buttonColor[1] - 2)
174+
175+
176+
menubar = Menu(root)
177+
178+
filemenu = Menu(menubar, tearoff=0)
179+
180+
# Add all Formulas to the dropdown menu.
181+
for formula in formulas:
182+
filemenu.add_command(label=formula[0], command=partial(addFormula, formula[1]))
183+
184+
filemenu.add_separator()
185+
186+
# Quit command
187+
filemenu.add_command(label="Exit", command=root.quit)
188+
189+
menubar.add_cascade(menu=filemenu, label='Formulas')
190+
191+
root.config(menu=menubar)
192+
193+
194+
# Call the var change once so it is evaluated withhout actual change.
195+
varChange('foo')
196+
197+
root.mainloop()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["4/2**2", "4/2**3", "{a} ? a = 6", "", "-({p}/2) + sqrt( ({p}/2) - {q}) ? p=10 & q=5", "0", "({a}**2)+({b}**2) ? a=5 & b=5", "2+3/2*2-4", "(({a}**2)+({b}**2))**0.5 ? a=1 & b=7", "quadratic_formula({a}, {b}, {c}) ? a=1 & b=5 & c=6", "2-1/8*55", " {a} + 1 ? a=14"]
1.14 KB
Loading

0 commit comments

Comments
 (0)