Any underscores in method names are removed, and the following character is capitalised. For example, you can use either of these two forms to call the same method:

Any underscores in method names are removed, and the following character is capitalised. For example, you can use either of these two forms to call the same method:

−

create_standard_status_bar_action()

−

createStandardStatusBarAction()

−

==Operator overloading==

+

<syntaxhighlight lang="ruby">

+

create_standard_status_bar_action()

+

createStandardStatusBarAction()

+

</syntaxhighlight>

+

==Operator overloading==

The full range of Qt operator methods is available, for example:

The full range of Qt operator methods is available, for example:

−

p1 = Qt::Point.new(5,5) => (5, 5)

+

<syntaxhighlight lang="ruby">

−

p2 = Qt::Point.new(20,20) => (20, 20)

+

p1 = Qt::Point.new(5,5) => (5, 5)

−

p1 + p2 => (25, 25)

+

p2 = Qt::Point.new(20,20) => (20, 20)

+

p1 + p2 => (25, 25)

+

</syntaxhighlight>

==Declare signals and slots==

==Declare signals and slots==

−

Signals and slots are declared as list of strings like this:

Signals and slots are declared as list of strings like this:

−

slots 'setColor(QColor)', 'slotLoad(const QString&)'..

+

<syntaxhighlight lang="ruby">

−

signals 'clicked()'..

+

slots 'setColor(QColor)', 'slotLoad(const QString&)'..

+

signals 'clicked()'..

+

</syntaxhighlight>

−

Currently C++ type signatures must be used, a future version of QtRuby will allow ruby type signatures instead.

+

For slots and signals without arguments you can use Ruby symbols:

+

+

<syntaxhighlight lang="ruby">

+

slots :slotLoad

+

signals :clicked

+

</syntaxhighlight>

+

+

Currently C++ type signatures must be used, a future version of QtRuby will allow ruby type signatures instead. (see the section on emitting Ruby Classes)

Connect slots and signals like this:

Connect slots and signals like this:

−

Qt::Object.connect( @colormenu, SIGNAL( "activated( int )" ),

+

<syntaxhighlight lang="ruby">

−

self, SLOT( "slotColorMenu( int )" ) )

+

Qt::Object.connect( @colormenu, SIGNAL( "activated(int)" ),

+

self, SLOT( "slotColorMenu(int)" ) )

+

</syntaxhighlight>

+

+

There is also two another possibilities:

+

+

<syntaxhighlight lang="ruby">

+

connect(:mysig, mytarget, :mymethod))

+

connect(SIGNAL('mysignal(int)'), mytarget, :mymethod))

+

</syntaxhighlight>

Or you can connect signal to a block:

Or you can connect signal to a block:

−

quit_button.connect(SIGNAL :clicked) { $qApp.quit }

+

<syntaxhighlight lang="ruby">

+

quit_button.connect(SIGNAL :clicked) { $qApp.quit }

+

</syntaxhighlight>

And emit signals like this:

And emit signals like this:

−

emit colorChanged( black )

+

<syntaxhighlight lang="ruby">

+

emit colorChanged( black )

+

</syntaxhighlight>

−

==Constructors==

+

==Emitting Ruby Classes==

+

Ruby classes can be emitted by embedding them inside a QVariant, and emitting the QVariant.

+

The following code provides a method called to_variant that can be used to easily convert objects to Variants

+

<syntaxhighlight lang="ruby">

+

class Qt::RubyVariant < Qt::Variant

+

def initialize(value)

+

super()

+

@value = value

+

end

+

+

attr_accessor :value

+

end

+

+

class Object

+

def to_variant

+

Qt::RubyVariant.new self

+

end

+

end

+

</syntaxhighlight>

+

+

Note: as of KDE 4.5 you can simply use

+

<syntaxhighlight lang="ruby">

+

variant = Qt::Variant.fromValue(my_ruby_object)

+

</syntaxhighlight>

+

to create a QVariant that contains your ruby object. To get it back, you just call Qt::Variant#value as usual.

+

+

This can be used as follows

+

<syntaxhighlight lang="ruby">

+

class MyObject < Qt::Object

+

signals "mySignal(QVariant)"

+

def doEmit

+

# since KDE 4.5:

