├── .vscode ├── launch.json └── settings.json ├── README.md ├── calculator_sg.py ├── calculator_tk.py ├── calculator_trinket.py ├── comparison.png ├── sg_example.PNG └── tk_example.PNG /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Current File", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "C:\\Program Files\\Python37\\python.exe" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyDataMath-II 2 | A simple Python calculator gui application based loosely on the [Texas Instruments DataMath II](https://americanhistory.si.edu/collections/search/object/nmah_1364035) produced circa 1975. If you're looking for some experience with a GUI, or you want to add a project to your Python portfolio, this is a great place to start. 3 | 4 | I'm using a [Digital-7 font](https://www.1001fonts.com/digital+clock-fonts.html) on the display. Download and install if you want to use it, but it's optional. You can use another font if you choose, such as "OCR A Extended" which comes packaged with Windows. But, I think the Digital-7 font looks nice. Please keep in mind that the text field sizing is based on the selected font, so if you use another font-type, you may need to tweek the button sizes. 5 | 6 | ## PySimpleGui Version 7 | Requirements: PySimpleGUI `pip install pysimplegui` or `conda install pysimplegui` 8 | - Watch the [YouTube tutorial](https://youtu.be/x5LSTDdffFk) 9 | - Get the [code](calculator_sg.py) 10 | - See a live demo on [Trinket](https://israel-dryer.trinket.io/sites/py-datamath-ii) 11 | 12 | ![](sg_example.PNG) 13 | 14 | ## Tkinter Version 15 | Tkinter should already be installed with your python distribution. But if not, you can install with `pip install tkinter` 16 | - Watch the [YouTube tutorial]() <-- coming soon 17 | - Get the [code](calculator_tk.py) 18 | 19 | ![](tk_example.PNG) 20 | -------------------------------------------------------------------------------- /calculator_sg.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A simple Python calculator gui application based loosely on the Texas Instruments DataMath II 3 | produced circa 1975 4 | 5 | Author: Israel Dryer 6 | Email: israel.dryer@gmail.com 7 | Modified: 2019-10-07 8 | 9 | ''' 10 | import PySimpleGUI as sg 11 | 12 | ##-----DEFAULT SETTINGS----------------------------------## 13 | bw: dict = {'size':(7,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#F8F8F8")} 14 | bt: dict = {'size':(7,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#F1EABC")} 15 | bo: dict = {'size':(15,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#ECA527"), 'focus':True} 16 | 17 | ##-----WINDOW AND LAYOUT---------------------------------## 18 | layout: list = [ 19 | [sg.Text('PyDataMath-II', size=(50,1), justification='right', background_color="#272533", 20 | text_color='white', font=('Franklin Gothic Book', 14, 'bold'))], 21 | [sg.Text('0.0000', size=(18,1), justification='right', background_color='black', text_color='red', 22 | font=('Digital-7',48), relief='sunken', key="_DISPLAY_")], 23 | [sg.Button('C',**bt), sg.Button('CE',**bt), sg.Button('%',**bt), sg.Button("/",**bt)], 24 | [sg.Button('7',**bw), sg.Button('8',**bw), sg.Button('9',**bw), sg.Button("*",**bt)], 25 | [sg.Button('4',**bw), sg.Button('5',**bw), sg.Button('6',**bw), sg.Button("-",**bt)], 26 | [sg.Button('1',**bw), sg.Button('2',**bw), sg.Button('3',**bw), sg.Button("+",**bt)], 27 | [sg.Button('0',**bw), sg.Button('.',**bw), sg.Button('=',**bo, bind_return_key=True)] 28 | ] 29 | 30 | window: object = sg.Window('PyDataMath-II', layout=layout, background_color="#272533", size=(580, 660), return_keyboard_events=True) 31 | 32 | 33 | ##----CALCULATOR FUNCTIONS-------------------------------## 34 | var: dict = {'front':[], 'back':[], 'decimal':False, 'x_val':0.0, 'y_val':0.0, 'result':0.0, 'operator':''} 35 | 36 | #-----HELPER FUNCTIONS 37 | def format_number() -> float: 38 | ''' Create a consolidated string of numbers from front and back lists ''' 39 | return float(''.join(var['front']).replace(',','') + '.' + ''.join(var['back'])) 40 | 41 | 42 | def update_display(display_value: str): 43 | ''' Update the calculator display after an event click ''' 44 | try: 45 | window['_DISPLAY_'].update(value='{:,.4f}'.format(display_value)) 46 | except: 47 | window['_DISPLAY_'].update(value=display_value) 48 | 49 | 50 | #-----CLICK EVENTS 51 | def number_click(event: str): 52 | ''' Number button button click event ''' 53 | global var 54 | if var['decimal']: 55 | var['back'].append(event) 56 | else: 57 | var['front'].append(event) 58 | update_display(format_number()) 59 | 60 | 61 | def clear_click(): 62 | ''' CE or C button click event ''' 63 | global var 64 | var['front'].clear() 65 | var['back'].clear() 66 | var['decimal'] = False 67 | 68 | 69 | def operator_click(event: str): 70 | ''' + - / * button click event ''' 71 | global var 72 | var['operator'] = event 73 | try: 74 | var['x_val'] = format_number() 75 | except: 76 | var['x_val'] = var['result'] 77 | clear_click() 78 | 79 | 80 | def calculate_click(): 81 | ''' Equals button click event ''' 82 | global var 83 | try: 84 | var['y_val'] = format_number() 85 | except ValueError: # When Equals is pressed without any input 86 | var['x_val'] = var['result'] 87 | try: 88 | var['result'] = eval(str(var['x_val']) + var['operator'] + str(var['y_val'])) 89 | update_display(var['result']) 90 | clear_click() 91 | except: 92 | update_display("ERROR! DIV/0") 93 | clear_click() 94 | 95 | 96 | #-----MAIN EVENT LOOP------------------------------------## 97 | while True: 98 | event, values = window.read() 99 | print(event) 100 | if event is None: 101 | break 102 | if event in ['0','1','2','3','4','5','6','7','8','9']: 103 | number_click(event) 104 | if event in ['Escape:27','C','CE']: # 'Escape:27 for keyboard control 105 | clear_click() 106 | update_display(0.0) 107 | var['result'] = 0.0 108 | if event in ['+','-','*','/']: 109 | operator_click(event) 110 | if event == '=': 111 | calculate_click() 112 | if event == '.': 113 | var['decimal'] = True 114 | if event == '%': 115 | update_display(var['result'] / 100.0) 116 | -------------------------------------------------------------------------------- /calculator_tk.py: -------------------------------------------------------------------------------- 1 | # PyDataMath-II Calculator 2 | import tkinter as tk 3 | 4 | WHITE = "#F8F8F8" # black/white 5 | TAN = "#F1EABC" # black/tan 6 | var = {'front':[], 'back':[], 'decimal':False, 'x_val':0.0, 'y_val':0.0, 'result':0.0, 'operator':''} 7 | 8 | root = tk.Tk() 9 | root.title('PyDataMath-II') 10 | root.config(bg='#272533') 11 | 12 | display_text = tk.StringVar() 13 | display_text.set('0.0000') 14 | 15 | canvas = tk.Canvas(bg='#272533', bd=0, highlightthickness=0) 16 | canvas.pack(padx=15, pady=15) 17 | 18 | def std_btn(text, bg, row, col, width=7, height=2, font=('Franklin Gothic Book', 24)): 19 | btn = tk.Button(canvas, text=text, bg=bg, width=width, height=height, font=font, command=lambda: event_click(text)) 20 | return btn.grid(row=row, column=col, padx=4, pady=4) 21 | 22 | tk.Label(canvas, text='PyDataMath-II', anchor='e', bg='#272533', fg='white', font=('Franklin Gothic Book', 14, 'bold')).grid(row=0, columnspan=4, sticky='ew', padx=4, pady=2) 23 | tk.Label(canvas, textvariable=display_text, anchor='e', bg='black', fg='red', font=('Digital-7',47)).grid(row=1, columnspan=4, sticky='ew', padx=4, pady=2) 24 | std_btn("C", TAN, 2, 0), std_btn("CE", TAN, 2, 1), std_btn("%", TAN, 2, 2), std_btn("/", TAN, 2, 3) 25 | std_btn("7", WHITE, 3, 0), std_btn("8", WHITE, 3, 1), std_btn("9", WHITE, 3, 2), std_btn("*", TAN, 3, 3) 26 | std_btn("4", WHITE, 4, 0), std_btn("5", WHITE, 4, 1), std_btn("6", WHITE, 4, 2), std_btn("-", TAN, 4, 3) 27 | std_btn("1", WHITE, 5, 0), std_btn("2", WHITE, 5, 1), std_btn("3", WHITE, 5, 2), std_btn("+", TAN, 5, 3) 28 | std_btn("0", WHITE, 6, 0), std_btn(".", WHITE, 6, 1) 29 | 30 | rtn_btn = tk.Button(canvas, text='=', bg='#ECA527', width=15, height=2, font=('Franklin Gothic Book', 24), command=lambda: event_click("=")) 31 | rtn_btn.focus() 32 | rtn_btn.grid(row=6, column=2, columnspan=2, padx=4, pady=4) 33 | 34 | # calculator functions 35 | def event_click(event): 36 | global var 37 | if event in ['CE','C']: 38 | clear_click() 39 | update_display(0.0) 40 | var['operator'] = '' 41 | var['result'] = 0.0 42 | if event in ['0','1','2','3','4','5','6','7','8','9']: 43 | number_click(event) 44 | if event in ['*','/','+','-']: 45 | operator_click(event) 46 | if event == '.': 47 | var['decimal'] = True 48 | if event == '%': 49 | update_display(var['result'] / 100.0) 50 | if event == '=': 51 | calculate_click() 52 | 53 | # helper functions 54 | def update_display(display_value): 55 | global display_text 56 | ''' update the calc display with number click events, update with results, and update with error messages ''' 57 | try: # to display float number 58 | display_text.set('{:,.4f}'.format(display_value)) 59 | except: # to display error message 60 | display_text.set(display_value) 61 | 62 | def format_number(): 63 | ''' create a consolidated string of numbers from front and back number lists ''' 64 | return ''.join(var['front']) + '.' + ''.join(var['back']) 65 | 66 | # click events 67 | def number_click(event): 68 | ''' add digit to front or back list when clicked ''' 69 | global var 70 | if var['decimal']: 71 | var['back'].append(event) 72 | else: 73 | var['front'].append(event) 74 | 75 | display_value = float(format_number()) 76 | update_display(display_value) 77 | 78 | def clear_click(): 79 | ''' clear contents of front and back list, reset display, and reset decimal flag ''' 80 | global var 81 | var['front'].clear() 82 | var['back'].clear() 83 | var['decimal'] = False 84 | 85 | def operator_click(event): 86 | ''' set the operator based on the event button, this may also trigger a calculation in the event 87 | that the result is used in a subsequent operation ''' 88 | global var 89 | var['operator'] = event 90 | try: # if no x_val exists, use prior result 91 | var['x_val'] = float(format_number()) 92 | except: 93 | var['x_val'] = var['result'] 94 | clear_click() 95 | 96 | def calculate_click(): 97 | ''' attempt to perform operation on x and y variables if exist ''' 98 | global var 99 | # check for x value 100 | if not var['x_val']: 101 | return 102 | try: # check for y value 103 | var['y_val'] = float(format_number()) 104 | except: 105 | var['y_val'] = 0.0 106 | try: # check for division by zero 107 | var['result'] = float(eval(str(var['x_val']) + var['operator'] + str(var['y_val']))) 108 | update_display(var['result']) 109 | except ZeroDivisionError: 110 | error = "ERROR! DIV/0" 111 | var['x_val'] = 0.0 112 | clear_click() 113 | update_display(error) 114 | 115 | clear_click() 116 | 117 | root.mainloop() -------------------------------------------------------------------------------- /calculator_trinket.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A simple Python calculator gui application based loosely on the Texas Instruments DataMath II 3 | produced circa 1975 4 | 5 | Author: Israel Dryer 6 | Email: israel.dryer@gmail.com 7 | Modified: 2019-10-07 8 | 9 | ''' 10 | import PySimpleGUI as sg 11 | 12 | # default settings 13 | bw: dict = {'size':(7,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#F8F8F8")} 14 | bt: dict = {'size':(7,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#F1EABC")} 15 | bo: dict = {'size':(16,2), 'font':('Franklin Gothic Book', 24), 'button_color':("black","#ECA527"), 'focus':True} 16 | 17 | layout: list = [ 18 | [sg.Text('PyDataMath-II', size=(50,1), justification='right', background_color="#272533", 19 | text_color='white', font=('Franklin Gothic Book', 14, 'bold'))], 20 | [sg.Text('0.0000', size=(16,1), justification='right', background_color='black', text_color='red', 21 | font=('Digital-7',51), relief='sunken', key="_DISPLAY_")], 22 | [sg.Button('C',**bt), sg.Button('CE',**bt), sg.Button('%',**bt), sg.Button("/",**bt)], 23 | [sg.Button('7',**bw), sg.Button('8',**bw), sg.Button('9',**bw), sg.Button("*",**bt)], 24 | [sg.Button('4',**bw), sg.Button('5',**bw), sg.Button('6',**bw), sg.Button("-",**bt)], 25 | [sg.Button('1',**bw), sg.Button('2',**bw), sg.Button('3',**bw), sg.Button("+",**bt)], 26 | [sg.Button('0',**bw), sg.Button('.',**bw), sg.Button('=',**bo, bind_return_key=True)], 27 | ] 28 | 29 | window: object = sg.Window('PyDataMath-II', layout=layout, element_justification='right', margins=(10,20), background_color="#272533", return_keyboard_events=True) 30 | 31 | ##----CALCULATOR FUNCTIONS-------------------------------## 32 | var: dict = {'front':[], 'back':[], 'decimal':False, 'x_val':0.0, 'y_val':0.0, 'result':0.0, 'operator':''} 33 | 34 | #-----HELPER FUNCTIONS 35 | def format_number() -> float: 36 | ''' Create a consolidated string of numbers from front and back lists ''' 37 | return float(''.join(var['front']) + '.' + ''.join(var['back'])) 38 | 39 | 40 | def update_display(display_value: str): 41 | ''' Update the calculator display after an event click ''' 42 | try: 43 | window['_DISPLAY_'].update(value='{:,.4f}'.format(display_value)) 44 | except: 45 | window['_DISPLAY_'].update(value=display_value) 46 | 47 | 48 | #-----CLICK EVENTS 49 | def number_click(event: str): 50 | ''' Number button button click event ''' 51 | global var 52 | if var['decimal']: 53 | var['back'].append(event) 54 | else: 55 | var['front'].append(event) 56 | update_display(format_number()) 57 | 58 | 59 | def clear_click(): 60 | ''' CE or C button click event ''' 61 | global var 62 | var['front'].clear() 63 | var['back'].clear() 64 | var['decimal'] = False 65 | 66 | 67 | def operator_click(event: str): 68 | ''' + - / * button click event ''' 69 | global var 70 | var['operator'] = event 71 | try: 72 | var['x_val'] = format_number() 73 | except: 74 | var['x_val'] = var['result'] 75 | clear_click() 76 | 77 | 78 | def calculate_click(): 79 | ''' Equals button click event ''' 80 | global var 81 | var['y_val'] = format_number() 82 | try: 83 | var['result'] = eval(str(var['x_val']) + var['operator'] + str(var['y_val'])) 84 | update_display(var['result']) 85 | clear_click() 86 | except: 87 | update_display("ERROR! DIV/0") 88 | clear_click() 89 | 90 | ##-----MAIN EVENT LOOP-----------------------------------## 91 | while True: 92 | event, values = window.read() 93 | print(event) 94 | if event is None: 95 | break 96 | if event in ['0','1','2','3','4','5','6','7','8','9']: 97 | number_click(event) 98 | if event in ['Escape:27','C','CE']: # 'Escape:27 for keyboard control 99 | clear_click() 100 | update_display(0.0) 101 | var['result'] = 0.0 102 | if event in ['+','-','*','/']: 103 | operator_click(event) 104 | if event == '=': 105 | calculate_click() 106 | if event == '.': 107 | var['decimal'] = True 108 | if event == '%': 109 | update_display(var['result'] / 100.0) -------------------------------------------------------------------------------- /comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/israel-dryer/PyDataMath-II/b4fe9c1a17ad2f841a58a8861ff658415b98ae61/comparison.png -------------------------------------------------------------------------------- /sg_example.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/israel-dryer/PyDataMath-II/b4fe9c1a17ad2f841a58a8861ff658415b98ae61/sg_example.PNG -------------------------------------------------------------------------------- /tk_example.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/israel-dryer/PyDataMath-II/b4fe9c1a17ad2f841a58a8861ff658415b98ae61/tk_example.PNG --------------------------------------------------------------------------------