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 Exception: 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 Exception: 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 mole = 602214076000000000000000 87 mol = mole 88 89 90 # results is a list of formula-result pairs 91 results = [] 92 93 94 # quick guide for the treeview-specific api 95 # https://riptutorial.com/tkinter/example/31880/treeview--basic-example 96 97 98 def update() -> None: 99 'Evaluates the current value/formula, then updates the bottom table.' 100 101 s = inp.get().strip() 102 if s == '': 103 # ignore empty(ish) inputs 104 return 105 106 try: 107 v = float(eval(s)) 108 inp.config(bg='white', fg='black') 109 results.append((s, v)) 110 except Exception: 111 inp.config(bg='darkred', fg='white') 112 # clear the output 113 for c in out.get_children(): 114 out.delete(c) 115 116 total = 0 117 for e in results: 118 total += float(e[1]) 119 win.title(f'Σ ≈ {total:,.4f} (QuAC)') 120 121 # update output rows 122 j = 0 # the actual row-insertion index 123 for i, e in enumerate(results): 124 # visually-separate groups of 5 rows by adding an empty one 125 if i > 0 and i % 5 == 0: 126 out.insert('', j, text='') 127 j += 1 128 val = [e[0], f'{e[1]:,.6f}'] 129 out.insert('', j, text=str(j+1), values=val) 130 j += 1 131 132 133 def check_shortcuts(event: EventType) -> None: 134 char = event.char 135 if char == '': 136 return 137 # copy formula and result to clipboard when enter is pressed 138 if ord(char) == 13: 139 update() 140 # quit when esc key is pressed 141 if ord(char) == 27: 142 win.quit() 143 144 145 def clear() -> None: 146 global results 147 results = [] 148 # clear the output 149 for c in out.get_children(): 150 out.delete(c) 151 # auto-focus on formula input 152 inp.select_range(0, END) 153 inp.focus_set() 154 # reset title too 155 win.title('Quick Adder/Calculator (QuAC)') 156 157 158 try: 159 win = Tk() 160 win.title('Quick Adder/Calculator (QuAC)') 161 win.bind('<Escape>', lambda _: win.quit()) 162 163 # top of window: formula-input and button to clear results 164 top = Frame(win) 165 top.pack() 166 Label(top, text='Number or Formula').grid(row=0, column=0, padx=12) 167 inp = Entry(top, text='', width=28, borderwidth=1) 168 inp.grid(row=0, column=1, padx=0) 169 inp.bind('<KeyPress>', check_shortcuts) 170 inp.select_range(0, END) 171 inp.focus_set() 172 cl = Button(top, text='Clear', font=8, command=clear) 173 cl.grid(row=0, column=2, padx=0, sticky='e') 174 cl.bind('<KeyPress>', lambda _: clear()) 175 176 # bottom of window only has the formula-results table 177 bottom = Frame(win) 178 bottom.pack(padx=0) 179 out = Treeview(bottom, column=['formula', 'result'], height=36) 180 out.heading('#0', text='#') 181 out.column('#0', width=40) 182 out.heading('formula', text='formula') 183 out.column('formula', width=200, anchor='e') 184 out.heading('result', text='result') 185 out.column('result', width=240, anchor='e') 186 out.pack() 187 188 # run gui 189 win.mainloop() 190 except Exception as e: 191 showerror('Error', str(e)) 192 win.quit()