File: quac.pyw 1 #!/usr/bin/python3 2 3 # The MIT License (MIT) 4 # 5 # Copyright © 2024 pacman64 6 # 7 # Permission is hereby granted, free of charge, to any person obtaining a copy 8 # of this software and associated documentation files (the “Software”), to deal 9 # in the Software without restriction, including without limitation the rights 10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 # copies of the Software, and to permit persons to whom the Software is 12 # furnished to do so, subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be included in 15 # all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # SOFTWARE. 24 25 26 # quac.pyw 27 # 28 # Quick Adder/Calculator (QuAC) does what it says with a GUI. 29 30 31 import math 32 from math import \ 33 acos, acosh, asin, asinh, atan, atan2, atanh, ceil, comb, \ 34 copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, expm1, \ 35 fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, \ 36 isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, \ 37 log10, log1p, log2, modf, nan, nextafter, perm, pi, pow, prod, \ 38 radians, remainder, sin, sinh, sqrt, tan, tanh, tau, trunc, ulp 39 try: 40 from math import cbrt, exp2 41 except: 42 pass 43 44 from random import \ 45 betavariate, choice, choices, expovariate, gammavariate, gauss, \ 46 getrandbits, getstate, lognormvariate, normalvariate, paretovariate, \ 47 randbytes, randint, random, randrange, sample, seed, setstate, \ 48 shuffle, triangular, uniform, vonmisesvariate, weibullvariate 49 50 from statistics import \ 51 bisect_left, bisect_right, fmean, \ 52 geometric_mean, harmonic_mean, mean, median, \ 53 median_grouped, median_high, median_low, mode, multimode, pstdev, \ 54 pvariance, quantiles, stdev, variance 55 try: 56 from statistics import \ 57 correlation, covariance, linear_regression, mul, reduce 58 except: 59 pass 60 61 from tkinter import Tk, Button, Entry, Frame, Label, END, EventType 62 from tkinter.messagebox import showerror 63 from tkinter.ttk import Treeview 64 65 66 # some convenience aliases to various funcs from the python stdlib 67 geomean = geometric_mean 68 harmean = harmonic_mean 69 sd = stdev 70 popsd = pstdev 71 var = variance 72 popvar = pvariance 73 randbeta = betavariate 74 randexp = expovariate 75 randgamma = gammavariate 76 randlognorm = lognormvariate 77 randnorm = normalvariate 78 randweibull = weibullvariate 79 80 # some occasionally-useful values 81 kb = 1024 82 mb = 1024 * kb 83 gb = 1024 * mb 84 tb = 1024 * gb 85 pb = 1024 * tb 86 87 88 # results is a list of formula-result pairs 89 results = [] 90 91 92 # quick guide for the treeview-specific api 93 # https://riptutorial.com/tkinter/example/31880/treeview--basic-example 94 95 96 def update() -> None: 97 '''Evaluates the current value/formula, then updates the bottom table''' 98 99 s = inp.get().strip() 100 if s == '': 101 # ignore empty(ish) inputs 102 return 103 104 try: 105 v = float(eval(s)) 106 inp.config(bg='white', fg='black') 107 results.append((s, v)) 108 except: 109 inp.config(bg='darkred', fg='white') 110 # clear the output 111 for c in out.get_children(): 112 out.delete(c) 113 114 total = 0 115 for e in results: 116 total += float(e[1]) 117 win.title(f'Σ ≈ {total:,.4f} (QuAC)') 118 119 # update output rows 120 j = 0 # the actual row-insertion index 121 for i, e in enumerate(results): 122 # visually-separate groups of 5 rows by adding an empty one 123 if i > 0 and i % 5 == 0: 124 out.insert('', j, text='') 125 j += 1 126 val = [e[0], f'{e[1]:,.6f}'] 127 out.insert('', j, text=str(j+1), values=val) 128 j += 1 129 130 131 def check_shortcuts(event: EventType) -> None: 132 char = event.char 133 if char == '': 134 return 135 # copy formula and result to clipboard when enter is pressed 136 if ord(char) == 13: 137 update() 138 # quit when esc key is pressed 139 if ord(char) == 27: 140 win.quit() 141 142 143 def clear() -> None: 144 global results 145 results = [] 146 # clear the output 147 for c in out.get_children(): 148 out.delete(c) 149 # auto-focus on formula input 150 inp.select_range(0, END) 151 inp.focus_set() 152 # reset title too 153 win.title('Quick Adder/Calculator (QuAC)') 154 155 156 try: 157 win = Tk() 158 win.title('Quick Adder/Calculator (QuAC)') 159 win.bind('<Escape>', lambda _: win.quit()) 160 161 # top of window: formula-input and button to clear results 162 top = Frame(win) 163 top.pack() 164 Label(top, text='Number or Formula').grid(row=0, column=0, padx=12) 165 inp = Entry(top, text='', width=28, borderwidth=1) 166 inp.grid(row=0, column=1, padx=0) 167 inp.bind('<KeyPress>', check_shortcuts) 168 inp.select_range(0, END) 169 inp.focus_set() 170 cl = Button(top, text='Clear', font=8, command=clear) 171 cl.grid(row=0, column=2, padx=0, sticky='e') 172 cl.bind('<KeyPress>', lambda _: clear()) 173 174 # bottom of window only has the formula-results table 175 bottom = Frame(win) 176 bottom.pack(padx=0) 177 out = Treeview(bottom, column=['formula', 'result'], height=36) 178 out.heading('#0', text='#') 179 out.column('#0', width=40) 180 out.heading('formula', text='formula') 181 out.column('formula', width=200, anchor='e') 182 out.heading('result', text='result') 183 out.column('result', width=240, anchor='e') 184 out.pack() 185 186 # run gui 187 win.mainloop() 188 except Exception as e: 189 showerror('Error', str(e)) 190 win.quit()