+

# emit mySignal(Qt::Variant.fromValue(ruby_object))

+

emit mySignal(ruby_object.to_variant)

+

end

+

+

slots "mySlot(QVariant)"

+

def mySlot(variant)

+

ruby_object = variant.value

+

end

+

end

+

</syntaxhighlight>

+

+

=== Alternate way to emit Ruby Classes ===

+

You can also try to emit a ruby class by emitting it's object_id (either as an Integer or a QVariant), and use ObjectSpace._id2ref to get the object back.

+

+

<syntaxhighlight lang="ruby">

+

class Object

+

def to_variant

+

Qt::Variant.new object_id

+

end

+

end

+

+

class Qt::Variant

+

def to_object

+

ObjectSpace._id2ref to_int

+

end

+

end

+

</syntaxhighlight>

+

+

==Constructors==

You can call constructors in the conventional style:

You can call constructors in the conventional style:

−

quit = Qt::PushButton.new("Quit", self, "quit")

+

<syntaxhighlight lang="ruby">

+

quit = Qt::PushButton.new("Quit", self, "quit")

+

</syntaxhighlight>

Or you can pass a block if you prefer:

Or you can pass a block if you prefer:

−

w = MyWidget.new { setCaption("foobar") }

+

<syntaxhighlight lang="ruby">

+

w = MyWidget.new { setCaption("foobar") }

+

</syntaxhighlight>

The block will be called in the context of the newly created instance.

The block will be called in the context of the newly created instance.

Line 82:

Line 200:

Ordinary arguments can be provided as well as a block at the end:

Ordinary arguments can be provided as well as a block at the end:

−

w = MyWidget.new(nil) { setCaption("foobar") }

+

<syntaxhighlight lang="ruby">

+

w = MyWidget.new(nil) { setCaption("foobar") }

+

</syntaxhighlight>

They are run in the context of the new instance.

They are run in the context of the new instance.

Line 88:

Line 208:

And there's more! You can also pass an arg to the block, and it will be run in the context of the arg:

And there's more! You can also pass an arg to the block, and it will be run in the context of the arg:

−

w = MyWidget.new { |theWidget| theWidget.setCaption "foobar" }

+

<syntaxhighlight lang="ruby">

+

w = MyWidget.new { |theWidget| theWidget.setCaption "foobar" }

+

</syntaxhighlight>

==Garbage Collection==

==Garbage Collection==

−

When a ruby instance is garbage collected, the underlying C++ instance will only be deleted if it isn't 'owned' by a parent object. Normally this will 'just work', but there are occasions when you need to delete the C++ ahead of garbage collection, and whether or not it has a parent. Use the dispose(), isDisposed() and disposed? methods like this:

When a ruby instance is garbage collected, the underlying C++ instance will only be deleted if it isn't 'owned' by a parent object. Normally this will 'just work', but there are occasions when you need to delete the C++ ahead of garbage collection, and whether or not it has a parent. Use the dispose(), isDisposed() and disposed? methods like this:

−

item2.dispose

+

<syntaxhighlight lang="ruby">

−

if item2.disposed?

+

item2.dispose

−

puts "item2 is disposed"

+

if item2.disposed?

−

end

+

puts "item2 is disposed"

+

end

+

</syntaxhighlight>

==C++ 'int*' and 'int&' argument types==

==C++ 'int*' and 'int&' argument types==

−

Ruby passes numeric values by value, and so they can't be changed when passed to a method. The Qt::Integer class provides a mutable numeric type which does get updated when passed as an argument. For example, this C++ method 'findByFileContent()':

Ruby passes numeric values by value, and so they can't be changed when passed to a method. The Qt::Integer class provides a mutable numeric type which does get updated when passed as an argument. For example, this C++ method 'findByFileContent()':

−

# static Ptr findByFileContent( const QString &fileName,

+

<syntaxhighlight lang="ruby">

−

# int *accuracy=0 );

+

# static Ptr findByFileContent( const QString &fileName,

−

+

# int *accuracy=0 );

−

acc = Qt::Integer.new(0)

+

−

fc = KDE::MimeType.findByFileContent("mimetype.rb", acc)

+

+

acc = Qt::Integer.new(0)

+

