PDA

View Full Version : Connection a QVector<QSlider *> adequately



olidem
16th March 2009, 13:13
Hi!

I have:


class MyWidget : public QWidget
{
Q_OBJECT

public:
MyWidget();
~MyWidget();
public slots:
void adequate_function(int slider_id, int value);
private:
QVector<QSlider*> m_sliders;
}
and my constructor looks like


MyWidget::MyWidget()
{
for (int i=0 ; i<5 ; i++)
{
m_sliders.append(QSlider(Qt::Horizontal, this));
m_sliders[i].setRange(0,100);
m_sliders[i].setPageStep(20);

//this does not work but explains what I like
QObject::connect( m_sliders[i], SIGNAL(valueChanged(int)),
this, SLOT(setDeformationFactor( i , int) ) );
}
}

so, I would like to call a function and give the slider id as argument.

Regards,Olli

Lykurg
16th March 2009, 13:22
it has to be
m_sliders.append(new QSlider(Qt::Horizontal, this));

and what is "i" in
QObject::connect( m_sliders[i], SIGNAL(valueChanged(int)), this, SLOT(setDeformationFactor( i , int) ) );?
The fist argument of setDeformationFactor has to be int and the second must have a default value.

EDIT: If you want to know which slider has called the slot use sender().

JimDaniel
16th March 2009, 13:44
I see a few problems.

1. You aren't adding pointers to sliders to your vector, you are adding objects created on the stack. Unless my c++ is failing me this code won't compile as is.

2. The slot name in your header does not match the slot name you are connecting to in the constructor. Is that just a typo?

3. The connection is not right. I see what you want to do by sending the "i" slider to the slot, but it doesn't work that way. The signal itself must emit the value. You have a couple options. There is a class called QSignalMapper. You could connect your sliders to that and it will emit a signal called mapped(int). Here is a short example from some code I wrote to do something similar:



//in this example, m_effect_sliders is a QList<QSlider *>
QSignalMapper * effect_slider_mapper = new QSignalMapper(this);
for(int i = 0; i < 5; i++)
{
connect(m_effect_sliders.at(i), SIGNAL(valueChanged(int)), effect_slider_mapper, SLOT(map()));
effect_slider_mapper->setMapping(m_effect_sliders.at(i), i);
}
connect(effect_slider_mapper, SIGNAL(mapped(int)), this, SLOT(updateEffectsValue(int)));

olidem
16th March 2009, 13:45
Well, "i" is the index of the slider that has been changed.
I want to solve elegantly the problem of handling each slider in a similar way:
If I had eg 5 methods


public slots:
void adequate_function0(int value);
void adequate_function1(int value);
void adequate_function2(int value);
void adequate_function3(int value);
void adequate_function4(int value);

I could do something similar.

Lykurg
16th March 2009, 14:04
Well, "i" is the index of the slider that has been changed.

You can't pass values that way!


I want to solve elegantly the problem of handling each slider in a similar way:
If I had eg 5 methods


public slots:
void adequate_function0(int value);
void adequate_function1(int value);
void adequate_function2(int value);
void adequate_function3(int value);
void adequate_function4(int value);

I could do something similar.

Use a signal mapper as JimDaniel has suggested or sender() in your slot to get the sender.

olidem
16th March 2009, 20:09
3. The connection is not right. I see what you want to do by sending the "i" slider to the slot, but it doesn't work that way. The signal itself must emit the value. You have a couple options. There is a class called QSignalMapper. You could connect your sliders to that and it will emit a signal called mapped(int). Here is a short example from some code I wrote to do something similar:



//in this example, m_effect_sliders is a QList<QSlider *>
QSignalMapper * effect_slider_mapper = new QSignalMapper(this);
for(int i = 0; i < 5; i++)
{
connect(m_effect_sliders.at(i), SIGNAL(valueChanged(int)), effect_slider_mapper, SLOT(map()));
effect_slider_mapper->setMapping(m_effect_sliders.at(i), i);
}
connect(effect_slider_mapper, SIGNAL(mapped(int)), this, SLOT(updateEffectsValue(int)));

Yeah! This look great!
But I don't see where the actual new value of the slider arives at the function. Do I have to request it from the concerned slider again manually?

olidem
16th March 2009, 20:12
I found another solution:



class NiceSlider : public QSlider
{
Q_OBJECT
public:
NiceSlider(int id, Qt::Orientation orientation, QWidget * parent);

signals:
void valueChanged( int value, int id);

protected:
void sliderChange(SliderChange c)
{
if(c == QAbstractSlider::SliderValueChange)
emit valueChanged( this->value(), _id );

QSlider::sliderChange(c);
};

private:
int _id;
};

and in the constructor from earlier:


MyWidget::MyWidget()
{
for (int i=0 ; i<5 ; i++)
{
m_sliders.append( new NiceSlider ( i, Qt::Horizontal, this) );
QObject::connect( m_sliders[i], SIGNAL(valueChanged(int,int)),
&m_view, SLOT(setDeformationFactor(int,int)));
}
}


NICE, isn't it ?

Thank you all a lot!

Best regards,
Olli

JimDaniel
16th March 2009, 20:20
EDIT: The above subclassing method also works. I've done that before too, in certain scenarios. For your problem I would say using a signal mapper is still the cleaner method, unless you have other reasons to create a subclass.

Yes, with the method I showed you need to manually grab the value from the list of QSliders, but it is very straightforward because you have the index. So you could do this inside your setDeformationFactor(int):



setDeformationFactor(int index)
{
int new_slider_value = m_sliders[index]->value();
}

Lykurg
16th March 2009, 20:26
NICE, isn't it ?
For your special need, yes. But think if you want to add a new slider while the program is running, you have to know which is the new id. And now think about dynamic adding and removing things became complicated and can rapid lead to a bug...

olidem
17th March 2009, 08:21
Yes, in principle I agree with you.
But in my special case, I will remove all sliders and add completely new ones from time to time. So there will never appear the case where I already use n sliders and add an n+1th afterwards.

Thanks for your great support and help!

With best regards,
Olli