File: pycalc.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 # pycalc.pyw
  27 #
  28 # PYthon CALCulator is a GUI app which live-evaluates the python expression
  29 # currently in its single-line input, re-running it as it changes.
  30 
  31 
  32 from json import dumps
  33 
  34 import math
  35 from math import \
  36     acos, acosh, asin, asinh, atan, atan2, atanh, ceil, comb, \
  37     copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, expm1, \
  38     fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, \
  39     isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, \
  40     log10, log1p, log2, modf, nan, nextafter, perm, pi, pow, prod, \
  41     radians, remainder, sin, sinh, sqrt, tan, tanh, tau, trunc, ulp
  42 try:
  43     from math import cbrt, exp2
  44 except Exception:
  45     pass
  46 
  47 from random import \
  48     betavariate, choice, choices, expovariate, gammavariate, gauss, \
  49     getrandbits, getstate, lognormvariate, normalvariate, paretovariate, \
  50     randbytes, randint, random, randrange, sample, seed, setstate, \
  51     shuffle, triangular, uniform, vonmisesvariate, weibullvariate
  52 
  53 from statistics import \
  54     bisect_left, bisect_right, fmean, \
  55     geometric_mean, harmonic_mean, mean, median, \
  56     median_grouped, median_high, median_low, mode, multimode, pstdev, \
  57     pvariance, quantiles, stdev, variance
  58 try:
  59     from statistics import \
  60         correlation, covariance, linear_regression, mul, reduce
  61 except Exception:
  62     pass
  63 
  64 from tkinter import Tk, Entry, Label, RIGHT, LEFT, EventType
  65 from typing import Callable, Iterable, Any
  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 mole = 602214076000000000000000
  89 mol = mole
  90 
  91 
  92 def check_shortcuts(event: EventType) -> None:
  93     char = event.char
  94     if char == '':
  95         return
  96 
  97     # quit when esc key is pressed
  98     if ord(char) == 27:
  99         win.quit()
 100 
 101 
 102 def update_result(event: EventType) -> None:
 103     try:
 104         expr = input.get()
 105         if expr == '':
 106             return
 107 
 108         # square brackets are easier to type and are valid math
 109         expr = expr.replace('[', '(').replace(']', ')')
 110         res = eval(expr)
 111         if isinstance(res, int) or isinstance(res, float):
 112             output['text'] = '= {:,}'.format(res)
 113         else:
 114             output['text'] = res
 115     except Exception as e:
 116         output['text'] = f'= {e}'
 117 
 118 
 119 win = Tk()
 120 win.title('Calculate')
 121 input = Entry(font=20, width=35)
 122 input.pack(side=LEFT, padx=5)
 123 input.bind('<KeyPress>', check_shortcuts)
 124 input.bind('<KeyRelease>', update_result)
 125 input.focus()
 126 output = Label(text='esc quits', font=20)
 127 output.pack(side=RIGHT, padx=10, pady=5)
 128 win.mainloop()