File: inca.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 # inca.pyw 27 # 28 # INteger-input CAlculator is a GUI app which live-evaluates the python 29 # expression currently in its single-line input, re-running it as it changes 30 # for each value in the integer range defined by 2 more expressions. 31 32 33 import math 34 from math import \ 35 acos, acosh, asin, asinh, atan, atan2, atanh, ceil, comb, \ 36 copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, expm1, \ 37 fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, \ 38 isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, \ 39 log10, log1p, log2, modf, nan, nextafter, perm, pi, pow, prod, \ 40 radians, remainder, sin, sinh, sqrt, tan, tanh, tau, trunc, ulp 41 try: 42 from math import cbrt, exp2 43 except: 44 pass 45 46 from random import \ 47 betavariate, choice, choices, expovariate, gammavariate, gauss, \ 48 getrandbits, getstate, lognormvariate, normalvariate, paretovariate, \ 49 randbytes, randint, random, randrange, sample, seed, setstate, \ 50 shuffle, triangular, uniform, vonmisesvariate, weibullvariate 51 52 from statistics import \ 53 bisect_left, bisect_right, fmean, \ 54 geometric_mean, harmonic_mean, mean, median, \ 55 median_grouped, median_high, median_low, mode, multimode, pstdev, \ 56 pvariance, quantiles, stdev, variance 57 try: 58 from statistics import \ 59 correlation, covariance, linear_regression, mul, reduce 60 except: 61 pass 62 63 from tkinter.ttk import Treeview 64 from tkinter.messagebox import showerror 65 from tkinter import Button, Entry, Frame, Label, StringVar, Tk, END, LEFT 66 67 68 # some convenience aliases to various funcs from the python stdlib 69 geomean = geometric_mean 70 harmean = harmonic_mean 71 sd = stdev 72 popsd = pstdev 73 var = variance 74 popvar = pvariance 75 randbeta = betavariate 76 randexp = expovariate 77 randgamma = gammavariate 78 randlognorm = lognormvariate 79 randnorm = normalvariate 80 randweibull = weibullvariate 81 82 # some occasionally-useful values 83 kb = 1024 84 mb = 1024 * kb 85 gb = 1024 * mb 86 tb = 1024 * gb 87 pb = 1024 * tb 88 89 # quick guide for the treeview-specific api 90 # https://riptutorial.com/tkinter/example/31880/treeview--basic-example 91 92 93 def update() -> None: 94 '''Evaluates the current formula, then updates the bottom table''' 95 96 # try to get the range of input values 97 try: 98 start = int(eval(fromnum.get())) 99 stop = int(eval(tonum.get())) 100 except: 101 return 102 103 # clear the output 104 for c in out.get_children(): 105 out.delete(c) 106 107 # update output rows 108 j = 0 # the actual row-insertion index 109 src = inp.get() 110 111 for i, n in enumerate(range(start, stop + 1)): 112 if i >= 1000: 113 # avoid hanging the GUI, so quit after calculating 1000 rows 114 break 115 116 # visually-separate groups of 5 rows by adding an empty one 117 if i > 0 and i % 5 == 0: 118 out.insert('', j, text='') 119 j += 1 120 121 # calculate formula-result for the current value 122 try: 123 x = n # allow x in formulas, as an alias for n 124 y = eval(src) 125 except Exception as e: 126 val = [f'{n}', f'{e}'] 127 out.insert('', j, text=f'{i + 1}', values=val) 128 129 # make it obvious an error just occurred and return 130 inp.config(bg='darkred', fg='white') 131 out.heading('result', text='f(n)') 132 return 133 134 # insert row with the current result 135 val = [f'{n}', f'{y:,.6f}'] 136 out.insert('', j, text=f'{i + 1}', values=val) 137 j += 1 138 139 out.heading('result', text=src) 140 # reset style to indicate success 141 inp.config(bg='white', fg='black') 142 143 144 try: 145 win = Tk() 146 win.title('INteger-input CAlculator') 147 win.bind('<Escape>', lambda _: win.quit()) 148 149 # top of window: formula and integer-range inputs 150 top = Frame(win) 151 Label(top, text='f(n) = ', font=20).grid(row=0, column=0, padx=0) 152 inp = Entry(top, text=StringVar(value='n'), width=30, borderwidth=1) 153 inp.select_range(0, END) 154 inp.grid(row=0, column=1, padx=0) 155 inp.bind('<KeyRelease>', lambda _: update()) 156 inp.focus_set() 157 Label(top, text='n = ', font=20).grid(row=0, column=2, padx=10) 158 fromnum = Entry(top, text=StringVar(value=1), width=6, borderwidth=1) 159 fromnum.grid(row=0, column=3, padx=2, sticky='e') 160 fromnum.bind('<KeyRelease>', lambda _: update()) 161 Label(top, text=' .. ', font=20).grid(row=0, column=4, padx=0) 162 tonum = Entry(top, text=StringVar(value=40), width=6, borderwidth=1) 163 tonum.grid(row=0, column=5, padx=2, sticky='e') 164 tonum.bind('<KeyRelease>', lambda _: update()) 165 top.pack(padx=1, pady=1) 166 167 # bottom of window only has results table 168 bottom = Frame(win) 169 out = Treeview(bottom, column=['n', 'result'], height=48) 170 out.heading('#0', text='#') 171 out.column('#0', width=80) 172 out.heading('n', text='n') 173 out.column('n', width=80, anchor='e') 174 out.heading('result', text='f(n)') 175 out.column('result', width=360, anchor='e') 176 out.pack() 177 bottom.pack(side=LEFT, padx=1, pady=1) 178 179 # show results and run gui 180 update() 181 win.mainloop() 182 except Exception as e: 183 showerror('Error', str(e)) 184 win.quit()