Today we’re going to take a quick look at how to create a N900 app by taking a simple existing Ruby on Rails application and turning it into a Maemo app using Qt on Rails. The main thrust of this blog post is to show how you would tweak the skeleton app generated by the Qt on Rails framework into something that might be useful in the real world. The Match Schedule app is very basic and only shows the upcoming fixtures for the day. But most iPhone apps are simple thin wrappers around a data layer anyway; and this is really only a proof of concept app, so I’d don’t feel to guilty about my humble achievement.

When the World Cup kicked off, I really wanted to have a schedule app on my N900 and couldn’t find one so hence the motivation. Bear in mind that in 2 days this app will be completely useless as the group stage will be over! Warning: it currently requires a level of technical ability to install this app on N900 as it has no installer. You should check out this related blog post on deploying your Qt on Rails apps on the n900 (Maemo) before tackling this one.

The application (source code) is available for download. Note: I haven’t stripped out unnecessary skeleton code from the application, which would exist immediately after generating the Qt application off the Rails codebase. The unnecessary code is related to Create/Edit/Delete functionality which we won’t need in our simple Match Schedule viewer. I left it in to show the minimum amount of work needed to tweak the generated app into a useful real world program. All in all (blog posts and stuff aside), it took about an hour to do. If I had to do it again I’d imagine it would take less than half that time.

All the following steps are done on your dev machine. At the end of the guide you’ll see how to deploy to your N900.

First we create a Rails app.

rails WorldCup
cd WorldCup
./script/generate scaffold Fixture when:string group:string match:string
rake db:migrate

Then I fired up the web server ./script/server and manually entered the fixtures (stupido! I know!). The ‘When’ field has the date formatted as ‘24/6 - 15:00’.

Next up, we turn the Rails app into a Qt app using Qt on Rails. We are still in the WorldCup directory.

./script/plugin install git://github.com/theirishpenguin/qtonrails.git
./script/generate qtify Fixture

This generates the skeleton Qt app. Now let’s bend it into shape, starting with the UI. From now on we’ll be working in the qtonrails/ plugin directory.

cd vendor/plugins/qtonrails
designer-qt4 app/qdesigns/qmainwindow.ui

Once Qt Designer appears, remove the File menu, Commandlink navigation buttons and Action buttons (by more or less right-clicking on those widgets and deleting)

Then regenerate a Ruby code version of the ui files (every time you change the .ui file using Qt Designer you need to do this)

rbuic4 app/qdesigns/qmainwindow.ui -x -o app/ui_proxies/qmainwindow.ui.rb

./run # or it that doesn't work try: ruby run

Then I got errors :-). Based on these errors, I changed the following…

From app/qpresenters/main_window_presenter.rb I deleted

connect(@ui.viewButton, SIGNAL('clicked()'), self, SLOT('view_clicked()'))

connect(@ui.newButton, SIGNAL('clicked()'), self, SLOT('new_clicked()'))

connect(@ui.editButton, SIGNAL('clicked()'), self, SLOT('edit_clicked()'))

connect(@ui.deleteButton, SIGNAL('clicked()'), self, SLOT('delete_clicked()'))

connect(@ui.fixturesNavLinkButton, SIGNAL('clicked()'), self, SLOT('fixtures_nav_clicked()'))

connect(@ui.actionQuit, SIGNAL('triggered()'), self, SLOT('close()'))

Now let’s try again

./run

Hey it worked! Cool! There’s more stuff we could now delete but we won’t as we’re focusing on doing the bare minimum.

In order to allow a column to be correctly resized and to provide row select behaviour (as opposed to having individual clickable cells), I added the following line just before the end of the initialize() method in app/qpresenters/main_window_presenter.rb

@tableview.resizeColumnsToContents()
@tableview.setSelectionBehavior(Qt::AbstractItemView::SelectRows)

The resizing of columns to fit their contents will probably become the default in a future Qt on Rails release.

Due to silly bug in Qt on Rails that tries to pull an unnecessary KDE library into generated applications (Issue 2 on the GitHub Tracker), we need to remove the line require 'korundum4' from vendor/plugins/qtonrails/app/ui_proxies/qmainwindow.ui.rb vendor/plugins/qtonrails/app/ui_proxies/fixture_qform.ui.rb

In order to display just today’s fixtures, we can change the index action in app/qcontrollers/fixtures_controller.rb (again under the qtonrails/ plugin directory)

def index
  accept_current_fixtures_from Fixture.all
end

… and add the private method

def accept_current_fixtures_from(fixtures)
  fixtures.reject do |fixture|
    dt = fixture.when.split(' - ')[0] # Get date from string
    date_args = (dt.split('/') + ["2010"]).reverse.map &:to_i
    Date.new(*date_args) < Date.today
  end
end

Note: The application source code available has the accept_current_fixtures_from() call commented out. This is because once the World Cup group stage is over in a couple of days the list of fixtures would be empty. I have decided that the value of this app as a useful demo in future outweighs the needs of my users over the next two days :-). In the source code you can simply add the call back in yourself if you wish.

Finally, we make the grid readonly. Because it was late when I did this, I skipped any fancy meta-programming and simply reopened the QtrTableModel to do so. Add this to config/environment.rb

class QtrTableModel
  def flags(index)
    return Qt::ItemIsSelectable | super(index)
  end
end

Phew! Done! To deploy the app to your N900, read the instructions at deploying your Qt on Rails apps on the n900 (Maemo).

Well, hopefully you’ve gotten a flavour of how to use Qt on Rails in a simple real world N900 app. If you’ve any feedback then please get in touch! Until the next time, enjoy the World Cup and I hope your country does well!