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 t
he 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 model
1 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).
Read more...