I am building a bit of a GUI program, one of the features, is that certain buttons are only clickable when appropriate, and are otherwise disabled. The program involves using a mouse, and a ttk Treeview item. What the program does is, when a user clicks within the Treeview, a function is called to determine what they clicked, and things go on from there.
I made my program using something like this, below is a generic example:
treeview.bind('', function_name)
"When a user clicks within the Treeview, run the function", and this worked flawlessly - but there was a very minor delay to things happening (because the mouse de-press was the trigger), so I thought, let's change "ButtonRelease-1" to "ButtonPress-1", so that things happen as soon as the user clicks within the Treeview - and as I predicted, the program is way more "instant" in terms of how it reacts... however there is a big problem created seemingly out of nowhere.
All of my logic stops working when I replace <ButtonRelease-1>
with <ButtonPress-1>
, I have created a "minimum reproducible sample code" or whatever it's called, which very well demonstrates the problem.
import tkinter as tk
from tkinter import *
from tkinter import ttk
alternate = 1
def add_row():
global alternate
if alternate == 0:
tree.insert('', tk.END,text="L1",values=("Big1","Test12345"))
alternate = 1
elif alternate == 1:
tree.insert('', tk.END,text="L1",values=("Big1","Test"))
alternate = 0
def delete_row():
last = tree.get_children()[-1]
tree.delete(last)
disablebutton_statecheck()
def disablebutton_statecheck(event=None):
print("triggered statecheck")
print(list(tree.selection()))
print(len(tree.selection()))
if len(tree.selection()) > 0:
button_6.state(["!disabled"])
else:
button_6.state(["disabled"])
window = Tk()
window.title("test window")
window.geometry("600x600 0 0")
section_1 = Frame(window, width = 600, height = 600, bg="#faf")
section_1.place(x=0, y=0)
tabControl_1 = ttk.Notebook(section_1, height = 470, width = 395) #395
tab1 = ttk.Frame(tabControl_1)
tabControl_1.add(tab1, text='Devices')
tabControl_1.place(y=10,x=25)
tree = ttk.Treeview(tab1, height=7)
tree.place(x=30, y=95)
tree.bind('<Motion>', 'break')
tree["columns"] = ("one", "two")
tree['show'] = 'headings'
tree.column("one", width=100, anchor='c', stretch=False)
tree.column("two", width=100, anchor='c', stretch=False)
tree.heading("one", text="Account")
tree.heading("two", text="Type")
tree.insert("",'end', text="L1",values=("Test","Test12345"))
button_1 = ttk.Button(tab1, text="add a row", command=add_row, width=17)
button_1.place(x=50, y=360)
button_2 = ttk.Button(tab1, text="delete row", command=delete_row, width=17)
button_2.place(x=200, y=360)
button_6 = ttk.Button(tab1, text="disable", width=17)
button_6.place(x=50, y= 50)
tree.bind('<ButtonRelease-1>', disablebutton_statecheck)
disablebutton_statecheck()
window.resizable(False,False)
window.mainloop()
Now, with this code, please run as is - and you can see, that when there are no items in the Treeview selected, the disable button is greyed out, but when there is 1 or more items selected in the treeview, the disable button is not greyed out. On windows, ctrl click will allow to select multiple items, and even to de-select items, so you can play around with things that way. The print statements confirm that after a click event, the program is able to accurately report how many items are selected, and which items they are, as well. Note that treeview row IDs start with I001, not I000.
Next, please replace "Release" with "Press" inside the tree.bind thingy, and run the code again, the behaviour is not correct - now, if you generate a few items, and click between them, you'll notice that the program reports the items previously selected, instead of the items you just selected.
For example, add a few rows - click on row 1, the program reports that nothing is selected, then, click on row 2 (which de-selects row 1), the program then reports that there is 1 item selected, and that it is row 1, despite row 2 being selected, etc, etc.
I cannot understand or explain why "tree.selection()" is not reporting correctly all of a sudden, when changing ButtonRelease-1 to ButtonPress-1 - there must be something I can do to get it to report correctly, as this will improve the "feel" of my program a lot.
Thanks in advance to anyone that reads this!
(OS: Windows 7, Python ver: 3.6.0)
CodePudding user response:
It is because the actual selection is done after the callback for <ButtonPress-1>
returns.
However you can delay the execution of disablebutton_statecheck()
a bit by using .after()
as below:
tree.bind('<ButtonPress-1>', lambda e: window.after(1, disablebutton_statecheck))
Then the default selection task is done before the execution of disablebutton_statecheck()
.