Andrew H
Andrew H

Reputation: 63

Binding number keys to buttons for calculator

How do I go on about doing this? I don't like the the idea of having to press each individual button. I'm really trying to understand all of this. I get the concept of binding I just don't know how to do it.

from tkinter import *


class Calc():
    def __init__(self):
        self.total = 0
        self.current = ""
        self.new_num = True
        self.op_pending = False
        self.op = ""
        self.eq_flag = False

    def num_press(self, num):
        temp = text_box.get()
        self.eq_flag = False
        temp2 = str(num)      
        if self.new_num == True:
            self.current = temp2
            self.new_num = False
        else:
            if temp2 == '.':
                if temp2 in temp:
                    return
            self.current = temp + temp2
        text_box.delete(0, END)
        text_box.insert(0, self.current)

    def calc_total(self):
        if self.op_pending == True:
            self.do_sum()
            self.op_pending = False

    def do_sum(self):
        self.current = float(self.current)
        if self.op == "add":
            self.total += self.current
        if self.op == "minus":
            self.total -= self.current
        if self.op == "times":
            self.total *= self.current
        if self.op == "divide":
            self.total /= self.current
        text_box.delete(0, END)
        text_box.insert(0, self.total)
        self.new_num = True

    def operation(self, op):
        if self.op_pending == True:
            self.do_sum()
            self.op = op
        else:
            self.op_pending = True
            if self.eq_flag == False:
                self.total = float(text_box.get())
            else:
                self.total = self.current
            self.new_num = True
            self.op = op
            self.eq_flag = False

    def cancel(self):
        text_box.delete(0, END)
        text_box.insert(0, "0")
        self.new_num = True

    def all_cancel(self):
        self.cancel()
        self.total = 0

    def sign(self):
        self.current = -(float(text_box.get()))
        text_box.delete(0, END)
        text_box.insert(0, self.current)

class My_Btn(Button):
    def btn_cmd(self, num):
        self["command"] = lambda: sum1.num_press(num)

sum1 = Calc()
root = Tk()
calc = Frame(root)
calc.grid()

root.title("Calculator")
text_box = Entry(calc, justify=RIGHT)
text_box.grid(row = 0, column = 0, columnspan = 4, pady = 5, padx=5)
text_box.insert(0, "0")

#Buttons
numbers = "789456123"
i = 0
bttn = []
for j in range(2,5):
    for k in range(3):
        bttn.append(My_Btn(calc, text = numbers[i]))
        bttn[i].grid(row = j, column = k, pady = 5, padx=5)
        bttn[i].btn_cmd(numbers[i])
        i += 1
clear = Button(calc, text = "C", width =1, height =1)
clear["command"] = sum1.cancel
clear.grid(row = 1, column = 0, pady = 5, padx=5)

all_clear = Button(calc, text = "AC", width =1, height =1)
all_clear["command"] = sum1.all_cancel
all_clear.grid(row = 1, column = 1, pady = 5, padx=5)

bttn_div = Button(calc, text = chr(247))
bttn_div["command"] = lambda: sum1.operation("divide")
bttn_div.grid(row = 1, column = 2, pady = 5, padx=5)

bttn_mult = Button(calc, text = "x", width =1, height =1)
bttn_mult["command"] = lambda: sum1.operation("times")
bttn_mult.grid(row = 1, column = 3, pady = 5, padx=5)

minus = Button(calc, text = "-", width =1, height =1)
minus["command"] = lambda: sum1.operation("minus")
minus.grid(row = 2, column = 3, pady = 5, padx=5)

add = Button(calc, text = "+", width =1, height =1)
add["command"] = lambda: sum1.operation("add")
add.grid(row = 3, column = 3, pady = 5, padx=5)

neg= Button(calc, text = "+/-", width =1, height =1)
neg["command"] = sum1.sign
neg.grid(row = 4, column = 3, pady = 5, padx=5)

equals = Button(calc, text = "=", width =1, height =1)
equals["command"] = sum1.calc_total
equals.grid(row = 5, column = 3, pady = 5, padx=5)

point = Button(calc, text = ".", width =1, height =1)
point["command"] = lambda: sum1.num_press(".")
point.grid(row = 5, column = 2, pady = 5, padx=5)

bttn_0 = Button(calc, text = "0", width =7, height =1)
bttn_0["command"] = lambda: sum1.num_press(0)
bttn_0.grid(row=5, column = 0, columnspan = 2, pady = 5, padx=5)

Upvotes: 0

Views: 2622

Answers (2)

abarnert
abarnert

Reputation: 366153

I think the right way to do it is the way unutbu suggested, with an Entry. (You can use ttk theming if you want to make it not look like a normal Entry, and you can use focus tricks to make it work even when the user hasn't clicked on it.)

But if you want to do things your way, you can. Your key function can dispatch to functions that do the actual work (append_digit for digits, backspace for backspace/delete). Then, if you want buttons that do the same thing, you can add them, with callbacks that dispatch to the same functions. For example:

from functools import partial
from Tkinter import *

root = Tk()

def key(event):
    print "pressed", repr(event.char)
    if event.char.isdigit():
        append_digit(event.char)
    elif event.char in ('\x08', '\x7f'):
        backspace()

def callback(event):
    frame.focus_set()
    print "clicked at", event.x, event.y

frame = Frame(root, width=100, height=100)
frame.bind("<Key>", key)
frame.bind("<Button-1>", callback)
frame.pack()

current = IntVar(0)
label = Label(frame, textvariable=current)
label.pack()

def button_callback(i):
    print "clicked button {}".format(i)
    append_digit(i)

def append_digit(digit):
    current.set(current.get() * 10 + int(digit))

def backspace():
    current.set(current.get() // 10)

for i in '1234567890':
    Button(frame, text=i, command=partial(button_callback, i)).pack()
Button(frame, text='C', command=backspace).pack()

frame.focus_set()
root.mainloop()

Obviously you can add more keystrokes and/or buttons—e.g., the "+" key and a button labeled "+" both trigger an add function, or a button labeled "AC" triggers a current.set(0) function, or whatever you want. And presumably you can design a nicer UI than a long column of buttons. This is just to show you the idea.

Upvotes: 1

unutbu
unutbu

Reputation: 880937

Rather than binding each key, I would use an Entry widget. People make mistakes. They'll want to hit the backspace key, or use the arrow keys or mouse to put the cursor someplace and edit what they've typed. You don't want to reinvent all that. If you give them an Entry widget, you get all that editing capability for free, and can parse the result when they press Enter.

entry.bind('<Enter>', parse)

Upvotes: 2

Related Questions