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).

Read more...

FXRuby :: Hello, World!

>> Tuesday, May 11, 2010

Hello! And welcome to the first tutorial! In these tutorials I'll try to explain how to use FXRuby in an efficient Ruby-ish way, and try to show how easy and useful it is! In each tutorial we are going to make a program to explain the tutorial topics better, I'm a fan of real-life examples, and although we are not going to make complete applications, the programs will show FXRuby features nicely (I hope!).

In this first tutorial I'll explain the very basics about FXRuby, and how to make the most simplistic program, the "Hello, World!". I'll assume normal Ruby knowledge. If you don't know Ruby or wanna refresh some stuff, here is a free online book, and here is another :).
Okay, let's begin, first of all you have to install FXRuby, the good news are, that FXRuby is packed with the standard ruby distribution, so if you have ruby you already have FXRuby :)
Let's begin doing the almost mandatory "Hello, World!" program. That program is normally a program which only output is "Hello, World!".
As we are doing GUI here, we could translate that into a window which writes "Hello, World!" somewhere in there, so let's do that.

#!/usr/bin/env ruby

require 'fox16'

app = Fox::FXApp.new

mainWindow = Fox::FXMainWindow.new(app, "Hello, World!", :width => 300, :height => 200)
mainWindow.show(Fox::PLACEMENT_SCREEN)

app.create
app.run

When you run that code, you'll see something like this.


That's it, we made a window, and if you have worked with some other GUI frameworks you'll know that, that was pretty easy :)
Let's review the code, first of all, we require fox16, which is the gem we are going to use.
All Fox applications have one FXApp object, which is the actual application, we are gonna add all the "stuff" there.
Next we create a FXMainWindow, as you can see by looking at the documentation, it has some optional parameters, but the required ones are app, and title, as a title we used "Hello, World!".

Note for Java programmers: One thing that makes different Fox from Swing, is that, in Swing, you have a parent controller and you add a child to the parent controller, in Fox, you specify the parent of the child controller, and that's how you add new components.

FXMainWindow also has some optional parameters, we sent :width and :height in order to specify...well...the window width and height :)
By default all windows are hidden, so we call the show method of the FXMainWindow, we pass the PLACENEMT_SCREEN argument just so it centers on the screen.

The next lines of code are required in order to run the application, I'll explain them in more detail later on, but by now, just know that you'll need to call create and run.

That works great for what we want to do, but it's not a very Ruby-ish way, we could write exactly the same with this code

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloWorldWindow &lt; FXMainWindow
  def initialize(app)
    super(app, "Hello, World!", :width => 300, :height => 200)
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  app = FXApp.new
  HelloWorldWindow.new(app)
  app.create
  app.run
end

As you can see, it looks like a normal Ruby program, but one thing is very important, note we can inherit from the FXMainWindow class, if you have done Swing, this will look very familiar, and it's a very good thing! That means we can extend the base class and add our own functionality to each of Fox's widgets, in a very easy way.
If you run the program now, you'll see exactly the same output, the same window we saw before.
Finally, let's just add a label with the "Hello, World!" text, we can add a label using the FXLabel class, in this case we just want the default behaviour so we wont create a new class for the label.

#!/usr/bin/env ruby

require 'fox16'
include Fox

class HelloWorldWindow &lt; FXMainWindow
  def initialize(app)
    super(app, "Hello, World!", :width => 300, :height => 200)
    FXLabel.new(self, "Hello, World!")
  end
  
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  app = FXApp.new
  HelloWorldWindow.new(app)
  app.create
  app.run
end

If you look at the documentation for the FXLabel class you'll know we just need to send the parent widget and the text we want to display as parameters.
If you run the program you'll see something like this


We have done our "Hello, World!" program in a very extensible and object oriented way, a very Ruby-ish way, that will allow us to extend the application very easily.
That's all for this tutorial, hope you find it useful! No downloads for this tutorial as it's only the source code you can find above.

Read more...

FXRuby - Project

>> Monday, May 10, 2010

I've spent quite a while deciding on which Ruby GUI library to use, and I decided I'll go for the simple, FXRuby is a great choice, I'm so happy I could get the book I'll write a progressive series of tutorials about FXRuby here :)
I decided to go back to Ruby GUI because I'm planning on some nice projects, and I always liked Ruby over Python, I dont really know why, but to make a long story short, I'll make a list with the benefits of FXRuby

  • You can make an OpenGL Canvas to use OpenGL and 3D in your program
  • It's included in Windows and Mac distribution of Ruby
  • It looks the same on all OS's, and I think it looks nice :) 
  • It's fast, as its basically a Ruby abstraction of the original C++ library (although there is a good part of FXRuby written in ruby)
  • It's Object Oriented, this makes it very easy to extend each widget, as I'm used to Java Swing, this is a very important point for me, as I find this very convenient
  • It's easy to learn compared to other huge libraries
  • And more :) 
There is a nice documentation on this, but it's not so popular, so i decided to help it out by writing my own tutorial serie, once I finish, the series will be available in PDF and examples code.
The first tutorial will be out soon, be ready!

Read more...

NekoCM Utils released as Open Source!

>> Wednesday, May 5, 2010

You can now contribute to NekoCM Utils! Although I dont know if many people is interested right now ;)
I will surely have more projects later, anyways, feel free to contribute / edit / use / etc.
NekoCM Utils is released under the LGPL licence.
You can find the mercurial repository here: http://www.bitbucket.org/fedekun/nekocm-utils

Enjoy~!

Read more...

Patatamosh

>> Sunday, May 2, 2010

Patatamosh will be a 2D fighting game which I will develop in order to test BIP so far. I also want to make it very extensible, so characters code is embeded inside Patatamosh.
Characters code is in Groovy, which is a very nice interpreted dynamically typed OO language, and can also be used as normal Java. So you could just use Java to write your own characters code :)
So far it's going pretty smooth, I will post all updates here. Wish me luck! 

Read more...

BIP Upgrades

>> Saturday, May 1, 2010

Well I've worked a bit more on BIP, the Input class now read a sequence of keys with a given delay, useful to check for actions if you are makign for example a fight game.
Also the Sprite class has been modified, the way to load spritesheets has changed, now it loads spritesheets with no requirement of it to have the same size for all images, it reads spritesheets generated by this program (which i reccommend by the way).
Another thing that changed from the Sprites class is the way to add animations, now, when you pack the spritesheet each image has it name you can use to add animations, for example say you have the images

  • wk_punch_1.png
  • wk_punch_2.png
  • wk_punch_3.png
  • wk_punch_4.png
You could add an animation by doing this

addAnimation("wk_punch", new String[]{ "wk_punch_1", "wk_punch_2", "wk_punch_3", "wk_punch_4" });

Way easier IMHO.
Also I fixed some bugs with Mouse Input and finished a little Button class, inside the gui subpackage. In the future I think it will have some more basic gui components, nothing fancy, I'll see how hard it is to give a simple interface to use swing or awt, BIP is all about simple interfaces :)

Read more...

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

Back to TOP