FXRuby :: Events

>> Thursday, May 13, 2010

As you might know, when we do graphical user interface, we are actually doing an event driven application. What does it mean? Simple, our application recieve and respond to events, normally, it does nothing unless something happens, for example, until you press a button, or close the window.
This concept is pretty general, almost every program you use which uses GUI (Graphical User Interface) does nothing when you do not make any event (except of course for repainting sometimes, and maybe auto-save and other features, but, you know what I mean :)), in this tutorial I'll teach you the basics about handling events in FXRuby.
Let's make a program with a button in it.

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloButton < FXMainWindow
  def initialize(p)
    super(p, "Hello Button", :width => 100, :height => 100)
    
    @button = FXButton.new(self, "Push me", :opts => BUTTON_NORMAL|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  FXApp.new do |app|
    HelloButton.new(app)
    app.create
    app.run
  end
end

Good, hopefully you understand that code, basically, unless you are familiar with FXRuby I recommend taking a look at the documentation for each component. So you know the constructor, and the basic behaviour.
If you take a look at the FXButton documentation, you'll see an event list, each with a brief description, those are all the events the FXButton generates.
The events for FXButton looks like this

SEL_KEYPRESS:sent when a key goes down; the message data is an FXEvent instance.
SEL_KEYRELEASE:sent when a key goes up; the message data is an FXEvent instance.
SEL_LEFTBUTTONPRESS:sent when the left mouse button goes down; the message data is an FXEvent instance.
SEL_LEFTBUTTONRELEASE:sent when the left mouse button goes up; the message data is an FXEvent instance.
SEL_COMMAND:sent when the button is clicked.


All constants beginning with SEL_ are called Message Types, the SEL_COMMAND is a special message type, as it's the default and most obvious behaviour, and the one you'll use the most. Another thing you'll see when dealing with events are Message Identifiers and Data Objects, message identifieras are basically an identifier of the widget which generated the event, and data objects depends on the widget, it gives extra information on the event.
If you take a look at the FXButton events list up there, you'll see that the data object is a FXEvent, the documentation for this object tells you all it's atributes and methods. You can find the atributes list below:


click_button  [R]  Mouse button pressed [Integer]
click_count  [R]  Click count [Integer]
click_time  [R]  Time of mouse button press [Integer]
click_x  [R]  Window-relative x-coordinate of mouse press [Integer]
click_y  [R]  Window-relative y-coordinate of mouse press [Integer]
code  [R]  Button, keysym or mode; DDE source [Integer]
last_x  [R]  Window-relative x-coordinate of previous mouse location [Integer]
last_y  [R]  Window-relative y-coordinate of previous mouse location [Integer]
root_x  [R]  Root window x-coordinate [Integer]
root_y  [R]  Root window y-coordinate [Integer]
rootclick_x  [R]  Root window x-coordinate of mouse press [Integer]
rootclick_y  [R]  Root window y-coordinate of mouse press [Integer]
state  [R]  Keyboard/modifier state [Integer]
target  [R]  Target drag type being requested [Integer]
text  [R]  Text of keyboard event [String]
time  [R]  Time of last event [Integer]
type  [R]  Event type [Integer]
win_x  [R]  Window-relative x-coordinate [Integer]
win_y  [R]  Window-relative y-coordinate [Integer]

As you can see, we have many atributes we can use to get more info on the event, you can make this with every widget, get the Message Type and Data Object information.
If you haven't realised so far, you'll need to have the documentation handy when writing your first applications, until you get very confident as to do them by heart. Documentation is always handy :)

Back to our code, let's see how to connect an event to our code, in earlier FXRuby versions and original Fox library, the way to do it is pretty harder, but luckly for us, FXRuby makes it easy for us now!

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloButton < FXMainWindow
  def initialize(p)
    super(p, "Hello Button", :width => 100, :height => 100)
    
    @button = FXButton.new(self, "Push me", :opts => BUTTON_NORMAL|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)
    @button.connect(SEL_COMMAND, method(:btnPressed))
  end
  
  def btnPressed(source, msgtype, data)
    FXMessageBox.information(self, MBOX_OK, "Click!", "You clicked the button!")
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  FXApp.new do |app|
    HelloButton.new(app)
    app.create
    app.run
  end
end

As you can see, we use the connect method which every widget has, to connect the widget with an event. There are two ways to do it, that is just one, I'll show the other one soon.
With this method, the connect method takes two arguments, the Message Type, and the name of the function it'll call, please note that the function must use three arguments, one for the sender, other for the selector and the last one for the Data Object. The first argument is the sender of the message, it's just a reference to the object with sent the event, the next object its called selector, is a mix of the Message Type and Message Identifier, you have a method to manipulate that and get the Type or the Identifier as you like.
As you can see in the documentation, the SEL_COMMAND Type of the FXButton class does not have a data object, so we cannot get more information on the event, we just know the button was pressed, and in most of the cases, that's all you need to know.

Let's take a look at the other way of making a connection, and lets use the data object, we'll need to connect to other event though, so we'll connect with the SEL_LEFTBUTTONRELEASE event.

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloButton < FXMainWindow
  def initialize(p)
    super(p, "Hello Button", :width => 100, :height => 100)
    
    @button = FXButton.new(self, "Push me", :opts => BUTTON_NORMAL|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)
    @button.connect(SEL_LEFTBUTTONRELEASE) do |sender, selector, data|
      x = data.click_x
      y = data.click_y
      FXMessageBox.information(self, MBOX_OK, "Click!", "You clicked at [#{x},#{y}]")
    end
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  FXApp.new do |app|
    HelloButton.new(app)
    app.create
    app.run
  end
end

As you can see when you click a few times, the location is relative to the widget, and the buttons stay as pressed, you can change that, but for now, we'll just leave it like that.

So you learnt how to connect events, and using the documentation to get information about events and data objects, hopefully you'll now be able to catch any event you need.
I'm gonna explain one more thing though, sometimes, you have to update data "automatically", for example, while an user type his name, you can save it, or when you modify your data, you want your GUI to reflect those changes.
This is done by calling the SEL_UPDATE event, the application sends SEL_EVENTS once in a while, so you can use this event to update your model1 data.
It may seem hard to understand but it's really very easy, check out this example

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloButton < FXMainWindow
  def initialize(p)
    super(p, "Hello Button", :width => 100, :height => 100)
    
    @activeMode = true
    
    @button = FXButton.new(self, "Push me", :opts => BUTTON_NORMAL|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)
    
    @button.connect(SEL_COMMAND) do |sender, selector, data|
      @activeMode = false
    end
    
    @button.connect(SEL_UPDATE) do |sender, selector, data|
      if !@activeMode
        @button.state = STATE_ENGAGED
      end
    end
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  FXApp.new do |app|
    HelloButton.new(app)
    app.create
    app.run
  end
end

As you can see, we connect the button state to the activeMode variable. Whenever that variable is changed, no matter how it was changed, the button will update.
You can also use this to update a widget when another widget is hidden, for example.

Okay, that's it for the Events, hopefully you'll now have a basic understanding of how FXRuby manages them.

*1: See Model View Controller (MVC).

Post a Comment

  © Blogger template Simple n' Sweet by Ourblogtemplates.com 2009

Back to TOP