From: Morton Goldberg Date: 2006-09-05T20:43:01+09:00 Subject: Re: How to properly bind context menus to canvas in Ruby/Tk? On Sep 3, 2006, at 10:40 AM, Josef Wolf wrote: > Hello! > > I am trying to bind context-menus to a Tk canvas. The canvas > itself should > have a context menu, giving the ability to create new objects. In > addition, > every object should have its own context menu so that the object > can be > modified/deleted. This is what I'm doing: > > #! /usr/bin/ruby > require 'tk' > $-w = 1 > > root = TkRoot.new > canvas = TkCanvas.new.pack > > # This is the context menu for the canvas to create new objects > # > menu=TkMenu.new > menu.add('command', 'label'=>'New Foo', 'command'=>proc{p "new > foo"}) > menu.add('command', 'label'=>'New Bar', 'command'=>proc{p "new > bar"}) > > # canvas binding > canvas.bind("Button-3") { |e| evt=e; menu.popup(e.x_root, > e.y_root) } > > # Now create a new object and bind to it a context menu to modify/ > delete > # the object > # > rect = TkcRectangle.new(canvas, 0, 0, 50, 50, "fill"=>"white") > menu=TkMenu.new > menu.add('command', 'label'=>'Edit', 'command'=>proc{p "edit"}) > menu.add('command', 'label'=>'Delete', 'command'=>proc{p "del"}) > > # object binding > canvas.itembind(rect, "Button-3") { |e| menu.popup(e.x_root, > e.y_root) } > > Tk.mainloop > > Unfortunately, this don't work as desired. As soon as the binding > to the > object is done, the canvas binding seems to be overridden. Even when > right-klicking on empty space in the canvas, the object's menu is > invoked. > > Any ideas what I am doing wrong here? I tried running your code on my system which is OS X 10.4.7 running Ruby 1.8.2, and I don't see what you report. What I see is following: * Right-clicking away from the the rectangle, the canvas' contextual menu pops-up. * Right-clicking on the rectangle. ** The rectangle's contextual menu pops-up. ** When the rectangle's menu is dismissed, the canvas' contextual menu pops-up. With a small tweak, I was able to keep the canvas' contextual menu from popping up when the the rectangle is right-clicked. So what I have now running on my system works as you expected. Regards, Morton P.S. Just in case you want to compare what I am running with your own code. #! /usr/bin/ruby -w # Date: September 5, 2006 # # Contextual menus on a canvas require 'tk' DEBUG = [] RIGHT_BUTTON = "Button-2" # On OS X right button is Button-2. begin root = TkRoot.new {title 'Ruby Tk'} canvas = TkCanvas.new.pack handled_by_rect = false # This is the context menu for the canvas to create new objects menu1=TkMenu.new menu1.add('command', 'label'=>'New Foo', 'command'=>proc{p "new foo"}) menu1.add('command', 'label'=>'New Bar', 'command'=>proc{p "new bar"}) # Canvas binding canvas.bind(RIGHT_BUTTON) do |e| if handled_by_rect handled_by_rect = false else menu1.popup(e.x_root, e.y_root) end end # Now create a new object and bind to it a context menu to modify/ delete # the object rect = TkcRectangle.new(canvas, 50, 50, 100, 100, "fill"=>"white") menu2=TkMenu.new menu2.add('command', 'label'=>'Edit', 'command'=>proc{p "edit"}) menu2.add('command', 'label'=>'Delete', 'command'=>proc{p "del"}) # Rectangle binding canvas.itembind(rect, RIGHT_BUTTON) do |e| menu2.popup(e.x_root, e.y_root) handled_by_rect = true end # Set initial window geometry; i.e., size and placement. win_w, win_h = 300, 200 # root.minsize(win_w, win_h) win_lf = (root.winfo_screenwidth - win_w) / 2 root.geometry("#{win_w}x#{win_h}+#{win_lf}+50") # Make Cmnd+Q work as expected on OS X. root.bind('Command-q') {Tk.root.destroy} Tk.mainloop ensure puts DEBUG unless DEBUG.empty? end