Aleksandar Beat
Aleksandar Beat

Reputation: 201

Binding function to treeview does not work Tkinter

I made ttk treeview in which you can manually add values. (Im not going to post the whole code because its to big). I also made a function that should calculate multiplication of two columns and put the result in third column. I bind two events to it , validate with ENTER key (<Return>) and validate when you click on other cell (<FocusOut>). From some reason, the program works only when you press ENTER key, and it does not work with FocusOut. It does not show any error, it just does not work. Do you know whats the problem?

def TotalCost(event):

    try:
        SelectedRow = NewTree.selection()[0]

        Quantity=round(float(NewTree.item(SelectedRow,"values")[3]),2)
        UnitCost=round(float(NewTree.item(SelectedRow,"values")[4]),2)

        TotalCost=float(round(Quantity*UnitCost,2))

        NewTree.set(SelectedRow, '#6', TotalCost)

    except IndexError:
        sys.exit()
        pass
    except ValueError: 
        Error=messagebox.showinfo("Error!","Please enter values for Planned Costs or Real Costs.")
        sys.exit() #za resavalje greske

        pass

NewTree.bind('<Return>', TotalCost)  # validate with Enter
NewTree.bind('<FocusOut>', TotalCost)  # validate when you click on other cell

Upvotes: 1

Views: 2042

Answers (2)

figbeam
figbeam

Reputation: 7176

I think you might want the <<TreeviewSelect>> event. It's generated whenever you select an item in the treeview, regardless if you click on the same or an other item.

Here's a little program that prints which event is generated when you do things. Run it and see if this can be of use.

from tkinter import *
from tkinter import ttk

root = Tk()
root.geometry('300x400')
NewTree = ttk.Treeview(root)
NewTree.grid()
NewTree.insert('', 'end',  text='Widget One')
NewTree.insert('', 'end',  text='Widget Two')
NewTree.insert('', 'end', text='Widget Three')

def callback(binding):
    print('Callback from:', binding)

NewTree.bind('<Return>', lambda event: callback(binding='Return'))
NewTree.bind('<FocusOut>', lambda event: callback(binding='FocusOut'))
NewTree.bind('<<TreeviewSelect>>', lambda event: callback(binding='TreeviewSelect'))
NewTree.bind('<Leave>', lambda event: callback(binding='Leave'))

root.mainloop()

By the way; naming conventions in Python says you should only Capitalize class names. It's completely up to you, but if you want other people to understand your code it helps to conform to standards. :)

Update: brief explanation of lambda in bind functions

Bind will call a function and also return an event as argument to that function.

The event will look something like <Leave event state=Mod1 focus=False x=205 y=24>.

With the lambda function I will consume the event from the bind function, and then provide a new function to call with the name of the action as argument. This will allow me to use one function as callback for all the bind functions instead of each bind function calling a separate function.

This is a practical way of solving some practical problems, eg. when creating an array of buttons:

for value in ["Stilton","Tilsit","Mozzarella"]:
    b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))
    b.pack()

which makes each button press call the my_callback() function with arg as argument.

I have not yet found any simple comprehensive explanation of lambda, especially as they also hide under headings such as "Currying" and "Late binding" which are general concepts which can be implemented with lambda functions.

Here are some links that I found informative:

Upvotes: 1

harsh patel
harsh patel

Reputation: 46

Here is an example of code to illustrate the binding functions and invoking function without any button. i have used python 2.7

import Tkinter as tk
main=tk.Tk()
var=tk.StringVar()

def rockon(event):
    try:
        var.set(int(e1.get())+int(e2.get()))
    except:
        pass

e1=tk.Entry(main)
e1.place(x=10,y=10)
e1.insert(tk.END,0)
e1.bind('<FocusOut>',rockon)   #either u press tab
e1.bind('<Leave>',rockon)      #either u move out of the widget
e1.bind('<Return>',rockon)     #or u press enter key, it goes into the function "rockon"


e2=tk.Entry(main)
e2.place(x=10,y=50)
e2.insert(tk.END,0)
e2.bind('<FocusOut>',rockon)
e2.bind('<Leave>',rockon)
e2.bind('<Return>',rockon)

label=tk.Label(main,textvariable=var)     #we print the total of two entries in label without pressing any button
label.place(x=10,y=100)

main.mainloop()

Upvotes: 2

Related Questions