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 ()
0 commit comments