1

I am new to Python and is about to build a GUI using tkinter. The GUI consists of a Notebook and I am trying to create a event handler for when the user click with the mouse on the different tabs. However when I click on a tab, the handlerfunction 'works' but it seems the selected tab is not 'updated' before the function is called.

As a sidenote: So far I have mainly used 'Tkinter 8.5 reference: a GUI for Python (Shipman)'. Please see code below. Grateful for any suggestions!

from tkinter import *
import tkinter.ttk as ttk

root = Tk()
note = ttk.Notebook(root)

tab1 = Frame(note,width = 10)
tab2 = Frame(note,width = 10)
tab3 = Frame(note,width = 10)

note.add(tab1, text = "Tab One")
note.add(tab2, text = "Tab Two")
note.add(tab3, text = "Tab Three")
note.grid()

def personalData(event):
    if event.widget.index("current") == 0:    
       print("One!") 
    else:
       print("Not One!") 

note.bind('<1>',personalData)
root.mainloop()
Cœur
  • 37,241
  • 25
  • 195
  • 267
user2423970
  • 9
  • 1
  • 2

3 Answers3

2

The reason this is happening is due to the order that events are processed. Custom bindings on widgets are processed before the built-in bindings, meaning that your binding on <1> will fire before the tab actually changes. For more information, research "binding tags" (or sometimes called "bind tags" or "bindtags").

A better solution is to bind on the virtual event <<NotebookTabChanged>>, which the notebook will generate after the tab has changed.

Example:

note.bind("<<NotebookTabChanged>>", personalData)

An additional benefit to binding to the virtual event rather than a mouse click is that the event will fire even if the tab is changed via some other mechanism besides a mouse click. For example, if a tab has the keyboard focus, you can select the next or previous tab using the arrow keys on your keyboard.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
1

You're experiencing this because events of bind are handled before events of bindtags, in other words, you're actually not changing the tab before the callback function to "<1>" event, personalData is handled.


One workaround would be to rotate the order of the sequence which the events are being handled with. Add after note.bind(...):

note.bindtags((note.bindtags()[1:] + note.bindtags()[:1]))

this will put the bind's callback as the last in the sequence and shift the rest to up in the queue.


Another way would be to replace the actual event("<1>") to a more appropriate one like in Novel's suggestion.

Nae
  • 14,209
  • 7
  • 52
  • 79
0

Note that when you click another tab, the current tab is still the previous tab; if you change your binding to

note.bind('<ButtonRelease-1>',personalData)

it will work right, as now, when the mouse button is released, the current tab is the tab already selected.

progmatico
  • 4,714
  • 1
  • 16
  • 27
  • 1
    The disadvantage to this solution is that it won't work if the user switches tabs using the keyboard (which you can do with the left and right arrows when the notebook tab has the keyboard focus). – Bryan Oakley Jan 04 '18 at 22:22
  • 1
    Sure, I agree with you in the preferred use of the virtual event `<>`. – progmatico Jan 04 '18 at 22:29
  • Thanks! The possibility to also react on keyboard focus makes sense. – user2423970 Jan 06 '18 at 11:40
  • Thanks now it works fine! The possibility to also react on keyboard focus makes sense. Actually before asking the quesion I wrote note.bind('<> (page 128 in GUI for tkinter) not realizing that the '-' was due to change of line in the tutorial (stupid of me..) However when running this code I did not receive any error message (IDLE Shell) which surprised me. – user2423970 Jan 06 '18 at 11:46
  • I think this scenario of an event happening before related update happens is pretty common in tkinter, with the purpose of allowing you to catch and modify the consequences of that event at your will. See interesting case here https://stackoverflow.com/questions/15771749/tkinter-set-stringvar-after-key-event-including-the-key-pressed/ – progmatico Jan 08 '18 at 22:24
  • About the surprise with no error message with a virtual event not defined, maybe @Bryan can help? Is it because the bind just puts an arbitrary string in a list of bind tags (which never gets referenced because you haven't add the event, so no error?) – progmatico Jan 08 '18 at 22:32
  • 1
    @user2423970 Technically speaking, `'<>'` is a valid virtual event identifier. Tkinter has no way of knowing if you created (or are planning to create) an event like that somewhere else in your code. – Bryan Oakley Jan 08 '18 at 22:46