Forum: Ruby (mandriva,eclipse,Qt) elementary problem : signals and slots

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Olivier L. (Guest)
on 2009-05-12 01:26
(Received via mailing list)
hi,

I would like to make a little program using Qt : it's a displayer /
converter between farenheit and celsius degrees.
I also want to use QtDesigner.

I made a little widget with QtDesigner, and this widget contains two
dials(dial and dial_2) and two spinboxes(kind of textboxes) (spinbox and
spinbox_2).
There are some connections : see below for the ruby file I obtained with
the tool rbuic4 (which converts the ".ui" file into ".rb").
my problem is this : I want that some changes made in dial_2 modify dial
(and then spinbox), eg :
    0 celsius degrees => 32 farenheit degrees.

here is the file made by rbuic4 and after this my try to modify the slot
setValue (but it doesn't run: the value of dial is always the same as
the one of dial_2.

temp.rb
*******************************************************************
=begin
** Form generated from reading ui file 'temp.ui'
**
** Created: lun. mai 11 21:43:04 2009
**      by: Qt User Interface Compiler version 4.5.0
**
** WARNING! All changes made in this file will be lost when recompiling
ui file!
=end

class Ui_Form
    attr_accessor :dial
    attr_reader :dial_2
    attr_reader :spinBox
    attr_reader :spinBox_2

    def setupUi(form)
    if form.objectName.nil?
        form.objectName = "form"
    end
    form.resize(400, 300)
    @dial = Qt::Dial.new(form)
    @dial.objectName = "dial"
    @dial.geometry = Qt::Rect.new(90, 40, 91, 101)
    @dial_2 = Qt::Dial.new(form)
    @dial_2.objectName = "dial_2"
    @dial_2.geometry = Qt::Rect.new(230, 30, 91, 121)
    @spinBox = Qt::SpinBox.new(form)
    @spinBox.objectName = "spinBox"
    @spinBox.geometry = Qt::Rect.new(100, 200, 58, 28)
    @spinBox_2 = Qt::SpinBox.new(form)
    @spinBox_2.objectName = "spinBox_2"
    @spinBox_2.geometry = Qt::Rect.new(240, 200, 58, 28)

    retranslateUi(form)
    Qt::Object.connect(@dial_2, SIGNAL('valueChanged(int)'), @spinBox_2,
SLOT('setValue(int)'))
    Qt::Object.connect(@dial, SIGNAL('valueChanged(int)'), @spinBox,
SLOT('setValue(int)'))
    Qt::Object.connect(@dial_2, SIGNAL('valueChanged(int)'), @dial,
SLOT('setValue(int)'))

    Qt::MetaObject.connectSlotsByName(form)
    end # setupUi

    def setup_ui(form)
        setupUi(form)
    end

    def retranslateUi(form)
    form.windowTitle = Qt::Application.translate("Form", "Form", nil,
Qt::Application::UnicodeUTF8)
    end # retranslateUi

    def retranslate_ui(form)
        retranslateUi(form)
    end

end

module Ui
    class Form < Ui_Form
    end
end  # module Ui


*******************************************************************

temp_dsc_form.rb
*******************************************************************
require 'temp.rb'
require 'Qt4'

une_appli=Qt::Application.new(ARGV)
un_wdg=Qt::Widget.new
Un_form=Ui_Form.new
Un_form.setupUi(un_wdg)


def (Un_form.dial).setValue(v)

    #super((1.8*v+32).to_i)
    super(0)
end

un_wdg.show
une_appli.exec

*******************************************************************


can you help me?

thx,

lolveley.











___________________________________________________________________________
Yahoo! Mail réinvente le mail ! Découvrez le nouveau Yahoo! Mail et son
interface révolutionnaire.
http://fr.mail.yahoo.com
Stefano C. (Guest)
on 2009-05-12 10:18
(Received via mailing list)
On Monday 11 May 2009, lolveley wrote:
> |the tool rbuic4 (which converts the ".ui" file into ".rb").
> |my problem is this : I want that some changes made in dial_2 modify dial
> |(and then spinbox), eg :
> |    0 celsius degrees => 32 farenheit degrees.
> |
> |here is the file made by rbuic4 and after this my try to modify the slot
> |setValue (but it doesn't run: the value of dial is always the same as
> |the one of dial_2.

I don't know why it doesn't work, as I never used the designer to make
signal/slot connections. I'd say it should work, but there might be some
limitations which I'm not aware of. My suggestion is to use a custom
widget
class instead of a generic Qt::Widget and to implement the slot which
converts
the values there. Here's how the tmp_dsc_form.rb could look like:

require 'Qt4'
require 'temp.rb'

class MyWidget < Qt::Widget

  slots 'change_values(int)'

  def initialize
    super
    @ui = Ui_Form.new
    @ui.setupUi self
    connect @ui.dial_2, SIGNAL('valueChanged(int)'), self,
SLOT('change_values(int)')
  end

  def change_values val
    new_value = (1.8*val+32).to_i
    @ui.spinBox.value = new_value
    @ui.dial.value = new_value
  end

end

une_appli=Qt::Application.new(ARGV)
un_wdg = MyWidget.new
un_wdg.show
une_appli.exec

Of course, if you use this you can remove the connections made from the
designer.

I hope this helps

Stefano
unknown (Guest)
on 2009-05-12 16:06
(Received via mailing list)
On May 12, 7:17 am, Stefano C. <removed_email_address@domain.invalid> wrote:
> > |I made a little widget with QtDesigner, and this widget contains two
> > |the one of dial_2.
> class MyWidget < Qt::Widget
>
> un_wdg.show
> une_appli.exec
>
> Of course, if you use this you can remove the connections made from the
> designer.
I'm not sure if this solves the problem of making the changes bi-
directional because if you use the 'valueChanged(int)' signal, as you
will end up with a loop because each dial will try to change the
other.

I think the original rbuic4 generated code was nearly correct, but it
should use 'sliderMoved(int)' signals in one dial connected to
'setValue(int)' in the other dial because that would loop. And it
needs an extra connection so that @dial_2 is changed when @dial is
changed.

    Qt::Object.connect(@dial, SIGNAL('sliderMoved(int)'), @dial_2, SLOT
('setValue(int)'))
    Qt::Object.connect(@dial_2, SIGNAL('sliderMoved(int)'), @dial, SLOT
('setValue(int)'))

But that wouldn't convert the values. So instead it needs an
intermediate Qt::Object (doesn't need to be a Qt::Widget) to convert
the values as Stefano suggests.


-- Richard
Olivier L. (Guest)
on 2009-05-12 18:22
(Received via mailing list)
hello,

thank you richard and stefano for your answers.
I'm afraid I haven't well understood the system of slots, if I want to
rewrite a slot - say setValue(int) - of a dial what can I put in this
method? I have to put a statement telling the slot to modify graphically
the widget, but I don't think it is possible.

I think _ but I'm not sure _ I have understood the solution of richard :
it is to create a class with a slot (for example setValue) which receive
the value in degree, and this slot emit a signal (here
dial.setValue(int)) which send the converted value (in Farenheit).

is it this?

lolveley.



removed_email_address@domain.invalid a écrit :
>>> |converter between farenheit and celsius degrees.
>>> |
>> require 'Qt4'
>>     connect @ui.dial_2, SIGNAL('valueChanged(int)'), self,
>>
> will end up with a loop because each dial will try to change the
>     Qt::Object.connect(@dial_2, SIGNAL('sliderMoved(int)'), @dial, SLOT
>
___________________________________________________________________________
Yahoo! Mail réinvente le mail ! Découvrez le nouveau Yahoo! Mail et son
interface révolutionnaire.
http://fr.mail.yahoo.com
unknown (Guest)
on 2009-05-12 19:41
(Received via mailing list)
On May 12, 3:21 pm, lolveley <removed_email_address@domain.invalid> wrote:
> the value in degree, and this slot emit a signal (here
> dial.setValue(int)) which send the converted value (in Farenheit).
You could emit a signal from the intermediate 'converter' Qt::Widget
or Qt::Object which is connected to the target dial's 'setValue()'
slot, or you can just call the slot directly with the converted value
- the effect is the same.

> is it this?

In QtRuby, a slot can either be a normal Ruby method, or it can be a
block. Maybe it is easier to understand if you connect a block
directly to each dial's 'sliderMoved(int)' signal, like this:

Un_form.dial.connect(SIGNAL('sliderMoved(int)')) do |farenheit|
  celcius = ((farenheit - 32) / 1.8).to_i
  puts "dial: #{farenheit} farenheit ==> #{celcius} celcius"
  Un_form.dial_2.value = celcius
  Un_form.spinBox_2.value = celcius
end

Un_form.dial_2.connect(SIGNAL('sliderMoved(int)')) do |celcius|
  farenheit = ((1.8 * celcius) + 32).to_i
  puts "dial_2: #{celcius} celcius ==> #{farenheit} farenheit"
  Un_form.dial.value = farenheit
  Un_form.spinBox.value = farenheit
end

The you don't need an intermediate Qt::Widget or Qt::Object to do the
conversion calculations.

Note that in QtRuby you can write 'setFoo(v)' methods like 'foo = v',
and so

  Un_form.spinBox.setValue(farenheit)

can also be written as:

  Un_form.spinBox.value = farenheit

-- Richard
Olivier L. (Guest)
on 2009-05-12 23:17
(Received via mailing list)
hi stefano,

I tried your example and it works fine, but I find this is not very
simple, indeed this is a basic example : what would it be for a more
complicated example?

lolveley.


Stefano C. a écrit :
>> |spinbox_2).
>
>
>   def change_values val
> une_appli.exec
>
> Of course, if you use this you can remove the connections made from the
> designer.
>
> I hope this helps
>
> Stefano
>
>
>






___________________________________________________________________________
Yahoo! Mail réinvente le mail ! Découvrez le nouveau Yahoo! Mail et son
interface révolutionnaire.
http://fr.mail.yahoo.com
Stefano C. (Guest)
on 2009-05-13 13:47
(Received via mailing list)
On Tuesday 12 May 2009, lolveley wrote:

> |>     @ui = Ui_Form.new
> |>
> |> end
> |>
> |> une_appli=Qt::Application.new(ARGV)
> |> un_wdg = MyWidget.new
> |> un_wdg.show
> |> une_appli.exec

> |hi stefano,
> |
> |I tried your example and it works fine, but I find this is not very
> |simple, indeed this is a basic example : what would it be for a more
> |complicated example?
> |
> |lolveley.

The concept is quite simple: you want to do something when the value of
the
second dial changes. In particular, you want to:
1. make the second spin box show the value chosen with the second dial
2. set the value of the first dial to the equivalent in Farenheit
degrees of
the value in the second dial
3. make the first spin box show the value chosen with the first dial

As you have already discovered, the first goal can be achieved directly
from
the UI designer: the dial has a signal which informs of the fact that
the
value has changed and the spin box has a slot which allows to change the
value
it displays: connect the signal and the slot and you're done.

The second and third goal require a bit more work, as there's no
suitable slot
in the first dial which make the conversion for you. In theory, this
would
mean that you have to create a Qt::Object-derived class, define a method
which
does the conversion and sets the value of the first dial, and declare it
as a
slot using the slots class method. In my code, I did this in the dial's
parent
widget, because I think it's the natural place to put code which
connects
together the behaviour of two child widgets, but you don't have to do
that.
However, as Richard suggested, in Ruby you have another choice to do
that:
instead of connecting signals to slots, you can connect them to a block.
This
way, you could keep all of your original code, replacing the method
definition
with a connection to a block. It could look like this:

require 'temp.rb'
require 'Qt4'

une_appli=Qt::Application.new(ARGV)
un_wdg=Qt::Widget.new
Un_form=Ui_Form.new
Un_form.setupUi(un_wdg)
Un_form.dial_2.connect(SIGNAL('valueChanged(int)')) do |v|
  new_val = (1.8*val+32).to_i
  Un_form.dial.value = new_val
  Un_form.spinBox.value = new_val
end

un_wdg.show
une_appli.exec

A block connected with a signal will be called whenever the signal is
emitted,
just like a slot, and the signal's argument will be passed to the block
(the
only downside of using blocks is that you can't disconnect them using
Qt::Object#disconnect).

To avoid having to remember to change the value of the first spin box,
you can
also connect the first dial's valueChanged signal to the first spin box
setValue slot (you can do this from the designer) and remove the last
line of
the block above. When the block is called and the value of the first
dial
changes, it will emit the valueChanged signal with the new value as
argument
and that will trigger the setValue slot of the spin box.

If you want to learn more about Qt programming, I suggest you the
excellent Qt
tutorial you can find at
http://doc.trolltech.com/4.4/tutorials-tutorial.html (it seems it has
been
removed from Qt 4.5 but you can find it in the documentation if you have
a
less recent version). It's written for C++, but it should be quite easy
to
understant. You can also find a ruby version of the tutorial in the
qtruby
source tarball in the ruby/qtruby/examples/tutorial directory.

I hope this helps

Stefano
This topic is locked and can not be replied to.