akhi07rx

4 min read

The same calculator in 51 lines


After the last post, I got curious how much smaller the thing could actually get without changing what it does.

The original was 70 lines. Most of that bulk came from the button layout: six rows of hardcoded tk.Button(...) calls, each with its own coordinates, repeated with minor variations. The logic was fine. The repetition was not.

The fix was to describe the grid as data and loop over it instead:

buttons = [
    ["%",   "CE",  "C",  ""],
    ["(",   "𝑥²",  ")",  "÷"],
    ["7",   "8",   "9",  "×"],
    ["4",   "5",   "6",  "-"],
    ["1",   "2",   "3",  "+"],
    ["+/-", "0",   ".",  "="],
]

for r, row in enumerate(buttons):
    for c, label in enumerate(row):
        cmd = actions.get(label, lambda l=label: push(l))
        kw  = {"bg": "#19a8b2"} if label == "=" else {}
        tk.Button(calc, text=label, font=("Consolas", 14), command=cmd, **kw).place(
            x=20 + c * 80, y=110 + r * 60, width=70, height=50
        )

The actions dict handles buttons that do something other than push a character into the field: clear, backspace, equals, and the symbols that need translating (÷ to /, × to *, 𝑥² to **2). Anything not in the dict defaults to inserting its label directly.

The other change worth mentioning is run(). When I first condensed things I tried squeezing it into a lambda, which immediately broke: Python evaluates tuple elements left to right, so box.delete(0, END) wiped the field before eval(box.get()) could read it. The original answer() function never had this problem because it captured the value first. Pulling run back out into a proper function restores that, and also makes room for a try/except so a bad expression shows "Error" instead of crashing the whole callback.

def run():
    expr = box.get()
    if not expr:
        return
    try:
        result = eval(expr)
        box.delete(0, END)
        box.insert(END, result)
    except Exception:
        box.delete(0, END)
        box.insert(END, "Error")

That is genuinely the whole diff. Same window, same buttons, same eval() shortcut underneath. Just less repetition and one fewer silent failure mode. 51 lines.

There are almost certainly better ways to structure this. I am still poking around.

The full source is on GitHub.