fc = KDE::MimeType.findByFileContent("mimetype.rb", acc)

+

</syntaxhighlight>

It supports the arithmetic operators, and so expressions such as 'acc + 3' will work.

It supports the arithmetic operators, and so expressions such as 'acc + 3' will work.

==C++ 'bool*' and 'bool&' argument types==

==C++ 'bool*' and 'bool&' argument types==

−

There is a similar problem for bool arg types, and the mutable Qt::Boolean class can be used like this:

There is a similar problem for bool arg types, and the mutable Qt::Boolean class can be used like this:

−

# QFont getFont(bool * ok, const QFont&initial,

+

<syntaxhighlight lang="ruby">

−

# QWidget* parent = 0, const char *name = 0);

+

# QFont getFont(bool * ok, const QFont&initial,

+

# QWidget* parent = 0, const char *name = 0);

−

ok = Qt::Boolean.new

+

ok = Qt::Boolean.new

−

font = Qt::FontDialog.getFont(ok,

+

font = Qt::FontDialog.getFont(ok,

−

Qt::Font.new("Helvetica [Cronyx]", 10),

+

Qt::Font.new("Helvetica [Cronyx]", 10),

−

self)

+

self)

−

if !ok.nil?

+

if !ok.nil?

−

# font is set to the font the user selected

+

# font is set to the font the user selected

−

else

+

else

−

# the user canceled the dialog

+

# the user canceled the dialog

−

end

+

end

+

</syntaxhighlight>

Use 'nil?' to test the value returned in the Boolean

Use 'nil?' to test the value returned in the Boolean

+

+

==C++ (const )(unsigned )char* argument types==

+

In some cases Qt/KDE object "takes ownership" over Ruby String passed as char* argument type. Programmer needs to make sure that Ruby String is not being garbage collected or changed for the time it's being used by Qt/KDE object. It is also quite possible that Qt/KDE object will change and eventually free it(memory used internally by Ruby String to store its data). Be very careful when you call this kind of methods and make sure that there is no overloaded version witch accepts QString or QByteArray first!

+

+

==C++ unsigned char* functions==

+

+

Very few functions (as QImage::bits()) return a uchar* to directly manipulate data. These functions are not supported in Ruby and will throw an ArgumentError. More information on the [http://lists.kde.org/?l=kde-bindings&m=122899325331866&w=2 mail list].

==Debugging==

==Debugging==

+

If a method call can't be matched in the Smoke library giving a 'method_missing' error, first check that you are passing correct class instance that is properly initialized (with super method called in constructors of custom Qt classes descendants). You can also turn on debugging to trace the matching process:

−

If a method call can't be matched in the Smoke library giving a 'method_missing' error, you can turn on debugging to trace the matching process:

+

<syntaxhighlight lang="ruby">

−

+

a = Qt::Application.new(ARGV)

−

a = Qt::Application.new(ARGV)

+

Qt.debug_level = Qt::DebugLevel::High

−

Qt.debug_level = Qt::DebugLevel::High

+

a.loadLibrary("foo") # Non existent method

−

a.loadLibrary("foo") # Non existent method

+

</syntaxhighlight>

Will give the following output:

Will give the following output:

Line 148:

Line 280:

static QWidget* QApplication::widgetAt(int, int, bool)

static QWidget* QApplication::widgetAt(int, int, bool)

...

...

+

Here, the list of candidate methods 'methodIds' is empty

Here, the list of candidate methods 'methodIds' is empty

Line 154:

Line 287:

You can trace virtual method callbacks:

You can trace virtual method callbacks:

−

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_VIRTUAL)

+

<syntaxhighlight lang="ruby">

+

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_VIRTUAL)

+

</syntaxhighlight>

Or trace QtRuby garbage collection:

Or trace QtRuby garbage collection:

−

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC)

+

<syntaxhighlight lang="ruby">

+

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC)

+

</syntaxhighlight>

==String i18n==

==String i18n==

Line 167:

Line 304:

==Qt Designer==

==Qt Designer==

+

A 'rbuic4' tool is included in qtruby/tools/rbuic to compile .ui files into ruby code. As described above, Qt Designer uses UTF-8. In addition to the options in the original uic C++ utility an '-x' flag has been added. This will generate a top level stub in the code:

