skip to Main Content

I have a problem with MenuSelect event in Tcl/Tk 8.6.11 (tried in Linux, Debian 10.7).

In fact, it doesn’t fire at all for the main and tearoff-ed menus. Though working fine in Tcl/Tk 8.6.9, and even in 8.6.11 – only while menus are not tearoff-ed.

A test code:

package require Tk
proc ::somehandler {w wt} {
  puts "Step [incr ::step]: $w / $wt, index=[$wt index active]"
}
set w [menu .m -tearoff 1]
$w add command -label {Item 1}
$w add command -label {Item 2}
bind $w <<MenuSelect>> [list ::somehandler $w %W]
pack [button .b -text "Click me" 
  -command {tk_popup .m [winfo pointerx .] [winfo pointery .]}]

I tried the following (idiotic though) replacement:

event delete <<MenuSelect>>
event add <<MenuSelect>> <Motion>
bind $w <<MenuSelect>> [list ::somehandler $w %W]

… with the same results.

Seemingly, it’s related to menu pathes dealt in Tk somewhat tricky, as seen in the above example.

I’m too lazy to change a standard code at switching from 8.6.9 to 8.6.11/12, 8.7 etc.

TIA for any hints.

2

Answers


  1. Chosen as BEST ANSWER

    For Tk 8.6.11+, bind Menu should be used instead of bind $w (for individual menu items). It adds some acrobatics to an event handler that should calculate what's a menu item to be dealt with.

    I.e. we have something like:

    bind Menu <<MenuSelect>> [list ::somehandler %W]
    

    The %W wildcard is passed to ::somehandler as a "cloned" name, if the menu item is in a cloned menu.

    And ::somehandler should calculate who is the %W in reality.

    Csaba Nemethi advises to use a procedure like clonename (from utils.tcl of BWidget package). This procedure gets a clone name from a "normal" menu item's path.

    Here is a bit modified version of it:

    proc clonename {mnu} {
      # Gets a clone name of a menu.
      #   mnu - the menu's path
      # This procedure is borrowed from BWidget's utils.tcl.
    
      set path [set menupath {}]
      set found 0
      foreach widget [lrange [split $mnu .] 1 end] {
        if {$found || [winfo class "$path.$widget"] eq {Menu}} {
          set found 1
          append menupath # $widget
          append path . $menupath
        } else {
          append menupath # $widget
          append path . $widget
        }
      }
      return $path
    }
    

    As an example of use, see test.tcl of:

    http://chiselapp.com/user/aplsimple/repository/baltip/zip/trunk/baltip.zip

    Thank you Donal and Csaba for your hints.


  2. This is probably related to the fact that menus use clones for tearoffs and the menubar. From the documentation:

    When a menu is set as a menubar for a toplevel window, or when a menu
    is torn off, a clone of the menu is made. This clone is a menu widget
    in its own right, but it is a child of the original. Changes in the
    configuration of the original are reflected in the clone. Additionally,
    any cascades that are pointed to are also cloned so that menu traversal
    will work right. Clones are destroyed when either the tearoff or
    menubar goes away, or when the original menu is destroyed.

    I can’t remember how exactly clones are really named, but you don’t normally interact with them directly; it’s only with event handling that you ever really see them. (I’ve only ever had to deal with them when doing tooltips for menus.)

    Normally, it’s considered best to avoid using <<MenuSelect>> and instead just set a -command on the entries that can be selected (or to just set the model variables right for checkbutton and radiobutton entries). And avoid tearoffs entirely; they’re a style of menu interaction that went out of fashion over 25 years ago.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search