ruby Qt: model / view / controller

From the wikipedia:

Model-view-controller (MVC) is an architectural pattern used in software engineering. In complex computer applications that present a large amount of data to the user, a developer often wishes to separate data (model) and user interface (view) concerns, so that changes to the user interface will not affect data handling, and that the data can be reorganized without changing the user interface.

MVC is not only useful for web frameworks and applications, here is a simple example of the implementation of the MVC pattern for a Qt GUI application.

For this example the business data (Model) will consist of just a counter. The GUI (View) will have elements to increase and decrease this counter. Since this is a very small proof-of-concept application there is no need for a Controller component. You can have a look at the code before we start.

First the Model class:-

class Model < Qt::Object
  slots 'increase()', 'decrease()'
  signals 'modified()'

  attr :counter

  def initialize
    super
    @counter = 0
  end

  def increase
    @counter += 1
    emit modified()
  end

  def decrease
    @counter -= 1
    emit modified()
  end
end

The Model extends Qt::Object so we can inherit the signals / slots functionality. Signals and slots will be the mechanisms used by our components to talk to each other. The class defines two slots: increase() and decrease(). If an external component wants to modify the data kept by the Model, the component will send a signal to one of these slots. In the same way, one the value of the counter is modified, the Model will emit the modified() signal.

The View class contains only two methods: the constructor and one slot. In the constructor we create the widgets of the interface and then connect the different signals and slots (more on this later). A single slot is defined in the class: counter_modified(). This slot will receive the signal sent by the Model each time the underlaying data is modified. Here is the code:-

class View < Qt::Widget
  slots 'counter_modified()'

  def initialize(model)
    super(nil)

    # keep a reference to the model
    @model = model

    # define widgets and layout
    grid = Qt::GridLayout.new
    @btn1 = Qt::PushButton.new('+1')
    @btn2 = Qt::PushButton.new('-1')
    @lcd = Qt::LCDNumber.new
    grid.addWidget(@btn1)
    grid.addWidget(@btn2)
    grid.addWidget(@lcd)
    self.setLayout(grid)

    # connect signals and slots between the Model and the View
    connect @btn1, SIGNAL('clicked()'), @model, SLOT('increase()')
    connect @btn2, SIGNAL('clicked()'), @model, SLOT('decrease()')
    connect @model, SIGNAL('modified()'), self, SLOT('counter_modified()')
  end


  def counter_modified
    @lcd.display(@model.counter)
  end
end

As mentioned earlier we are not using a Controller component for this example. However, the controller behaviour still exist but is included in the View class.

The Model has two slots and is able to emit a signal. We only need to bind this messages with the corresponding widgets in the user interface. The lines below connect the clicked() of the buttons with the different slots of the Model. Likewise, the modified() signal of the Model is connected to the counter_modified() slot of the View.

connect @btn1, SIGNAL('clicked()'), @model, SLOT('increase()')
connect @btn2, SIGNAL('clicked()'), @model, SLOT('decrease()')
connect @model, SIGNAL('modified()'), self, SLOT('counter_modified()')

When the Model emits the modified() signal, the View will update the value displayed in the Qt::LCDNumber widget.

More complex data models may require more complex patterns, but that is another story and shall be told another time.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Slashdot
  • Technorati

Comments are closed.