# Zoomable TkCanvas?

**URL:** https://rubytalk.org/t/zoomable-tkcanvas/55283
**Category:** ruby-talk
**Created:** [7 September 2009 16:30 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283 "2009-09-07T16:30:11Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Josef\_Wolf](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@Josef\_Wolf](https://rubytalk.org/u/Josef_Wolf)
#### Post date: [7 September 2009 16:30 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/1 "2009-09-07T16:30:11Z")

</div>

Hello,

It looks like TkCanvas has no methods for zooming in and out. For Perl, I  
have found the Tk::Worldcanvas and Tk::Abstractcanvas modules. Anybody knows  
about something like that for ruby?

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [7 September 2009 19:26 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/2 "2009-09-07T19:26:32Z")

</div>

Josef Wolf wrote:

> It looks like TkCanvas has no methods for zooming in and out. For Perl, I  
> have found the Tk::Worldcanvas and Tk::Abstractcanvas modules. Anybody knows  
> about something like that for ruby?

Those perl modules sound interesting--maybe it would be useful to have them in ruby if someone hasn't done that yet.

I've used Tk's Canvas from tcl and from ruby, and I've always had to implement zooming myself in wrapper classes. One way to do it is tag all canvas objects (or just the ones that zoom), and use the Canvas#scale method on that tag. You have to keep track of the current zoom level (Tk doesn't), and use that to calculate the arguments to #scale. You also have to adjust the scroll bars (using 'configure :scrollregion =\> ...'). Then use xview/yview to keep the current view position in sync with the zoom level.

I think that covers it, but if you're interested, take a look at canvas.rb in my tkar project. Tkar is a process, rather than a library, but it abstracts out details like zooming and provides a basic user interface for controlling zoom, pan, etc.

[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/343516](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/343516)

