Monday, January 08, 2007

Installing QTRuby on Mac OSX

Over the last few weeks, I've been working to get QT3 working with Ruby. I've been using the book Rapid GUI Development with QtRuby by Caleb Tennis published by Pragmatic Programers (http://www.pragmaticprogrammer.com/titles/ctrubyqt/index.html). I think the book is an excellent introduction to Qt on Ruby.

However, since the book was written a number of things have changed in the versions of software available and the build process and the instructions for Mac OSX as provided in the text aren't exactly right.

Caleb Tennis (the author) has been kind enough to help me get through the installation problems via email, but I wanted to pull everything together into one place to help other Mac users. The text below explains what I've done to get things working on PPC and Intel Macs.

-------------------

Getting Qt Running

in Safari, go to
ftp://ftp.trolltech.com/qt/source

This will mount the trolltech's source directory in Finder and open a window

In the search box, type qt-mac-free and you'll see the mac releases. If you scroll down, you'll see that the most recent release of version 3 is 3.3.7.

Drag and drop "qt-mac-free-3.3.7.tar.gz" onto your Desktop. Once the 15.7mb file has downloaded, eject the mounted source drive (drag it's icon to the trash, or right click and select eject).

Double click the tar.gz file on your desktop and it will uncompress into a folder named "qt-mac-free-3.3.7". You can delete the .tar.gz and the .tar files now.

open a terminal window and (1) move the Qt 3.3.7 folder to /Developer
mv ~/Desktop/qt-mac-free-3.3.7 /Developer
and create a symbolic link called "qt" to that directory
cd /Developer
ln -s qt-mac-free-3.3.7 qt

The next thing you need to do is setup environment variables for Qt.
In the INSTALL file, Qt asks you to put these in .profile if you are using BASH as your shell (BASH is the Tiger default). However, that didn't work for me.

I needed to put them in
.bash_profile
in order for them to be picked up.

So edit your ~/.bash_profile file (emacs, vim, whatever) and add:
##########
# Setup for Qt3
##########
QTDIR=/Developer/qt
PATH=$QTDIR/bin:$PATH
DYLD_LIBRARY_PATH=$QTDIR/lib:$DYLD_LIBRARY_PATH
export QTDIR PATH DYLD_LIBRARY_PATH

close the existing terminal you've been using for editing & open a new one. This will force your .bash_profile to be executed and you can verify that all variables are being correctly set by typing
env
and scanning for QTDIR and that PATH and DYLD_LIBRARY_PATH exist and have
/Developer/qt
/Developer/qt/bin
/Developer/qt/lib

Make a symbolic link for the Qt Documentation
ln -s $QTDIR/doc/man $QTDIR/man

Build Qt by doing:
cd $QTDIR
./configure -thread -no-tablet
(you need to configure the -thread version for QtRuby to work)

type "yes" to accept the terms of the license

once the configure completes, build the software:
/usr/bin/make
(this took about an hour on a 2GHz G5)

--NOTE: The next step (symbolic links) is NOT necessary if you set DYLD_LIBRARY_PATH as described above
--However, it is part of the Qt Install instructions so I'm including it for completeness
setup symbolic links for the dynamic libraries
sudo ln -sf $QTDIR/lib/libqt-mt.3.dylib /usr/lib
sudo ln -sf $QTDIR/lib/libqui.1.dylib /usr/lib

verify Qt built correctly by running the Demo in:
/Developer/qt/examples/demo
from Finder


Getting QtRuby running.

Get a copy of QtRuby by going to
http://rubyforge.org/projects/korundum

on that page, there is a Download link for the package qtruby (version 1.0.13).

Clicking that takes you to the download page. Scroll down to the section for qtruby and then look for
qtruby-1.0.13.tgz
and click on it to download

You should see a "qtruby-1.0.13.tgz" file on your desktop.
Double click that & it will expand and create a folder cladded "qtruby-1.0.13"

Delete the .tgz file

--NOTE: I've been told that later versions of the qtruby package have fixed this issue, so you may not need to do this
go to the
smoke/qt
directory and edit the Makefile.in and change version info on line 233 from
3:4:92
to
3:4:0

After the edit, the line should read:
libsmokeqt_la_LDFLAGS = -version-info 3:4:92 -no-undefined $(KDE_NO_UNDEFINED) \
$(all_libraries) $(GLINC)

also in the
smoke/qt
directory, edit
smokeqt.pro
and change DESTDIR to
/usr/local/lib

finally, go to
../../qtruby/rubylib/designer/rbuic/
and edit
rbuic.pro
changing DESTDIR to
/usr/local/bin


now go back to the top level of QtRuby-1.0.13
cd ~/Desktop/qtruby-1.0.13/
and do a configure
./configure '--with-qt-dir=/Developer/qt' '--enable-mac'

now you're ready to create the makefile
cd smoke/qt
perl generate.pl

stay in the current directory (smoke/qt) and now finish creating the makefile using Trolltech's qmake
$QTDIR/qmake/qmake -makefile

At this point, you're ready to build and install. These are both done when you do a make, but the install part will fail unless you sudo the make so it runs as administrator
sudo make

Next we need to build the 'Qt' extension
cd ~/Desktop/qtruby-1.0.13/qtruby/rubylib/qtruby
ruby extconf.rb --with-qt-dir=/Developer/qt --with-smoke-dir=/usr/local --with-smoke-include=../../../smoke

(note for those who are flipping back and forth between this and the install files that came with the software: because we installed smoke into /usr/local we had to refer to that in the -with-smoke-dir parameter)

make
sudo make install

now build the rbuic
cd ../designer/rbuic/
$QTDIR/qmake/qmake -makefile

Build the 'qui' QWidgetFactory extension
cd ../uilib
ruby extconf.rb --with-qtruby-include=../../qtruby --with-qt-dir=/Developer/qt
make
sudo make install

Ok ... QtRuby should now be installed, so lets test it

Test #1 -- Can Ruby find Qt?
in a terminal window, start irb
irb
and try to load Qt
require 'Qt'

this should return true
=> true

if you get an error instead (dyld: NSLinkModule() error), then you need to check you DYLD_LIBRARY_PATH and make sure it contains "Developer/qt/lib"
env | grep DYLD_LIBRARY_PATH
if you don't see the that present, go back and look at your .bash_profile

Test #2 -- Is Ruby getting and processing events
this test is important because in some cases (for example, you replaced the version of ruby that came with Tiger with one that you built yourself from the 1.8.4 src), you default copy of ruby won't be setup to process events on the Mac correctly and you'll need to create a "rubyw" that can do so.

Copy and paste the following ruby code (taken verbatim from Caleb's book) into a file file called test2.rb:
require 'Qt'
class MyWidget <>
slots 'button_was_clicked()'
def initialize(parent=nil)
super(parent)
@label = Qt::Label.new(self)
@button = Qt::PushButton.new(self)
@layout = Qt::VBoxLayout.new(self)
@layout.addWidget(@label)
@layout.addWidget(@button)
@clicked_times = 0

@label.setText("The button has been clicked #{@clicked_times} times")
@button.setText("MyButton")

connect(@button,SIGNAL('clicked()'),self,
SLOT('button_was_clicked()'))
end

def button_was_clicked
@clicked_times += 1
@label.setText("The button has been clicked #{@clicked_times} times")
end
end

a = Qt::Application.new(ARGV)
mw = MyWidget.new
a.setMainWidget(mw)
mw.show
a.exec

run this by executing the following in terminal:
ruby $PWD/test2.rb

A small window with a label ("The button has been clicked 0 times") and a button should appear. Clicking the button should change the text of the label to indicate how many times you've clicked.

If this works, you're good to go.

If what happens instead is that the button appears to push down, but never pop up and the text never changes, chances are your copy of ruby isn't bound to the correct Mac resource fork.

Run the following:
ruby=`which ruby`
rubyw=${ruby}w
sudo cp $ruby $rubyw
sudo /Developer/Tools/Rez -t APPL Carbon.r -o $rubyw

and then try again, using the rubyw (not ruby) executable:
rubyw $PWD/test2.rb


Hopefully that will work.

I'm not 100% sure about whether you need to actually keep rubyw as a separate executable. I think that you actually could Rez you base ruby executable and the you'd have one copy of the language that could run standard ruby and handle Mac events. I think this is what was done with the default copy that shipped with Tiger. However, I don't know enough of the mac internals to be certain about this, so try it at your own risk (at least save a copy of ruby before overwriting it with rubyw :-))