−

A 'rbuic' tool is included in qtruby/rubylib/designer/rbuic to compile .ui files into ruby code. As described above, Qt Designer uses UTF-8. In addition to the options in the original uic C++ utility an '-x' flag has been added. This will generate a top level stub in the code:

+

<syntaxhighlight lang="bash">

−

+

$ rbuic mainform.ui -x -o mainform.rb

−

$ rbuic mainform.ui -x -o mainform.rb

+

</syntaxhighlight>

Will add this to the end of the generated code:

Will add this to the end of the generated code:

−

if $0 == __FILE__

+

<syntaxhighlight lang="ruby">

−

a = Qt::Application.new(ARGV)

+

if $0 == __FILE__

−

w = MainForm.new

+

a = Qt::Application.new(ARGV)

−

a.setMainWidget(w)

+

w = MainForm.new

−

w.show

+

w.show

−

a.exec

+

a.exec

−

end

+

end

+

</syntaxhighlight>

Then you can test the example code straight away:

Then you can test the example code straight away:

Line 186:

Line 325:

$ ruby mainform.rb

$ ruby mainform.rb

−

Use the '-kde' option to require the 'Korundum' extension rather than the 'Qt' one. If the '-x' option is used in conjunction, it generates a KDE top level. For example:

+

Use the '-kde' option to require the 'korundum4' extension rather than the 'Qt4' one. If the '-x' option is used in conjunction, it generates a KDE top level. For example:

−

$ rbuic -x -kde knotifywidgetbase.ui -o knotifywidgetbase.rb

+

<syntaxhighlight lang="bash">

+

$ rbuic4 -x -kde knotifywidgetbase.ui -o knotifywidgetbase.rb

+

</syntaxhighlight>

Will generate this top level code:

Will generate this top level code:

−

if $0 == __FILE__

+

<syntaxhighlight lang="ruby">

−

about = KDE::AboutData.new("knotifywidgetbase",

+

if $0 == __FILE__

−

"KNotifyWidgetBase", "0.1")

+

about = KDE::AboutData.new("knotifywidgetbase",

−

KDE::CmdLineArgs.init(ARGV, about)

+

"KNotifyWidgetBase", "0.1")

−

a = KDE::Application.new()

+

KDE::CmdLineArgs.init(ARGV, about)

−

w = KNotifyWidgetBase.new

+

a = KDE::Application.new()

−

a.setMainWidget(w)

+

w = KNotifyWidgetBase.new

−

w.show

+

w.show

−

a.exec

+

a.exec

−

end

+

end

+

</syntaxhighlight>

−

==Loading .ui files at runtime with QUI::WidgetFactory==

+

==Loading .ui files at runtime with Qt::UiLoader==

−

You can load a Qt Designer .ui file at runtime with the 'qui' extension, for example:

+

Let's say you created a file in your project directory called 'gui.ui' using Qt Designer ('/usr/bin/designer', comes with Qt. At least on Arch).

+

You can load it like so:

−

require 'Qt'

+

<syntaxhighlight lang="ruby">

−

require 'qui'

+

require 'Qt4'

−

+

require 'qtuitools'

−

a = Qt::Application.new(ARGV)

+

−

if ARGV.length == 0

+

class MyApp < Qt::Application

−

exit

+

def initialize

−

end

+

super ARGV

−

+

−

if ARGV.length == 2

+

# 'gui.ui' was created with qt designer ('designer' on arch linux)

−

QUI::WidgetFactory.loadImages ARGV[0]

+

file = Qt::File.new 'gui.ui' do

−

w = QUI::WidgetFactory.create ARGV[1]

+

open Qt::File::ReadOnly

−

if w.nil?

+

end

−

exit

+

−

end

+

window = Qt::UiLoader.new.load file

−

w.show()

+

−

a.connect(a, SIGNAL('lastWindowClosed()'), a, SLOT('quit()'))

+

file.close

−

a.exec()

+

−

end

+

−

==QtRuby shell==

+

if window.nil?

+

print "Error. Window is nil.\n"

+

exit

+

end

+

window.show

+

end

+

end

−

You can use the QtRuby shell in bin/rbqtsh to create widgets interactively from the command line.

