Feedback messages to user using Tk

Thanks Morton, however I want the message to change when the program produces feedback for the user, i.e. the program is zipping up files, as these files are zipped I should like to display the incrementing percentage complete. A user will not be clicking a button each time a file is added, unfortunately; 2,500 files would probably cause serious RSI!

Do you know how to update the message without a command/bind event? Another part of the program will be setting the message, like puts.

Ta

Gem

···

-----Original Message-----
From: Morton Goldberg [mailto:m_goldberg@ameritech.net]
Sent: 27 September 2006 12:53
To: ruby-talk ML
Subject: Re: Feedback messages to user using Tk...

               *** WARNING ***

This mail has originated outside your organization,
either from an external partner or the Global Internet.
     Keep this in mind if you answer this message.

On Sep 27, 2006, at 4:26 AM, Cameron, Gemma (UK) wrote:

Hi all!

If anyone is very familiar with Tk I would be very grateful for a
few pointers.

Basically I'm looking to feedback my program's output to the user
in a GUI (Tk Label) rather than printing to the console (which it
currently does). This information would simply be a string to let
the user know what's going on and maybe even what percentage of the
work has been done. I don't want a command/bound event to drive
this - I simply want to send a message to a method which will
update the Tk Label dynamically.

I envision the calling the class like this:

    @output = GraphicalOutput.new
    @output.message("Now moving files....")

to replace the current:
    p "Now moving files..."

Is this possible with Tk? I've been looking for days now and
haven't found much. I know how to use the TkLabel.configure
('text'=>..) etc. to update the text, however just calling this
when the mainloop is running does not seem to dynamically update
the label. I've even got the stopwatch example going from the
O'Reilly cookbook.

Am I using the wrong approach and/or mentality for this problem?!

Ruby/Tk makes it easy to do what you want. You don't need to bother
with low-level methods like 'configure' -- Ruby/Tk provides more
runbyish ways -- in this case access methods for a label's text. I
hope the following small example will help you.

<code>
#! /usr/bin/ruby -w

require 'tk'

DEBUG =

MESSAGES = [
    "This is the first message",
    "This is another message",
    "This is the third message",
    "Are you getting bored?"
]

class TestWindow
    # Each time the button is clicked on, the label shows a new message.
    def btn_action
       @indx = (@indx + 1) % MESSAGES.length
       @lbl.text = MESSAGES[@indx]
    end

    def initialize
       begin
          @indx = 0

          # Set up the widgets.
          root = TkRoot.new { title 'Ruby Tk' }
          @lbl = TkLabel.new { text MESSAGES.first }
          @btn = TkButton.new { text "Next Message" }
          @btn.command = lambda { btn_action }
          @lbl.pack(:pady => 20)
          @btn.pack(:pady => 20)

          # Set initial window geometry; i.e., size and placement.
          win_w, win_h = 300, 135
          # root.minsize(win_w, win_h)
          win_x = (root.winfo_screenwidth - win_w) / 2
          root.geometry("#{win_w}x#{win_h}+#{win_x}+50")

          # Set resize permissions.
          root.resizable(false, false)

          # Make Cmnd+Q work as expected (running on OS X).
          root.bind('Command-q') {Tk.root.destroy}

          Tk.mainloop
       ensure
          puts DEBUG unless DEBUG.empty?
       end
    end
end

TestWindow.new
</code>

Regards, Morton

********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

The fact that I used a button to trigger the change doesn't mean that a user action is required to effect the change. The btn_action method is just an ordinary instance method of TestWindow. Any instance method of the class can do what it does. Also, it can be called by any object that possesses a reference to the window class.

However, from what you describe above, I think you might be interested in the Feedback iwidget. Here is an example of its use:

<code>
#! /usr/bin/ruby -w
# Author: Morton Goldberg

···

On Sep 27, 2006, at 8:24 AM, Cameron, Gemma (UK) wrote:

Thanks Morton, however I want the message to change when the program produces feedback for the user, i.e. the program is zipping up files, as these files are zipped I should like to display the incrementing percentage complete. A user will not be clicking a button each time a file is added, unfortunately; 2,500 files would probably cause serious RSI!

Do you know how to update the message without a command/bind event? Another part of the program will be setting the message, like puts.

#
# Date: September 6, 2006
#
# Progress Indicator 2

require 'tk'
require 'tkextlib/iwidgets'

DEBUG =

begin
    # Build a window containing a progress indicator and a button.
    root = TkRoot.new {title 'Ruby/Tk Progress Indicator'}
    fb = Tk::Iwidgets::Feedback.new(root) {
       steps 20
       labeltext "Click the Button"
       barcolor 'red'
       barheight 20
       troughcolor 'gray90'
    }
    fb.component_widget('trough').
       configure('relief'=>'ridge', 'borderwidth'=>4)
    fb.component_widget('bar').
       configure('relief'=>'sunken', 'borderwidth'=>5)
    fb.pack('fill'=>'x', 'padx'=>15, 'pady'=>10)
    btn = TkButton.new(root) {
       text "Do Something"
       command {btn.action}
    }
    btn.pack('pady'=>10)
    btn.instance_variable_set(:@fb, fb)
    # Starts the timer going when the button is clicked.
    def btn.action
       self.state = 'disable' # Strange -- explicit receiver required
       # Set the timer to trigger at 200 m-sec intervals, once for each
       # progress step.
       $timer = TkTimer.start(200, @fb.steps) {@fb.update}
    end
    fb.instance_variable_set(:@btn, btn)
    # Run on each timer tick.
    def fb.update
       labeltext = "Doing Something ..."
       step
       # loop_rest returns remaining trigger intervals.
       unless $timer.loop_rest > 1
          labeltext = "Click the Button"
          reset
          @btn.state = 'normal'
       end
    end

    # Set initial window geometry; i.e., size and placement.
    win_w, win_h = 300, 160
    # root.minsize(win_w, win_h)
    win_l = (TkWinfo.screenwidth('.') - win_w) / 2
    root.geometry("#{win_w}x#{win_h}+#{win_l}+50")

    # Set resize permissions.
    root.resizable(false, false)

    # Make Cmnd+Q work as expected.
    root.bind('Command-q') {Tk.root.destroy}

    Tk.mainloop
ensure
    puts DEBUG unless DEBUG.empty?
end
</code>

Again, don't get fooled by there being buttons involved in these examples. The buttons are just artifacts needed to keep the examples simple.

Regards, Morton

Morton Goldberg wrote:

The fact that I used a button to trigger the change doesn't mean that
a user action is required to effect the change. The btn_action method
is just an ordinary instance method of TestWindow. Any instance
method of the class can do what it does. Also, it can be called by
any object that possesses a reference to the window class.

However, from what you describe above, I think you might be
interested in the Feedback iwidget. Here is an example of its use:

Thanks for both of the examples. It will take me a while to pick my way
through the code and understand it properly. But I ran both and read
through once so I think there is already things I can use.

Thanks a lot. Im finding this project very slow going so examples like
this help a lot!

Mary

···

--
Posted via http://www.ruby-forum.com/\.