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