+

a = MyApp.new

+

a.exec

+

</syntaxhighlight>

==API reference==

==API reference==

−

Use the bin/rbqtapish tool to discover which methods are available in the QtRuby api. This command:

+

Use the bin/rbqtapi tool to discover which methods are available in the QtRuby api. This command:

−

$ rbqtapish Qt::TextEdit

+

<syntaxhighlight lang="bash">

+

$ rbqtapi Qt::TextEdit

+

</syntaxhighlight>

Will list all the methods in the Qt::TextEdit class

Will list all the methods in the Qt::TextEdit class

−

$ rbqtapi -rsetCaption

+

<syntaxhighlight lang="bash">

+

$ rbqtapi -rsetCaption

+

</syntaxhighlight>

Lists all methods whose names contain the string 'setCaption'

Lists all methods whose names contain the string 'setCaption'

Line 244:

Line 397:

==Example programs==

==Example programs==

−

The best way to start programming QtRuby is to look at some existing code and start messing with it.. The are various samples under qtruby/rubylib/examples and korundum/rubylib/examples.

+

The best way to start programming QtRuby is to look at some existing code and start messing with it.. The are various samples under qtrubyexamples and korundum/examples.

−

=KDE Specific Infomation=

+

==Writing Unit Tests==

+

Using Ruby allows you the power to leverage testing frameworks such as RSpec in order to unit and integration test your classes.

−

Instead of <code>require 'Qt'</code>, use <code>require 'Korundum'</code> for KDE programs.

+

