Ruby/tk - getting started

I wrote my first small tk app (mostly by pasting in code from various
examples and reading through the tcl/tk docs), but now I've run into a
wall. The following code displays a canvas and lets the user draw lines
on it; the bit I'm stuck at is getting the x,y coordinates of the
mouseclick to be the coordinates on the canvas, rather than on the
canvas display viewport. (i.e., right now when I scroll, the (x,y) I get
from @canvas.bind("1",...) is not the same (x,y) that line.coords needs,
since they're using two different coordinate systems)

martin

require 'tk'

class PatternDraw
  def initialize(parent)

    TkLabel.new(parent, 'wraplength'=>'4i', 'justify'=>'left',
    'text'=>"Cadlab Pattern Generator."){
      pack('side'=>'top')
    }

    @grid = TkFrame.new(parent) {
      pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
    }
    TkGrid.rowconfigure(@grid, 0, 'weight'=>1, 'minsize'=>0)
    TkGrid.columnconfigure(@grid, 0, 'weight'=>1, 'minsize'=>0)

    _grid = @grid

    # canvas
    @canvas = TkCanvas.new(parent, {
      'background' => 'white',
      'borderwidth' => 2,
      'relief' => 'ridge',
      'scrollregion'=> ['-10c', '-10c', '10c', '10c']
    }) {|c|
      grid('in'=>_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
      'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')

      TkScrollbar.new(parent, 'command'=>proc{|*args| c.yview(*args) }) {|vs|
        c.yscrollcommand(proc{|first,last| vs.set first,last})
        grid('in'=>_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
        'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
      }

      TkScrollbar.new(parent, 'orient'=>'horiz',
      'command'=>proc{|*args| c.xview(*args)}) {|hs|
        c.xscrollcommand(proc{|first,last| hs.set first,last})
        grid('in'=>_grid, 'padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
        'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
      }
    }

    @start_x = @start_y = 0
    @canvas.bind("Motion", proc {|x, y| do_motion(x, y)}, "%x %y")
    @canvas.itembind('all', 'Any-Enter', proc{objEnter @canvas})
    @canvas.itembind('all', 'Any-Leave', proc{objLeave @canvas})
    @canvas.bind("1", proc {|e| do_press(e.x, e.y)})
    @canvas.bind("2", proc{|x,y| @canvas.scan_mark(x,y)}, '%x %y')
    @canvas.bind("3", proc {|e| release_line})
    @canvas.bind('B2-Motion', proc{|x,y| @canvas.scan_dragto(x,y)}, '%x %y')
  end

  def objEnter(c)
    id = c.find_withtag('current')[0].id
    $oldFill = c.itemconfiginfo(id, 'fill')[4]
    c.itemconfigure(id, 'fill'=>'SeaGreen1')
  end

  def objLeave(c)
    id = c.find_withtag('current')[0].id
    c.itemconfigure(id, 'fill'=>$oldFill)
  end

  def do_press(x, y)
    if @current_line
      @current_line.coords @start_x, @start_y, x, y
      @current_line.fill 'black'
      @current_line = nil
      do_press(x,y)
    else
      @start_x = x
      @start_y = y
      @current_line = TkcLine.new(@canvas, x, y, x, y)
    end
  end
  
  def do_motion(x, y)
    if @current_line
      @current_line.coords @start_x, @start_y, x, y
    end
  end
  
  def release_line
    @canvas.delete(@current_line)
    @current_line = nil
  end
end

root = TkRoot.new{ title 'Canvas' }
PatternDraw.new(root)
Tk.mainloop

Hi,

···

From: Martin DeMello <martindemello@yahoo.com>
Subject: ruby/tk - getting started
Date: Wed, 15 Sep 2004 21:19:56 +0900
Message-ID: <5pW1d.419414$M95.315449@pd7tw1no>

on it; the bit I'm stuck at is getting the x,y coordinates of the
mouseclick to be the coordinates on the canvas, rather than on the
canvas display viewport.

Probably, TkCanvas#canvasx (or canvasy) method satisfies you.

From Tcl/Tk's 'canvas' manual,
-----------------------------------------------------------
pathName canvasx screenx ?gridspacing?
       Given a window x-coordinate in the canvas screenx,
       this command returns the canvas x-coordinate that
       is displayed at that location. If gridspacing is
       specified, then the canvas coordinate is rounded to
       the nearest multiple of gridspacing units.
-----------------------------------------------------------

So, on Ruby/Tk, "<canvas>.canvasx(screenx, [gridspacing])"
will return the value which you want.
--
                                  Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Perfect, thanks!

martin

···

Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> wrote:

Hi,

From: Martin DeMello <martindemello@yahoo.com>
Subject: ruby/tk - getting started
Date: Wed, 15 Sep 2004 21:19:56 +0900
Message-ID: <5pW1d.419414$M95.315449@pd7tw1no>
> on it; the bit I'm stuck at is getting the x,y coordinates of the
> mouseclick to be the coordinates on the canvas, rather than on the
> canvas display viewport.

Probably, TkCanvas#canvasx (or canvasy) method satisfies you.