[http://rubyforge.org/projects/tkar](http://rubyforge.org/projects/tkar)

> **···**
>
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

### Author: ![Josef\_Wolf](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@Josef\_Wolf](https://rubytalk.org/u/Josef_Wolf)
#### Post date: [7 September 2009 22:20 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/3 "2009-09-07T22:20:23Z")

</div>

> Josef Wolf wrote:
> 
> > It looks like TkCanvas has no methods for zooming in and out. For Perl,  
> > I have found the Tk::Worldcanvas and Tk::Abstractcanvas modules. Anybody  
> > knows about something like that for ruby?
> 
> Those perl modules sound interesting--maybe it would be useful to have  
> them in ruby if someone hasn't done that yet.

Yeah, they make life a whole lot easier. In addition to zooming, they:  
- maintain original coordinates at all zoom factors. Thus even after  
&nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
&nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
&nbsp;&nbsp;&nbsp;completely transparent to the user of the module..  
- turn around the y-axis (origin is at left bottom)  
- handle scroll bars + panning

> I've used Tk's Canvas from tcl and from ruby, and I've always had to  
> implement zooming myself in wrapper classes.

Strange, that such a powerful widget is missing such a basic functionality.

> One way to do it is tag all  
> canvas objects (or just the ones that zoom), and use the Canvas#scale  
> method on that tag. You have to keep track of the current zoom level (Tk  
> doesn't), and use that to calculate the arguments to #scale. You also have  
> to adjust the scroll bars (using 'configure :scrollregion =\> ...'). Then  
> use xview/yview to keep the current view position in sync with the zoom  
> level.

Sounds easy enough. Is it really that easy? I think this would work only  
if you do not add or move any items after you have made zoom operations.  
Looks like above mentioned modules do a whole lot more of work. They  
override all of the item creation and modification methods to fix movement  
or addition of new items to the current zoom factor.

> I think that covers it, but if you're interested, take a look at canvas.rb  
> in my tkar project. Tkar is a process, rather than a library, but it  
> abstracts out details like zooming and provides a basic user interface for  
> controlling zoom, pan, etc.
> 
> [http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/343516](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/343516)
> 
> [http://rubyforge.org/projects/tkar](http://rubyforge.org/projects/tkar)

Sounds very interesting.

Thanks, I'll check that out!

> **···**
>
> On Tue, Sep 08, 2009 at 04:26:32AM +0900, Joel VanderWerf wrote:

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [7 September 2009 22:35 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/4 "2009-09-07T22:35:31Z")

</div>

Josef Wolf wrote:

> - maintain original coordinates at all zoom factors. Thus even after  
> &nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
> &nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
> &nbsp;&nbsp;&nbsp;completely transparent to the user of the module..

Yep, tkar does that too--object coordinates are independent of zoom level. TkCanvas coordinates are hidden in the abstraction.

> - turn around the y-axis (origin is at left bottom)

Tkar has an option to flip the y-axis (and also an option to use radians instead of degrees for rotation commands).

> - handle scroll bars + panning

Check.

> > One way to do it is tag all canvas objects (or just the ones that zoom), and use the Canvas#scale method on that tag. You have to keep track of the current zoom level (Tk doesn't), and use that to calculate the arguments to #scale. You also have to adjust the scroll bars (using 'configure :scrollregion =\> ...'). Then use xview/yview to keep the current view position in sync with the zoom level.
> 
> Sounds easy enough. Is it really that easy? I think this would work only  
> if you do not add or move any items after you have made zoom operations.

You are right, that's just the basic idea... tkar does coordinate transforms for all operations (move, rotate, add).

> **···**
>
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

### Author: ![Josef\_Wolf](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@Josef\_Wolf](https://rubytalk.org/u/Josef_Wolf)
#### Post date: [8 September 2009 16:40 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/5 "2009-09-08T16:40:17Z")

</div>

> Josef Wolf wrote:
> 
> > - maintain original coordinates at all zoom factors. Thus even after  
> > &nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
> > &nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
> > &nbsp;&nbsp;&nbsp;completely transparent to the user of the module..
> 
> Yep, tkar does that too--object coordinates are independent of zoom level.  
> TkCanvas coordinates are hidden in the abstraction.

Hmm, somehow I fail to see how this is supposed to work.

I see zoom\_by does the scaling, adjusts the scrollregion and updates the  
view. IMHO, to keep coordinates independent from zoom level, you would need  
to intercept query/movement/creation of the items. Without that

TkcRectangle.new(canvas, [100,100], [300, 200])  
canvas.zoom\_by(2.0)  
TkcRectangle.new(canvas, [100,100], [300, 200])

would result in two rectangles with different sizes.

> > - turn around the y-axis (origin is at left bottom)
> 
> Tkar has an option to flip the y-axis (and also an option to use radians  
> instead of degrees for rotation commands).
> 
> > - handle scroll bars + panning
> 
> Check.

This is done with the help of the Window class, AFAICS. So it is not as  
transparent as the Tk::AbstractCanvas module.

> **···**
>
> On Tue, Sep 08, 2009 at 07:35:31AM +0900, Joel VanderWerf wrote:

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [8 September 2009 17:05 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/6 "2009-09-08T17:05:34Z")

</div>

Josef Wolf wrote:

> > Josef Wolf wrote:
> > 
> > > - maintain original coordinates at all zoom factors. Thus even after  
> > > &nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
> > > &nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
> > > &nbsp;&nbsp;&nbsp;completely transparent to the user of the module..
> > 
> > Yep, tkar does that too--object coordinates are independent of zoom level. TkCanvas coordinates are hidden in the abstraction.
> 
> Hmm, somehow I fail to see how this is supposed to work.
> 
> I see zoom\_by does the scaling, adjusts the scrollregion and updates the  
> view. IMHO, to keep coordinates independent from zoom level, you would need  
> to intercept query/movement/creation of the items. Without that
> 
> TkcRectangle.new(canvas, [100,100], [300, 200])  
> canvas.zoom\_by(2.0)  
> TkcRectangle.new(canvas, [100,100], [300, 200])
> 
> would result in two rectangles with different sizes.

Tkar is intended to be used as a \_process\_ not as a library. Another process (doesn't have to be ruby, doesn't have to be a Tk gui) sends commands to tkar over a pipe or socket. Those commands use the abstract coordinate system.

If you use the \_Tk\_ methods such as TkcRectangle.new, they will use Tk's native coordinates.

The corresponding methods in Tkar are in the primitives.rb file. For example the #rect method. This method understands scaling. It also understands rotation, which Tk primitives do not. These methods aren't designed to be used as a library, though.

> This is done with the help of the Window class, AFAICS. So it is not as  
> transparent as the Tk::AbstractCanvas module.

Different kind of abstraction here--tkar implements a little language to drive animations over IO, it's not a library API.

I think a ruby port of the perl Tk::AbstractCanvas would be useful, but in a different way from tkar. I wrote tkar primarily so that I could do 2D animations in simulink--a ruby library isn't much use for that, but a socket interface is fine (and has the advantage of distributing workload). Also, with a little munging, you can pipe the output of real-time log files and get useful animations. See ps.rb for an example--it filters the output of ps to show a graphical representation of the cpu usage of running processes.

> **···**
>
> > On Tue, Sep 08, 2009 at 07:35:31AM +0900, Joel VanderWerf wrote:
> 
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [8 September 2009 17:05 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/7 "2009-09-08T17:05:37Z")

</div>

Josef Wolf wrote:

> > Josef Wolf wrote:
> > 
> > > - maintain original coordinates at all zoom factors. Thus even after  
> > > &nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
> > > &nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
> > > &nbsp;&nbsp;&nbsp;completely transparent to the user of the module..
> > 
> > Yep, tkar does that too--object coordinates are independent of zoom level. TkCanvas coordinates are hidden in the abstraction.
> 
> Hmm, somehow I fail to see how this is supposed to work.
> 
> I see zoom\_by does the scaling, adjusts the scrollregion and updates the  
> view. IMHO, to keep coordinates independent from zoom level, you would need  
> to intercept query/movement/creation of the items. Without that
> 
> TkcRectangle.new(canvas, [100,100], [300, 200])  
> canvas.zoom\_by(2.0)  
> TkcRectangle.new(canvas, [100,100], [300, 200])
> 
> would result in two rectangles with different sizes.

Tkar is intended to be used as a \_process\_ not as a library. Another process (doesn't have to be ruby, doesn't have to be a Tk gui) sends commands to tkar over a pipe or socket. Those commands use the abstract coordinate system.

If you use the \_Tk\_ methods such as TkcRectangle.new, they will use Tk's native coordinates.

The corresponding methods in Tkar are in the primitives.rb file. For example the #rect method. This method understands scaling. It also understands rotation, which Tk primitives do not. These methods aren't designed to be used as a library, though.

> This is done with the help of the Window class, AFAICS. So it is not as  
> transparent as the Tk::AbstractCanvas module.

Different kind of abstraction here--tkar implements a little language to drive animations over IO, it's not a library API.

I think a ruby port of the perl Tk::AbstractCanvas would be useful, but in a different way from tkar. I wrote tkar primarily so that I could do 2D animations in simulink--a ruby library isn't much use for that, but a socket interface is fine (and has the advantage of distributing workload). Also, with a little munging, you can pipe the output of real-time log files and get useful animations. See ps.rb for an example--it filters the output of ps to show a graphical representation of the cpu usage of running processes.

> **···**
>
> > On Tue, Sep 08, 2009 at 07:35:31AM +0900, Joel VanderWerf wrote:
> 
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

### Author: ![Josef\_Wolf](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@Josef\_Wolf](https://rubytalk.org/u/Josef_Wolf)
#### Post date: [8 September 2009 19:20 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/8 "2009-09-08T19:20:12Z")

</div>

So OK. I thought, although I'm a complete newbie to ruby, I'd try to roll  
my own. At least, that would result in a good exercise. So I started by  
stealing the basics for a scrolled canvas from

[http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-talk/122597?122482-123428](http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-talk/122597?122482-123428)

and applying this patch:

--- lib/scrolledcanvas.rb.orig  
+++ lib/scrolledcanvas.rb  
@@ -6,6 +6,8 @@  
&nbsp;&nbsp;&nbsp;include TkComposite

&nbsp;&nbsp;&nbsp;def initialize\_composite(keys={})  
+ @zoom = 1.0 # need this for the zoom\_by method

> **···**
>
> On Wed, Sep 09, 2009 at 02:05:34AM +0900, Joel VanderWerf wrote:
> 
> > Josef Wolf wrote:
> > 
> > > On Tue, Sep 08, 2009 at 07:35:31AM +0900, Joel VanderWerf wrote:
> > > 
> > > > Josef Wolf wrote:
> > > > 
> > > > > - maintain original coordinates at all zoom factors. Thus even after  
> > > > > &nbsp;&nbsp;&nbsp;zoom operations, events as well as querying/moving/adding items are  
> > > > > &nbsp;&nbsp;&nbsp;done as if the canvas is at zoom factor 1.0. So zoom is handled  
> > > > > &nbsp;&nbsp;&nbsp;completely transparent to the user of the module..
> > > > 
> > > > Yep, tkar does that too--object coordinates are independent of zoom  
> > > > level. TkCanvas coordinates are hidden in the abstraction.
> > > 
> > > I see zoom\_by does the scaling, adjusts the scrollregion and updates the  
> > > view. IMHO, to keep coordinates independent from zoom level, you would need  
> > > to intercept query/movement/creation of the items. Without that
> > > 
> > > TkcRectangle.new(canvas, [100,100], [300, 200])  
> > > canvas.zoom\_by(2.0)  
> > > TkcRectangle.new(canvas, [100,100], [300, 200])
> > > 
> > > would result in two rectangles with different sizes.
> 
> > [...]  
> > I think a ruby port of the perl Tk::AbstractCanvas would be useful, but in  
> > a different way from tkar. [...]
> 
> +  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@h\_scr = TkScrollbar.new(@frame)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@v\_scr = TkScrollbar.new(@frame)
> 
> @@ -23,7 +25,7 @@  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@v\_scr.grid(:row=\>0, :column=\>1, :sticky=\>'ns')
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delegate('DEFAULT', @canvas)  
> - delegate('background', @text, @h\_scr, @v\_scr)  
> + delegate('background', @frame, @h\_scr, @v\_scr) # looked like a typo  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delegate('activeforeground', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delegate('troughcolor', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delegate('repeatdelay', @h\_scr, @v\_scr)
> 
> Then, I copied the zoon\_by, xview and yview methods from your tkar package  
> and commented the call to adjust\_scrollregion to avoid access to the  
> uninitialized @bounds array.
> 
> So at this stage, I have a canvas that can be scrolled and zoomed. Fine.
> 
> But how do I override the methods to create the items? In Perl/Tk, that  
> would be easy, since item creation is done via canvas methods. But in  
> Ruby/Tk, items are created via their own classes (e.g. TkcLine.new(args)  
> or something). There don't seem to exist methods in the Canvas class to  
> create items, which could easily be overridden.
> 
> Any hints?
> 
> PS: here's the current state of affairs:
> 
> #!/usr/bin/env ruby
> 
> require 'tk'
> 
> class TkScrolledCanvas \< TkCanvas  
> &nbsp;&nbsp;include TkComposite
> 
> &nbsp;&nbsp;def initialize\_composite(keys={})  
> &nbsp;&nbsp;&nbsp;&nbsp;@zoom = 1.0
> 
> &nbsp;&nbsp;&nbsp;&nbsp;@h\_scr = TkScrollbar.new(@frame)  
> &nbsp;&nbsp;&nbsp;&nbsp;@v\_scr = TkScrollbar.new(@frame)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;@canvas = TkCanvas.new(@frame)  
> &nbsp;&nbsp;&nbsp;&nbsp;@path = @canvas.path
> 
> &nbsp;&nbsp;&nbsp;&nbsp;@canvas.xscrollbar(@h\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;@canvas.yscrollbar(@v\_scr)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;TkGrid.rowconfigure(@frame, 0, :weight=\>1, :minsize=\>0)  
> &nbsp;&nbsp;&nbsp;&nbsp;TkGrid.columnconfigure(@frame, 0, :weight=\>1, :minsize=\>0)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;@canvas.grid(:row=\>0, :column=\>0, :sticky=\>'news')  
> &nbsp;&nbsp;&nbsp;&nbsp;@h\_scr.grid(:row=\>1, :column=\>0, :sticky=\>'ew')  
> &nbsp;&nbsp;&nbsp;&nbsp;@v\_scr.grid(:row=\>0, :column=\>1, :sticky=\>'ns')
> 
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('DEFAULT', @canvas)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('background', @text, @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('activeforeground', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('troughcolor', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('repeatdelay', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('repeatinterval', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('borderwidth', @frame)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate('relief', @frame)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;delegate\_alias('canvasborderwidth', 'borderwidth', @canvas)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate\_alias('canvasrelief', 'relief', @canvas)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;delegate\_alias('scrollbarborderwidth', 'borderwidth', @h\_scr, @v\_scr)  
> &nbsp;&nbsp;&nbsp;&nbsp;delegate\_alias('scrollbarrelief', 'relief', @h\_scr, @v\_scr)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;configure(keys) unless keys.empty?  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def zoom\_by zf  
> &nbsp;&nbsp;&nbsp;&nbsp;zf = Float(zf)  
> &nbsp;&nbsp;&nbsp;&nbsp;@zoom \*= zf  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;vf = (1 - 1/zf) / 2  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;x0, x1 = xview ; xf = x0 + vf \* (x1-x0)  
> &nbsp;&nbsp;&nbsp;&nbsp;y0, y1 = yview ; yf = y0 + vf \* (y1-y0)  
> &nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;scale 'all', 0, 0, zf, zf  
> &nbsp;&nbsp;&nbsp;&nbsp;adjust\_scrollregion  
> &nbsp;&nbsp;&nbsp;  
> &nbsp;&nbsp;&nbsp;&nbsp;xview "moveto", xf  
> &nbsp;&nbsp;&nbsp;&nbsp;yview "moveto", yf  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def adjust\_scrollregion  
> # configure :scrollregion =\> @bounds.map {|u|u\*@zoom}  
> &nbsp;&nbsp;&nbsp;&nbsp;## if all of canvas can be shown, hide the scroll bars  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def xview(mode=nil, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;if mode and mode == "scroll" and @follow\_xdelta  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;number, what = args  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x\_pre, = xview  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r = super(mode, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x\_post, = xview  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0,y0,x1,y1 = @bounds  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@follow\_xdelta += (x\_post - x\_pre) \* (x1-x0)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif not mode  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super()  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(mode, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;def yview(mode=nil, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;if mode and mode == "scroll" and @follow\_ydelta  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;number, what = args  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y\_pre, = yview  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r = super(mode, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y\_post, = yview  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x0,y0,x1,y1 = @bounds  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@follow\_ydelta += (y\_post - y\_pre) \* (y1-y0)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif not mode  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super()  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(mode, \*args)  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end  
> end
> 
> root = TkRoot.new { title "zoomcanvas" }
> 
> c = TkScrolledCanvas.new(:scrollregion=\>[0,0,500,400],  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:relief=\>"sunken").pack(:expand=\>1,:fill=\>"both")  
> TkcRectangle.new(c, [100,100], [300, 200])  
> c.bind("1", proc{|e| TkcRectangle.new(c, [100,100], [300, 200]) })  
> root.bind("z") { c.zoom\_by(1.5) }  
> root.bind("Z") { c.zoom\_by(1/1.5) }
> 
> Tk.mainloop

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [8 September 2009 20:29 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/9 "2009-09-08T20:29:47Z")

</div>

Josef Wolf wrote:

> But how do I override the methods to create the items? In Perl/Tk, that  
> would be easy, since item creation is done via canvas methods. But in  
> Ruby/Tk, items are created via their own classes (e.g. TkcLine.new(args)  
> or something). There don't seem to exist methods in the Canvas class to  
> create items, which could easily be overridden.

Maybe subclass (or delegate to) the Tk classes:

class MyRectangle \< TkcRectangle  
&nbsp;&nbsp;&nbsp;def initialize(x,y,w,h)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(...) # adjust args depending on zoom level etc.  
&nbsp;&nbsp;&nbsp;end  
end

> **···**
>
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

### Author: ![Josef\_Wolf](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@Josef\_Wolf](https://rubytalk.org/u/Josef_Wolf)
#### Post date: [8 September 2009 21:30 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/10 "2009-09-08T21:30:23Z")

</div>

Thanks for your patience with me, Joel!

> Josef Wolf wrote:
> 
> > But how do I override the methods to create the items? In Perl/Tk, that  
> > would be easy, since item creation is done via canvas methods. But in  
> > Ruby/Tk, items are created via their own classes (e.g. TkcLine.new(args)  
> > or something). There don't seem to exist methods in the Canvas class to  
> > create items, which could easily be overridden.
> 
> Maybe subclass (or delegate to) the Tk classes:
> 
> class MyRectangle \< TkcRectangle  
> &nbsp;&nbsp;def initialize(x,y,w,h)  
> &nbsp;&nbsp;&nbsp;&nbsp;super(...) # adjust args depending on zoom level etc.  
> &nbsp;&nbsp;end  
> end

That would result in:  
- lots of new subclasses, polluting namespaces  
- lots of code duplication  
- it would not be a drop-in replacement: one would have to change the  
&nbsp;&nbsp;&nbsp;class names of the items when switching from standard canvas to the  
&nbsp;&nbsp;&nbsp;improved canvas

Maybe extending TkcItem would be a better solution. Something like:

&nbsp;&nbsp;&nbsp;class TkcItem  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alias orig\_initialize initialize  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def initialize(parent, \*args)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# do whatever we need  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;orig\_initialize parent, args  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;end

Opinions?

> **···**
>
> On Wed, Sep 09, 2009 at 05:29:47AM +0900, Joel VanderWerf wrote:

---

<div class="post-metadata">

### Author: ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)
#### Post date: [8 September 2009 22:16 UTC](https://rubytalk.org/t/zoomable-tkcanvas/55283/11 "2009-09-08T22:16:28Z")

</div>

Josef Wolf wrote:

> Thanks for your patience with me, Joel!
> 
> > Josef Wolf wrote:
> > 
> > > But how do I override the methods to create the items? In Perl/Tk, that  
> > > would be easy, since item creation is done via canvas methods. But in  
> > > Ruby/Tk, items are created via their own classes (e.g. TkcLine.new(args)  
> > > or something). There don't seem to exist methods in the Canvas class to  
> > > create items, which could easily be overridden.
> > 
> > Maybe subclass (or delegate to) the Tk classes:
> > 
> > class MyRectangle \< TkcRectangle  
> > &nbsp;&nbsp;def initialize(x,y,w,h)  
> > &nbsp;&nbsp;&nbsp;&nbsp;super(...) # adjust args depending on zoom level etc.  
> > &nbsp;&nbsp;end  
> > end
> 
> That would result in:  
> - lots of new subclasses, polluting namespaces  
> - lots of code duplication  
> - it would not be a drop-in replacement: one would have to change the  
> &nbsp;&nbsp;&nbsp;class names of the items when switching from standard canvas to the  
> &nbsp;&nbsp;&nbsp;improved canvas

It's a matter of taste, I suppose. Preserving the existing Tk classes has an advantage: if you want to place objects on the canvas that are not affected by zoooming (foreground, OSD-type display, controls, etc), you can still use the base classes.

Keep namespaces clean by putting everything in a module:

module MyTk  
&nbsp;&nbsp;&nbsp;class TkcRectangle \< ::TkcRectangle  
&nbsp;&nbsp;&nbsp;...

That way, you have both:

MyTk::TkcRectangle.new # new kind  
TkcRectangle # old kind  
::TkcRectangle # old kind even when in MyTk scope

I don't see a problem with new subclasses or code duplication.

> **···**
>
> > On Wed, Sep 09, 2009 at 05:29:47AM +0900, Joel VanderWerf wrote:
> 
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
