Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > How to properly bind context menus to canvas in Ruby/Tk?

Reply
Thread Tools

How to properly bind context menus to canvas in Ruby/Tk?

 
 
Josef Wolf
Guest
Posts: n/a
 
      09-03-2006
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?

 
Reply With Quote
 
 
 
 
Josef Wolf
Guest
Posts: n/a
 
      09-04-2006
On Mon, Sep 04, 2006 at 12:45:34AM +0900, Paul Lutus wrote:

Thanks for your reply, Paul!

> > 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?

>
> First, rename the second menu something other than "menu" You have already
> used that name, and this multiple use is causing you a lot of confusion.


I admit that this is bad style I've made copy/paste. In the real
program, the creation/binding of the menus is done in different methods,
thus they are actually two different variables (beacuse they are local
to their method)

> Second, after a bit of experimentation, I offer this educated guess. The
> rectangle object cannot process mouse events, so it cannot invoke a context
> menu different from that bound to the canvas.


AFAIK, the rectangle is no real object in its own right. The canvas
handles everything for it. That's the reason why its binding is done
with canvas.itembind() instead of rect.bind(). IMHO, it is very
important to understand this difference.

> It is apparent that the canvas is receiving all mouse events, not the
> rectangle,


Exactly

> and in order for the rectangle to launch its own context menu,
> it would have to process mouse events independently. AFAICS it can't.


The canvas handles those events. But for some reason the canvas fails
to differentiate.

> I want to emphasize this is just a guess, and I don't use the 'Tk' library
> because it is too poorly documented.


It's poorly documented, but it works great once you figure out how to
use it

Especially the canvas widget is pure black magic. The concept of
attaching arbitrary tags to items combined to the ability to attach
bindings to the tags is very powerfull.

> Have you considered using something other than 'Tk'?


I'm not aware of any usable alternatives. What would you suggest?

 
Reply With Quote
 
 
 
 
Morton Goldberg
Guest
Posts: n/a
 
      09-05-2006

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.
<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
</code>

 
Reply With Quote
 
Josef Wolf
Guest
Posts: n/a
 
      09-05-2006
On Tue, Sep 05, 2006 at 08:43:01PM +0900, Morton Goldberg wrote:

Thanks for the reply, Morton!

> 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.


How do you "dismiss" the menu? Whatever I do, I never get the second menu.

> 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.


Your handled_by_rect flag seems to do the trick. But this would be a
global flag in my case since the menus are created from different
classes. I would like to avoid such a global flag.

The Tk::Bind manpage mentions that processing of matching callbacks can
be controlled by "return" or calling "Tk->break" inside the callback.
That is, the rectangle's callback should call Tk->break. But none of
the involved classes seem to have a break method. And returning
false/true/nil/1/0 don't have any effect, too. Any ideas?

 
Reply With Quote
 
Morton Goldberg
Guest
Posts: n/a
 
      09-05-2006
On Sep 5, 2006, at 2:00 PM, Josef Wolf wrote:

> On Tue, Sep 05, 2006 at 08:43:01PM +0900, Morton Goldberg wrote:
>
> Thanks for the reply, Morton!
>
>> 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.

>
> How do you "dismiss" the menu? Whatever I do, I never get the
> second menu.


By left clicking -- either on or off the menu. Which menu don't you
get, the canvas' or the rect's?

>> 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.

>
> Your handled_by_rect flag seems to do the trick. But this would be a
> global flag in my case since the menus are created from different
> classes. I would like to avoid such a global flag.


WeLL, I try to avoid globals, too, but sometimes a global is the
simplest solution. On the other hand you could create a class, say
CanvasFlag, to manage the flag -- creating a class will solve most
Ruby design problems

> The Tk::Bind manpage mentions that processing of matching callbacks
> can
> be controlled by "return" or calling "Tk->break" inside the callback.
> That is, the rectangle's callback should call Tk->break. But none of
> the involved classes seem to have a break method. And returning
> false/true/nil/1/0 don't have any effect, too. Any ideas?


I believe the Ruby/Tk equivalent is Tk.callback_break, but I don't
think it will help here where we're dealing with an interaction
between a bind and an itembind.

Regards, Morton



 
Reply With Quote
 
Josef Wolf
Guest
Posts: n/a
 
      09-06-2006
On Wed, Sep 06, 2006 at 04:00:45AM +0900, Morton Goldberg wrote:
> On Sep 5, 2006, at 2:00 PM, Josef Wolf wrote:


> >How do you "dismiss" the menu? Whatever I do, I never get the
> >second menu.

> By left clicking -- either on or off the menu.


Don't work here.

> Which menu don't you get, the canvas' or the rect's?


I always get the canvas menu and never see the rectangle's menu.

> >The Tk::Bind manpage mentions that processing of matching callbacks
> >can
> >be controlled by "return" or calling "Tk->break" inside the callback.
> >That is, the rectangle's callback should call Tk->break. But none of
> >the involved classes seem to have a break method. And returning
> >false/true/nil/1/0 don't have any effect, too. Any ideas?

