Ruby Forum wxRuby > xrcise tutorial, undefined method 'upper_bt'

Posted by Tim Field (Guest)
on 02.11.2007 08:53
(Received via mailing list)
Hi
I'm trying to follow the tutorial on xrcise at
http://wxruby.rubyforge.org/wiki/wiki.pl?UsingXRCise

But I keep getting the following error when I try to run the example;
tutorial.rb:21:in `initialize': undefined method 'upper_bt' for
#<CaseChangeFrame:0x2d610f8> (NoMethodError)
        from tutorial.rb:28:in `new'
        from tutorial.rb:28:in `on_init'
        from 
c:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.2-i386-mswin32/lib/wx/classes/app.rb:16:in
`main_loop'
        from 
c:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.2-i386-mswin32/lib/wx/classes/app.rb:16:in
`run'
        from tutorial.rb:27

The relevant ruby code looks like.

 # Inherit from the generated base class and set up event handlers
 class CaseChangeFrame < TextFrameBase
   def initialize
     super
     evt_button(upper_bt) { text_box.upcase! }
     evt_button(lower_bt) { text_box.downcase! }
   end
 end

I have triple checked that I have given the button widget an Id name of
upper_bt in DialogBlocks

Has anyone come across this before?

Thanks
Tim
Posted by Alex Fenton (Guest)
on 02.11.2007 12:19
(Received via mailing list)
Hi Tim

Tim Field wrote:
> `main_loop'
>         from 
> c:/ruby/lib/ruby/gems/1.8/gems/wxruby-1.9.2-i386-mswin32/lib/wx/classes/app.rb:16:in 
> `run'
>         from tutorial.rb:27
Thanks for the report. Hard to say what's going on here - could you post
the my_frame.xrc that you're running XRCise against, and also the
my_frame.rb that's been generated?

alex
Posted by Ash Wilson (Guest)
on 02.11.2007 22:03
(Received via mailing list)
To my eyes, this looks like a simple syntax slip.  The argument to
evt_button is being interpreted as a method call, not a symbol as 
intended.
Note the colons:

evt_button(:upper_bt) { text_box.upcase! }
evt_button(:lower_bt) { text_box.downcase! }

- Ash
Posted by Alex Fenton (Guest)
on 02.11.2007 22:21
(Received via mailing list)
Ash Wilson wrote:
> To my eyes, this looks like a simple syntax slip.  The argument to 
> evt_button is being interpreted as a method call, not a symbol as 
> intended.  Note the colons:
>
> evt_button(:upper_bt) { text_box.upcase! }
> evt_button(:lower_bt) { text_box.downcase! }
The first argument to evt_button, as to the other event handlers that
capture commands from Controls, is a Window or a Window id that is the
source of the vents

So these should be methods which return a Wx::Button

alex
Posted by Tim Field (Guest)
on 03.11.2007 01:42
Attachment: my_frame.rb (881 Bytes)
Attachment: tutorial.xrc (2,3 KB)
Attachment: tutorial.rb (702 Bytes)
(Received via mailing list)
Hi Alex

Thanks for taking a look, Here are the files, I also tried the syntax
changes mentioned by Ash but this didn't work either,
resulted in this error.
Must specify Wx::Window event source or its Wx id, not ':upper_bt'
(ArgumentError)
I'm fairly new to ruby syntax so it wouldn't be beyond me to have such a
problem but seems its something else

Tim
Posted by Mario Steele (Guest)
on 03.11.2007 02:26
(Received via mailing list)
Hey Tim and Alex,

Tim Field wrote:
> Tim
Wow, must be one of the first fast ones I've actually debugged here.
I've done my own debugging, and it looks like, your trying to setup the
ID for your objects, and they haven't even been defined yet.  And for
you Alex, in order to access "finder"  You should proabbly store that as
a Method Call, or store it as a Class Variable, so that it can be
accessed, in this sistuation.

Anyways, the core problem, is first, for the method at which your
generating the code, and adding to it, you need to change the "finder"
variable in my_frame.rb  in TestFrameBase#initialize() to a class
variable, so it can be accessed by sub-classed versions of the
TestFrameBase.  You can do this easily by just adding an '@' symbol to
it.  Nothing to it really.

Next, in your sub-classed version in tutorial.rb, I made a few changes.
Basically, I added the three calls to
@finder.call("id_we_are_looking_for") for upper_bt, lower_bt, and
text_box, then changed the upcase and downcase to the following:
text_box.value = text_box.value.upcase/downcase As you can modify the
string with upcase!/downcase!, but it doesn't modify it in the wxRuby
Buffer, so you need to re-assign it to the buffer, so that it will
convert over properly for you, and display the changes.  The New code
goes as follows:

======================my_frame.rb=======================

# This class was automatically generated from XRC source. It is not
# recommended that this file is edited directly; instead, inherit from
# this class and extend its behaviour there.
#
# Source file: tutorial.xrc
# Generated at: Sat Nov 03 13:22:37 +1300 2007

class TextFrameBase < Wx::Frame

  def initialize(parent = nil)
    super()
    xml = Wx::XmlResource.get
    xml.flags = 2 # Wx::XRC_NO_SUBCLASSING
    xml.init_all_handlers
    xml.load("tutorial.xrc")
    xml.load_frame_subclass(self, parent, "ID_WXFRAME")

    @finder = lambda do | x |
      int_id = Wx::xrcid(x)
      begin
        Wx::Window.find_window_by_id(int_id, self) || int_id
      # Temporary hack to work around regression in 1.9.2; remove
      # begin/rescue clause in later versions
      rescue RuntimeError
        int_id
      end
    end

  end
end

=====================tutorial.rb===========================
require 'wx'
 # load in the generated code
 require 'my_frame'

 # Mix-in for a Wx::TextCtrl
 module CaseChangeTextCtrl
   # convert all the text in the control to upper case
   def upcase!
     self.value = self.value.upcase
   end
   # convert all the text in the control to lower case
   def downcase!
     self.value = self.value.downcase
   end
 end

  # Inherit from the generated base class and set up event handlers
 class CaseChangeFrame < TextFrameBase
   def initialize
     super
     upper_bt = @finder.call("upper_bt")
     lower_bt = @finder.call("lower_bt")
     text_box = @finder.call("text_box")
     evt_button(upper_bt) { text_box.value = text_box.value.upcase }
     evt_button(lower_bt) { text_box.value = text_box.value.downcase }
   end
 end

  # Run the class
 Wx::App.run do
   CaseChangeFrame.new.show
 end


============================================================

Enjoy the new code, and Alex, you should look into this, incase someone
decides they want to do something similar to this.  This would exactly
be the way that I would want to do it.

L8ers,

Mario Steele
Posted by Tim Field (Guest)
on 03.11.2007 09:07
(Received via mailing list)
Hi Mario

Wow thanks for that sure enough it works!

Do you think the tutorial at
http://wxruby.rubyforge.org/wiki/wiki.pl?UsingXRCise should be updated, 
I'm
surprised this hasn't been an issue for someone else?

Also I was thinking in php (i work with php for a living) there are
special get and set methods you can define which are only triggered when
your object or inherited object doesn't contain the member or method 
you're
trying to access. 
(http://nz.php.net/manual/en/language.oop5.overloading.php
)

so in this example in php I would write something like.

function __get($name){
   if($element = $this->finder($name))
            return $element;
   trigger_error("undefined property $name");
}

Maybe such a function would be useful in the base class in the tutorial
example to look up the form elements thus preventing the need for the
"upper_bt = @finder.call("upper_bt")" lines.

Anyway I had a good look at the ruby manual I have and couldn't find a 
way
to do this, I'm sure there is? or perhaps you guys can think of a more
elegant solution?

Thanks again for helping me though that issue guys!

Tim
Posted by Alex Fenton (Guest)
on 03.11.2007 16:47
(Received via mailing list)
Hi Tim

Tim Field wrote:
>
> Thanks for taking a look, Here are the files, I also tried the syntax 
> changes mentioned by Ash but this didn't work either, 
As Mario says, it's the missing finder calls that are the problem, but
these should be in the autogenerated base class, not the user class
you're writing. Finder is meant to be a private implementation detail.

There is something strange going on because when I run xrcise over the
xrc file you sent, it does generate the missing lines in my_frame.rb

  attr_reader :text_box, :upper_bt, :lower_bt

...

    @text_box = finder.call("text_box")
    @text_box.extend(CaseChangeTextCtrl)
    @upper_bt = finder.call("upper_bt")
    @lower_bt = finder.call("lower_bt")

Please could you double-check you're running xrcise over the latest
saved version of your xrc file, as you posted it here.

If those lines are still not showing up, could you say what ruby version
and platform you're on.

thanks
alex

thanks
alex
Posted by Alex Fenton (Guest)
on 03.11.2007 16:57
(Received via mailing list)
Tim Field wrote:
> Also I was thinking in php (i work with php for a living) there are 
> special get and set methods you can define which are only triggered 
> when your object or inherited object doesn't contain the member or 
> method you're trying to access. ( 
> http://nz.php.net/manual/en/language.oop5.overloading.php)
Object#method_missing is what you're looking for.

class Foo
  def method_missing(meth, *args)
    puts "Called non-existing method '#{meth}' with arguments
'#{args.join(',')}'"
  end
end

It's best used with discretion, because it breaks reflection on an
object's supported methods eg,

Foo.new.respond_to?(:blah)
=> false

Even if 'blah' would be a valid method call.

cheers
alex
Posted by Tim Field (Guest)
on 04.11.2007 09:49
(Received via mailing list)
Hi Alex,

Those lines definitely aren't showing up. (when using that xrc file or
another one I have)

I'm running ruby 1.8.6 on winxp

-Tim
Posted by Alex Fenton (Guest)
on 05.11.2007 18:59
(Received via mailing list)
Tim

Tim Field wrote:
> Those lines definitely aren't showing up. (when using that xrc file or 
> another one I have)
>
> I'm running ruby 1.8.6 on winxp
I couldn't reproduce the bug with XP/1.8.4, but I tried installing the
latest 1.8.6 and now can.

It's due to some incompatible change in REXML shipped with ruby 1.8.4
(version 3.1.3) and 1.8.6 (version 3.1.6), somewhere in it's XPath
parsing, but exactly what's needed is eluding me right now.

Anyway, thanks again for the report. We should be able to pin down
what's up and put out a version of wxSugar inc XRCise that works on
1.8.6 in the next week or so.

alex
Posted by Rick Ashton (kly)
on 03.05.2008 15:03
Alex Fenton wrote:
> I couldn't reproduce the bug with XP/1.8.4, but I tried installing the
> latest 1.8.6 and now can.
> 
> It's due to some incompatible change in REXML shipped with ruby 1.8.4
> (version 3.1.3) and 1.8.6 (version 3.1.6), somewhere in it's XPath
> parsing, but exactly what's needed is eluding me right now.
> 
> Anyway, thanks again for the report. We should be able to pin down
> what's up and put out a version of wxSugar inc XRCise that works on
> 1.8.6 in the next week or so.
> 
> alex

Hey I get the same thing... running 1.8.6 on Mac OS X 10.5.2. Got a gem 
of the latest wxSugar 0.1.20 but this still seems to be an issue in this 
version.

Anyone know?

Thx!