Most classes such as Models can be easily tested via the APIs they expose. Below is a small extract with some interesting tests from the mingle_mover project (http://github.com/gja/mingle_mover). The class being tested is a TableModel

−

The KDE K* classes such as KApplication are renamed as KDE::Application. The other KDE classes are in the KParts::, KIO:: or DOM:: namespaces, with the same names as their C++ counterparts.

+

Note how we create a stub to pass to QAbstractTableModel::data, which accepts a QModelIndex as an argument.

−

Use the 'rbkdeapi' script to introspect the Korundum api from the command line. For example:

+

<syntaxhighlight lang="ruby">

+

describe CardModel do

+

it "Should Implement QAbstractTableModel" do

+

CardModel.ancestors.should include Qt::AbstractTableModel

+

end

−

$ rbkdeapi KDE::Action

+

it "Should Not Be Editable" do

+

@model.data(mock_index(1,2), Qt::EditRole).should_not be_valid

+

@model.headerData(nil, nil, Qt::EditRole).should_not be_valid

+

+

flags = @model.flags(nil)

+

flags.should have_flag Qt::ItemIsEnabled

+

flags.should have_flag Qt::ItemIsSelectable

+

flags.should_not have_flag Qt::ItemIsEditable

+

end

−

Will list all the methods in the KDE::Action class. There are currently (as at KDE 3.3 beta 2) 977 classes/30841 methods in the Smoke library runtime, so the coverage of the Qt/KDE api is pretty complete.

Below is a simple class which can be used to test out a class via the signals it emits:

−

Note that the class doesn't have to inherit from DCOPObject. If you include a 'k_dcop' slots declaration a 'listener' dcop object instance is created automatically, and these four methods are added to your class:

+

<syntaxhighlight lang="ruby">

+

class RubySignalSpy < Qt::Object

+

def self.create(*args, &block)

+

Class.new(self).new(*args, &block)

+

end

−

interfaces()

+

def count(name)

−

functions()

+

@calls[name].size

−

connectDCOPSignal()

+

end

−

disconnectDCOPSignal()

+

−

The name of the object is always the ruby classname, and you can only instantiate one instance for each ruby class that has 'k_dcop' declarations. See examples/dcop/dcopslot.rb and dcopsignal.rb for an example of the simplest approach.

+

def params(name, invocation = 0)

+

@calls[name][invocation]

+

end

−

If you wish to use the full functionality of a DCOPObject, you can subclass it and call all the methods, not just the four above. Additionally, you can instantiate more than one instance per class and rename the dcop object with the setObjId() method or by passing the name to the constructor. See the examples/dcop/petshop.rb code for an example of a more complex dcop object.

+

def method_missing(name, *args, &block)

+

@calls[name.to_sym] << args

+

exec_action_for(name, args)

+

end

−

Define a dcop slot like this in one ruby program:

+

def responds_to?(name)

+

true

+

end

−

k_dcop 'QPoint getPoint(QString)'

+

private

−

+

def initialize

−

def getPoint(msg)

+

@calls = {}

−

puts "message: #{msg}"

+

def @calls.[](index)

−

return Qt::Point.new(50, 100)

+

super || self[index] = []

−

end

+

end

+

@actions = {}

+

super

+

end

−

Call it from another program and print the reply, like this:

+

def mocked_slots(*names, &block)

+

slots *names

+

names.each { |name| @actions[name] = block }

+

end

−

dcopRef = KDE::DCOPRef.new("dcopslot", "MyWidget")

+

def exec_action_for(name, args)

+

@actions[name].call(self, args) if @actions[name]

+

end

−

There are three different ways to specify a DCOP call:

+

def slots(*args)

+

self.class.slots(*args)

+

end

+

end

+

</syntaxhighlight>

−

res = dcopRef.call("getPoint(QString)", "Hello from dcopcall")

+

<syntaxhighlight lang="ruby">

−

res = dcopRef.call("getPoint", "Hello from dcopcall")

+

it "Should be able to emit a signal when called" do

−

res = dcopRef.getPoint("Hello from dcopcall")

+

reciever = RubySignalSpy.create do

+

slots "recieved(int, int)" # Explicitly name slots with parameters

+

mocked_slot :some_other_slot do |spy, params| # Pass a block to be executed when called

+

end # You must call mocked_slot with a symbol

+

end

−

If the dcop slot has a 'void' or 'ASYNC' type, the result will be true if the call succeeds or nil if it fails

Amaze your friends! Do the programming equivalent of leaping over tall buildings in one bound! Here with one line of quite clear code, we read a file from disc and assign it the 'clipboardContents' klipper attribute via dcop:

The KDE K* classes such as KApplication are renamed as KDE::Application. The other KDE classes are in the KParts::, KIO:: or DOM:: namespaces, with the same names as their C++ counterparts.

−

Instead of:

+

Use the 'rbkdeapi' script to introspect the Korundum api from the command line. For example:

−

result = dcopRef.isFoo()

+

<syntaxhighlight lang="bash">

+

$ rbkdeapi KDE::Action

+

</syntaxhighlight>

−

You can use this more rubyish form:

+

Will list all the methods in the KDE::Action class. There are currently (as at KDE 3.3 beta 2) 977 classes/30841 methods in the Smoke library runtime, so the coverage of the Qt/KDE api is pretty complete.

−

+

−

if dcopRef.foo?

+

−

puts "foo is true"

+

−

else

+

−

puts "foo? is false"

+

−

end

+

−

+

−

Similarly you can use foo? as an alias for methods of the form hasFoo(). See examples/dcop/dcoppredicate.rb and dcopslot.rb

+

−

+

−

==Underscore to CamelCase DCOP method name conversion==

+

−

+

−

Any underscores in a method name are removed, and the following character is capitalised. For example:

+

−

+

−

res = dcopRef.get_point("Hello from dcopsend")

+

−

+

−

Is a synonym for:

+

−

+

−

res = dcopRef.getPoint("Hello from dcopsend")

+

−

+

−

==Send to a DCOPRef==

+

−

+

−

There are two different ways to specify a DCOP send:

+

−

+

−

res = dcopRef.send("mySlot(QString)", "Hello from dcopsend")

+

−

res = dcopRef.send("mySlot", "Hello from dcopsend")

+

−

+

−

The result will either be true or false (but not nil for fail like DCOPRef.call() method described above).

+

−

+

−

When a call of the form 'dcopRef.getPoint(5, "foobar")' is made, the C++ type signature is obtained from the list of those returned by DCOPRef.functions(). However, if a method name is overloaded the ruby argument types are used to derive a type signature, in order to resolve the call like this:

+

−

+

−

String => QString

+

−

Float => double

+

−

Integer => int

+

−

TrueClass|FalseClass (ie 'true' or 'false') => bool

+

−

Qt::Widget etc => QWidget

+

−

KDE::URL etc => KURL

+

−

Array => QStringList

+

−

+

−

Specify the full C++ type signature using the form 'dcopRef.call("getPoint(int,QString)", 5, "foobar")' if these rules fail to pick the right method.

+

−

+

−

==Defining DCOP Signals==

+

−

+

−

k_dcop_signals 'void testEmitSignal(QString)'

+

−

+

−

def doit()

+

−

emit testEmitSignal("Hello DCOP Slot")

+

−

end

+

−

+

−

Connect slot 'mySlot' to a DCOP signal like this:

+

−

+

−

res = slottest.connectDCOPSignal("dcopsignal",

+

−

"SenderWidget",

+

−

"testEmitSignal(QString)",

+

−

"mySlot(QString)",

+

−

true)

+

=Build dependencies=

=Build dependencies=

−

* ruby 1.8 or greater

+

* ruby 1.8 or greater (svn trunk works with 1.9.1)

−

* automake 1.7 or greater

+

* cmake 2.6 or greater

−

* Qt 3.1 or greater

+

* Qt 4.0 or greater

−

* KDE 3.1 or greater (for korundum)

+

* KDE 4.1 or greater (for korundum)

=Tutorials=

=Tutorials=

−

There is a ruby translation of [http://developer.kde.org/language-bindings/ruby/tutorial/tutorial.html Qt Tutorial #1], and the corresponding ruby code is in qtruby/rubylib/tutorial/t1 to t14.

There is a ruby translation of [http://developer.kde.org/language-bindings/ruby/tutorial/tutorial.html Qt Tutorial #1], and the corresponding ruby code is in qtruby/rubylib/tutorial/t1 to t14.

−

And a Qt4 version of the same tutorial translated to Ruby by Darshan Ishaya [http://www.darshancomputing.com/qt4-qtruby-tutorial/ Qt4 Tutorial #1]

+

And a Qt4 version of the same tutorial translated to Ruby by Darshan Ishaya [[Development/Tutorials/Qt4_Ruby_Tutorial|Qt4 Ruby Tutorial]]

The book [http://www.pragmaticprogrammer.com/titles/ctrubyqt/ Rapid GUI Development with QtRuby] is now available.

The book [http://www.pragmaticprogrammer.com/titles/ctrubyqt/ Rapid GUI Development with QtRuby] is now available.

+

+

There is also an approach to create an [[/Ruby-Qt/KDE Book|Ruby-Qt/KDE Book]] under a free license. The content will be created in this wiki. The book made with latex will be derived from the content in the wiki. Any Questions? Contact [[User:SaLOUt|me]]!

* [http://www.ruby-forum.com/topic/189346#new Very useful link how to create your first Qt window dialog]

[[Category:Ruby]]

[[Category:Ruby]]

Revision as of 15:40, 13 July 2012

Very complete bindings to both the KDE API and the Qt APIs. The Korundum package includes both a QtRuby Qt-only binding along with the full combined Qt/KDE one. The QtRuby package contains just Qt bindings with no dependencies on KDE.

Constructors

The block will be called in the context of the newly created instance.

Ordinary arguments can be provided as well as a block at the end:

w = MyWidget.new(nil){ setCaption("foobar")}

They are run in the context of the new instance.

And there's more! You can also pass an arg to the block, and it will be run in the context of the arg:

w = MyWidget.new{|theWidget| theWidget.setCaption"foobar"}

Garbage Collection

When a ruby instance is garbage collected, the underlying C++ instance will only be deleted if it isn't 'owned' by a parent object. Normally this will 'just work', but there are occasions when you need to delete the C++ ahead of garbage collection, and whether or not it has a parent. Use the dispose(), isDisposed() and disposed? methods like this:

item2.disposeif item2.disposed?
puts"item2 is disposed"end

C++ 'int*' and 'int&' argument types

Ruby passes numeric values by value, and so they can't be changed when passed to a method. The Qt::Integer class provides a mutable numeric type which does get updated when passed as an argument. For example, this C++ method 'findByFileContent()':

C++ (const )(unsigned )char* argument types

In some cases Qt/KDE object "takes ownership" over Ruby String passed as char* argument type. Programmer needs to make sure that Ruby String is not being garbage collected or changed for the time it's being used by Qt/KDE object. It is also quite possible that Qt/KDE object will change and eventually free it(memory used internally by Ruby String to store its data). Be very careful when you call this kind of methods and make sure that there is no overloaded version witch accepts QString or QByteArray first!

C++ unsigned char* functions

Very few functions (as QImage::bits()) return a uchar* to directly manipulate data. These functions are not supported in Ruby and will throw an ArgumentError. More information on the mail list.

Debugging

If a method call can't be matched in the Smoke library giving a 'method_missing' error, first check that you are passing correct class instance that is properly initialized (with super method called in constructors of custom Qt classes descendants). You can also turn on debugging to trace the matching process:

Another debugging mechanism allows various trace 'channels' to be switched on.

You can trace virtual method callbacks:

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_VIRTUAL)

Or trace QtRuby garbage collection:

Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC)

String i18n

QtRuby supports $KCODE values of 'u', 'e' and 's' or the corresponding '-K' options from the command line. Qt Designer .ui files have UTF-8 strings so if you use any 8 bit UTF-8 characters, you will need to set $KCODE='u' or use the -Ku command line option.

Other capabilities and offerings

Qt Designer

A 'rbuic4' tool is included in qtruby/tools/rbuic to compile .ui files into ruby code. As described above, Qt Designer uses UTF-8. In addition to the options in the original uic C++ utility an '-x' flag has been added. This will generate a top level stub in the code:

API reference

Use the bin/rbqtapi tool to discover which methods are available in the QtRuby api. This command:

$ rbqtapi Qt::TextEdit

Will list all the methods in the Qt::TextEdit class

$ rbqtapi -rsetCaption

Lists all methods whose names contain the string 'setCaption'

Example programs

The best way to start programming QtRuby is to look at some existing code and start messing with it.. The are various samples under qtrubyexamples and korundum/examples.

Writing Unit Tests

Using Ruby allows you the power to leverage testing frameworks such as RSpec in order to unit and integration test your classes.

Most classes such as Models can be easily tested via the APIs they expose. Below is a small extract with some interesting tests from the mingle_mover project (http://github.com/gja/mingle_mover). The class being tested is a TableModel

Note how we create a stub to pass to QAbstractTableModel::data, which accepts a QModelIndex as an argument.

Testing UI Classes

It is much more difficult to test UI classes. In most cases, you will need to instantiate a QApplication so that you can create your widgets.

Some rudimentary tests can be written by simulating clicks are various locations. Watch this space as more tests get written

describe SomeTableView do
before(:all)do@app = Qt::Application.new(ARGV)@view = SomeTableView.newend
it "Should accept a mouse click on second row"@row2 = @view.rowViewportPosition1@view.mousePressEvent(Qt::MouseEvent.new(Qt::Event::MouseButtonPress, Qt::Point.new(0,@row2), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier))# Assert something here, such as that a signal has been emittedend
after(:all)do@app.dispose!
endend

KDE Specific Infomation

Instead of

require'Qt4'

, use

require'korundum4'

for KDE programs.

The KDE K* classes such as KApplication are renamed as KDE::Application. The other KDE classes are in the KParts::, KIO:: or DOM:: namespaces, with the same names as their C++ counterparts.

Use the 'rbkdeapi' script to introspect the Korundum api from the command line. For example:

$ rbkdeapi KDE::Action

Will list all the methods in the KDE::Action class. There are currently (as at KDE 3.3 beta 2) 977 classes/30841 methods in the Smoke library runtime, so the coverage of the Qt/KDE api is pretty complete.

Build dependencies

ruby 1.8 or greater (svn trunk works with 1.9.1)

cmake 2.6 or greater

Qt 4.0 or greater

KDE 4.1 or greater (for korundum)

Tutorials

There is a ruby translation of Qt Tutorial #1, and the corresponding ruby code is in qtruby/rubylib/tutorial/t1 to t14.

And a Qt4 version of the same tutorial translated to Ruby by Darshan Ishaya Qt4 Ruby Tutorial

There is also an approach to create an Ruby-Qt/KDE Book under a free license. The content will be created in this wiki. The book made with latex will be derived from the content in the wiki. Any Questions? Contact me!