2

I'm trying to program a calculator in python for a while now and there is a problem with my entry that I can't solve it although I don't see any problem with it.

so here is an example for my code:

from Tkinter import *

window =Tk()
window.title("Calculator")
#creating an entry
string=StringVar
entry = Entry(window, width=40,textvariable=string )
entry.grid(row=0, column=0, columnspan=6, ipady=10)
entry.focus()


#basically I have a function for creating buttons but here I will do it the traditional way.

num_one=Button(window,text="1",width=2,height=2,padx=20,pady=20,)
num_one.grid(row=1,column=0,padx=1,pady=1)

#crating an index for the calculator
index=0
#creating a function to insert the number one to the entry in the index position and then add one to the index

def print_one(index):
    entry.insert(index,"1")

binding the num_one button to the function above

num_one.bind("Button-1",print_one(index))

Now the problem is that the string "1" should be entered to the entry only if I click n the num_one button, but when I start the program automatically the number "1" goes into the entry.

Cœur
  • 37,241
  • 25
  • 195
  • 267

2 Answers2

2

Lots of issues that I noticed in your code -

  1. string=StringVar - You need to call it like StringVar() , otherwise you are just setting StringVar class (not its object) to `string.

  2. When you do -

    num_one.bind("Button-1",print_one(index))
    

    You actually call the function first and bind the return value , you should instead bind the function object (without calling it) , Example -

    num_one.bind("<Button-1>",print_one)
    
  3. For binding the function to left mouse click, you need to bind to <Button-1> (notice the < and > at the ends) not Button-1 .

  4. In your function, the first parameter you will recieve (the function that is bound) is the event object , not the next index. You can instead use something like -

    string.set(string.get() + "1")
    
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • thank you! I didn't understood your explanation in number 4. can you try explain it again to me? – Yair Mishnayot Aug 09 '15 at 09:31
  • Check here - http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm You do not get `index` as the first parameter when the button is clicked. Instead you can get the data and then set it back after adding `1` . – Anand S Kumar Aug 09 '15 at 09:35
  • thanks man! fixed the problem! in the function I needed to give it "event" as a parameter and then declare index as a global variable insert the number one to the entry and add one to the index. – Yair Mishnayot Aug 09 '15 at 09:53
  • @TheTechGuy: FWIW, it's not a great idea to use `string` as a variable name, since it's the name of a [standard module](https://docs.python.org/3/library/string.html?module-string#module-string). It's not actually an error to do that, and it won't hurt anything if your code doesn't import that module, but it can be confusing for people reading the code. And if you _do_ change your code to `import string` for some reason then you'll get mysterious bugs. – PM 2Ring Aug 09 '15 at 10:42
2

As Anand says, there are various problems with your current code, both in the syntax & the design.

I'm not sure why you want to track the Entry's index yourself, since the Entry widget already does that. To insert text at the current cursor position you can use Tkinter.INSERT in the entry.insert() method call.

It looks like you intend to write a separate callback function for each of the number buttons. That's unnecessary, and it could get messy.

The code below shows a way to use a single callback function for multiple buttons. We attach the button's number as an attribute to the button itself. The callback function can easily access that number because the Event object argument that the callback is called with contains the widget that activated it as an attribute.

Note that my code uses import Tkinter as tk rather than from Tkinter import *. Sure, it makes the code a little more long-winded, but it prevents name collision.

import Tkinter as tk

window = tk.Tk()
window.title("Calculator")

entry_string = tk.StringVar()
entry = tk.Entry(window, width=40, textvariable=entry_string)
entry.grid(row=0, column=0, columnspan=6, ipady=10)
entry.focus()

def button_cb(event):
    entry.insert(tk.INSERT, event.widget.number)

for i in range(10):
    y, x = divmod(9 - i, 3)
    b = tk.Button(window, text=i, width=2, height=2, padx=20, pady=20)
    b.grid(row=1+y, column=2-x, padx=1, pady=1)
    #Save this button's number so it can be accessed in the callback
    b.number = i
    b.bind("<Button-1>", button_cb)

window.mainloop()

Ideally, GUI code should go into a class, as that makes it easier for the widgets to share data, and it tends to make the code neater.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Wow man that is really make the code much easier! the only thing I don't recognize in your code is divemode(). what does it do with the values inside? – Yair Mishnayot Aug 09 '15 at 10:00
  • @TheTechGuy: `divmod()` is a built-in function that does integer division. It returns the quotient and remainder as a tuple. See the official Python [functions](https://docs.python.org/3/library/functions.html#divmod) docs for more info. – PM 2Ring Aug 09 '15 at 10:34