tags:

views:

296

answers:

3

Here's a sample of a SpinBox that writes its changes to underlying variables. The main problem that I'm having is valueChanged is called when the widget is constructed. Is there a more elegant way to do this? I think it's weird that I connected a widget to itself, but valueChanged isn't virtual.

class ValueWriterInt: public QSpinBox {
    Q_OBJECT

public:
    ValueWriterInt(vector<int*> const& value): myValue(value) { 
     QObject::connect(this, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
    }
    ~ValueWriterInt() {}

private slots:
    void valueChanged(int new_value) {
        for (auto it = myValue.begin(); it != myValue.end(); ++it)
      **it = new_value;
    }

private:
    vector<int*>   myValue;
};
A: 

So what are you trying to accomplish here? Yep, valueChanged ain't virtual -- why should it be, your objects should directly connect their own slots to whatever signals they want to react to, no?

Alex Martelli
Virtual calls are significantly more efficient than signal/slots. Doesn't matter a whole lot for anything UI-driven, though.
MSalters
I am trying to build a parameters pane, where the custom widgets on the pane live in a tree. It is turning out to be a lot harder than I thought...
Neil G
A: 

I see no other alternative than to use SIGNAL-SLOT connections. However, I would change the name of the slot, so it doesn't have the same name as the signal.

It is intriguing how the slot is called even if there is no connection done yet. I suspect that changing the name of the slot will solve that issue.

Cătălin Pitiș
Two good points. Thanks. I ended up changing the name just to be sure. And, I figured out that the signal was being called when the widget was being inserted into the tree using setIndexWidget().
Neil G
It's perfectly fine if valueChanged(int) is called at the construction. It is after all **a signal**, and not a slot.
Ariya Hidayat
+1  A: 

I see nothing particularly weird about connecting a widget to itself. Having a single method of detecting and responding to data updates actually sounds like a good thing because you have fewer points of failure to check when you are debugging. In your specific case, it is causing some undesired behavior, but in general it is a fine solution.

Now, having expressed the opinion that a reflexive connection isn't inherently inelegant, I am going to suggest a less than "elegant" solution to prevent the calling of valueChanged after construction. You can have a flag to determine whether the object was just constructed and return early to prevent the code being run immediately after construction. In your example:

class ValueWriterInt: public QSpinBox {
Q_OBJECT

public:
    ValueWriterInt(vector<int*> const& value): myValue(value), myAfterInit(true) { 
        QObject::connect(this, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
    }
    ~ValueWriterInt() {}

private slots:
    void        valueChanged(int new_value) {
        if (myAfterInit) {
            myAfterInit = false;
            return;
        }
        for (auto it = myValue.begin(); it != myValue.end(); ++it)
                **it = new_value;
    }

private:
    vector<int*>                myValue;
    boolean                     myAfterInit;
};

That isn't too bad of a solution. It will at least give you your desired behavior until (and if) you can find a more elegant method.

A. Levy
Thank you. This is what I ended up doing, but it is extremely reassuring to have independent confirmation.valueChanged gets called a lot more often than I thought. For example, calling "setIndexWidget" causes valueChanged to be called on the widget. The bool helps there too.Thanks again!
Neil G