Reputation: 63
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
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
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