|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +# Python calculator for conversion to SI units |
| 4 | + |
| 5 | +import pygame |
| 6 | +from math import * |
| 7 | + |
| 8 | +BACKGROUND = 255, 255, 255 |
| 9 | +BORDER = 2 |
| 10 | +RES = 600,495 # initial window size |
| 11 | +HSIZE, VSIZE = 6, 9 # button grid dimension (top row is for display) |
| 12 | +MAXLEN = 100 # maximum digits |
| 13 | + |
| 14 | +# font name |
| 15 | +fn = "fsex2.ttf" |
| 16 | + |
| 17 | +us_units = ( |
| 18 | +("_floz", "*0.0295735295625"), # l |
| 19 | +("_pt", "*0.473176473"), # l |
| 20 | +("_qt", "*0.946352946"), # l |
| 21 | +("_gal", "*3.785411784"), # l |
| 22 | +("_nm", "*1852"), # m |
| 23 | +("_in", "*0.0254"), # m |
| 24 | +("_ft", "*0.3048"), # m |
| 25 | +("_sqft", "*0.09290304"), # m² |
| 26 | +("_acre", "*4046.8564224"), # m² |
| 27 | +("_yd", "*0.9144"), # m |
| 28 | +("_mi", "*1609.344"), # m |
| 29 | +("_lb", "*0.45359237"), # kg |
| 30 | +) |
| 31 | + |
| 32 | +# button layout |
| 33 | +bmap = """_in _ft _yd _mi ** C |
| 34 | + _gal exp( sin( cos( tan( log10( |
| 35 | + _pt pi abs( **2 sqrt( log( |
| 36 | + _qt deg( SCI ( ) / |
| 37 | + _floz rad( 7 8 9 * |
| 38 | + _sqft fah( 4 5 6 - |
| 39 | + _acre cel( 1 2 3 + |
| 40 | + _nm _lb 0 . j =""" |
| 41 | + |
| 42 | +# color indices for buttons |
| 43 | +col_ind = """333312 |
| 44 | +311111 |
| 45 | +311111 |
| 46 | +311114 |
| 47 | +315554 |
| 48 | +315554 |
| 49 | +315554 |
| 50 | +335554""" |
| 51 | + |
| 52 | +col_ind = col_ind.splitlines() |
| 53 | + |
| 54 | +# RGB colors for each color index |
| 55 | +cols = ( |
| 56 | +(255, 255, 255), |
| 57 | +(255,96,117), |
| 58 | +(255,237,83), |
| 59 | +(142,148,255), |
| 60 | +(228,142,215), |
| 61 | +(159,229,110), |
| 62 | +) |
| 63 | + |
| 64 | +# text colors and sizes |
| 65 | +button_rgb = 0, 0, 0 |
| 66 | +display_rgb = 0, 0, 0 |
| 67 | +size_button = 30 |
| 68 | +size_disp = 55 |
| 69 | + |
| 70 | +but = [] |
| 71 | +for l in bmap.splitlines(): |
| 72 | + bb = l.split() |
| 73 | + but.append(bb) |
| 74 | + |
| 75 | +def deg(x): |
| 76 | + return degrees(x) |
| 77 | + |
| 78 | +def rad(x): |
| 79 | + return radians(x) |
| 80 | + |
| 81 | +def fah(x): |
| 82 | + # Celsius -> Fahrenheit |
| 83 | + return (x * 9/5) + 32 |
| 84 | + |
| 85 | +def cel(x): |
| 86 | + # Fahrenheit -> Celsius |
| 87 | + return (x - 32) * 5/9 |
| 88 | + |
| 89 | +class PyCalc: |
| 90 | + def __init__(self): |
| 91 | + pygame.init() |
| 92 | + self.res = RES |
| 93 | + self.screen = pygame.display.set_mode(self.res, pygame.RESIZABLE) |
| 94 | + pygame.display.set_caption('PyCalc (units)') |
| 95 | + self.screen.fill(BACKGROUND) |
| 96 | + self.font = pygame.font.Font(fn, size_button) |
| 97 | + self.fontres = pygame.font.Font(fn, size_disp) |
| 98 | + self.clock = pygame.time.Clock() |
| 99 | + self.inp = "" |
| 100 | + |
| 101 | + def events(self): |
| 102 | + global a, b |
| 103 | + |
| 104 | + for event in pygame.event.get(): |
| 105 | + if event.type == pygame.QUIT: self.running = False |
| 106 | + if event.type == pygame.VIDEORESIZE: |
| 107 | + self.res = event.w, event.h |
| 108 | + self.last = 0 |
| 109 | + self.screen = pygame.display.set_mode(self.res, pygame.RESIZABLE) |
| 110 | + if event.type == pygame.MOUSEBUTTONDOWN: |
| 111 | + x, y = pygame.mouse.get_pos() |
| 112 | + boxx, boxy = self.res[0] // HSIZE, self.res[1] // VSIZE |
| 113 | + X, Y = x // boxx, y // boxy |
| 114 | + if Y == 0 or Y > VSIZE - 1 or X > HSIZE - 1: |
| 115 | + return |
| 116 | + ci = but[Y - 1][X] |
| 117 | + if ci == "=": # evaluate expression |
| 118 | + try: |
| 119 | + inp2 = self.inp |
| 120 | + for u1, u2 in us_units: |
| 121 | + inp2 = inp2.replace(u1, u2) |
| 122 | + self.inp = str(eval(inp2)) |
| 123 | + a = eval(self.inp) |
| 124 | + except: |
| 125 | + self.inp += " *ERROR*" |
| 126 | + elif ci == "C": # clear screen |
| 127 | + self.inp = "" |
| 128 | + elif ci == "SCI": # convert to scientific notation |
| 129 | + try: |
| 130 | + self.inp = "%e" % float(self.inp) |
| 131 | + except: |
| 132 | + pass |
| 133 | + else: # or add button text to input |
| 134 | + self.inp += but[Y - 1][X] |
| 135 | + |
| 136 | + def run(self): |
| 137 | + self.running = True |
| 138 | + while self.running: |
| 139 | + self.clock.tick(10) |
| 140 | + self.events() |
| 141 | + self.update() |
| 142 | + pygame.quit() |
| 143 | + |
| 144 | + def update(self): |
| 145 | + self.screen.fill(BACKGROUND) |
| 146 | + boxx, boxy = self.res[0] // HSIZE, self.res[1] // VSIZE |
| 147 | + for y in range(1, VSIZE): |
| 148 | + for x in range(HSIZE): |
| 149 | + ci = int(col_ind[y - 1][x]) |
| 150 | + pygame.draw.rect(self.screen, cols[ci], |
| 151 | + [x * boxx + BORDER, y * boxy + BORDER, |
| 152 | + boxx - 2 * BORDER, boxy - 2 * BORDER]) |
| 153 | + t = but[y - 1][x] |
| 154 | + if t[-1] == "(" and t != "(": |
| 155 | + t = t[:-1] |
| 156 | + if t != "#": |
| 157 | + tr = self.font.render(t, True, button_rgb) |
| 158 | + ox = max(0, (boxx - tr.get_width()) // 2) |
| 159 | + oy = max(0, (boxy - tr.get_height()) // 2) |
| 160 | + self.screen.blit(tr, (x * boxx + BORDER + ox, y * boxy + BORDER + oy)) |
| 161 | + if len(self.inp) < MAXLEN: # check if maximum digits exceeded |
| 162 | + t = self.inp |
| 163 | + else: |
| 164 | + try: |
| 165 | + self.inp = "%e" % float(self.inp) |
| 166 | + t = self.inp |
| 167 | + except: |
| 168 | + t = "*TOO LONG*" |
| 169 | + tr = self.fontres.render(t, True, display_rgb) |
| 170 | + self.screen.blit(tr, (0, 0)) |
| 171 | + pygame.display.flip() |
| 172 | + |
| 173 | +app = PyCalc() |
| 174 | +app.run() |
| 175 | + |
0 commit comments