> I believe the Ruby/Tk equivalent is Tk.callback_break, but I don't
> think it will help here where we're dealing with an interaction
> between a bind and an itembind.


I tried Tk.callback_break too, and it didn't work. I just forgot to
mention this in my last mail.

 
Reply With Quote
 
Morton Goldberg
Guest
Posts: n/a
 
      09-07-2006
On Sep 6, 2006, at 12:50 PM, Josef Wolf wrote:

> On Wed, Sep 06, 2006 at 04:00:45AM +0900, Morton Goldberg wrote:
>> On Sep 5, 2006, at 2:00 PM, Josef Wolf wrote:

>
>>> How do you "dismiss" the menu? Whatever I do, I never get the
>>> second menu.

>> By left clicking -- either on or off the menu.

>
> Don't work here.
>
>> Which menu don't you get, the canvas' or the rect's?

>
> I always get the canvas menu and never see the rectangle's menu.


I think the reason my code works differently from yours is because I
use separate variable names for the two contextual menus while you
use the same one for both. It really does make a difference.

Regards, Morton

 
Reply With Quote
 
Josef Wolf
Guest
Posts: n/a
 
      09-07-2006
On Thu, Sep 07, 2006 at 10:31:00AM +0900, Morton Goldberg wrote:
> On Sep 6, 2006, at 12:50 PM, Josef Wolf wrote:
> >On Wed, Sep 06, 2006 at 04:00:45AM +0900, Morton Goldberg wrote:


> >>Which menu don't you get, the canvas' or the rect's?

> >I always get the canvas menu and never see the rectangle's menu.

> I think the reason my code works differently from yours is because I
> use separate variable names for the two contextual menus while you
> use the same one for both. It really does make a difference.


It _does_ make a difference, but in another way:

- If I use the same variable, always the last allocated menu wins.
Thus with the version I posted, always rectangle's menu wins.

- If I use different variables (as in my original code because they
are in different methods), always the canvas menu wins.

I _never_ see two different menus unless I introduce the handled_by_root
flag.

 
Reply With Quote
 
Morton Goldberg
Guest
Posts: n/a
 
      09-07-2006
On Sep 7, 2006, at 1:40 AM, Josef Wolf wrote:

> On Thu, Sep 07, 2006 at 10:31:00AM +0900, Morton Goldberg wrote:
>> On Sep 6, 2006, at 12:50 PM, Josef Wolf wrote:
>>> On Wed, Sep 06, 2006 at 04:00:45AM +0900, Morton Goldberg wrote:

>
>>>> Which menu don't you get, the canvas' or the rect's?
>>> I always get the canvas menu and never see the rectangle's menu.

>> I think the reason my code works differently from yours is because I
>> use separate variable names for the two contextual menus while you
>> use the same one for both. It really does make a difference.

>
> It _does_ make a difference, but in another way:
>
> - If I use the same variable, always the last allocated menu wins.
> Thus with the version I posted, always rectangle's menu wins.
>
> - If I use different variables (as in my original code because they
> are in different methods), always the canvas menu wins.
>
> I _never_ see two different menus unless I introduce the
> handled_by_root
> flag.


You _can_ see different menus if you change the posted version of
your code to assign each menu to a different variable. I can't
comment on your original code -- I've never seen it. But your posted
code will work much better if you use different variables for each
menu. Please try it yourself and see what happens.

Regards, Morton



 
Reply With Quote
 
Josef Wolf
Guest
Posts: n/a
 
      09-07-2006
On Thu, Sep 07, 2006 at 11:41:55PM +0900, Morton Goldberg wrote:
> On Sep 7, 2006, at 1:40 AM, Josef Wolf wrote:
> >On Thu, Sep 07, 2006 at 10:31:00AM +0900, Morton Goldberg wrote:
> >>On Sep 6, 2006, at 12:50 PM, Josef Wolf wrote:
> >>>On Wed, Sep 06, 2006 at 04:00:45AM +0900, Morton Goldberg wrote:


> >I _never_ see two different menus unless I introduce the
> >handled_by_root flag.

>
> You _can_ see different menus if you change the posted version of
> your code to assign each menu to a different variable. I can't
> comment on your original code -- I've never seen it. But your posted
> code will work much better if you use different variables for each
> menu. Please try it yourself and see what happens.


Well, I tried and I see different menus only when I use different
variables _and_ modify the code to use the handled_by_root flag. This
is probably because "dismissing a menu" has different effects in OS-X
than on KDE.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
how to couper contenier of a canvas in an outer canvas??? olsr.kamal@gmail.com Python 10 03-15-2013 08:46 PM
Select menus above flyout menus? help? news.west.cox.net HTML 8 12-09-2004 12:54 PM
Canvas with scrollbars - how to get correct canvas coordinate when the scroll bars have moved? PhilC Python 2 10-25-2004 11:57 AM
Canvas scrolling - scrollBar become "disabled" on change in canvas Askari Python 2 08-30-2004 02:56 PM



Advertisments