In qt, how do I implement a widget that stays consistent with variables in the code

StackOverflow https://stackoverflow.com/questions/818981

  •  03-07-2019
  •  | 
  •  

Question

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;
};
Was it helpful?

Solution

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.

OTHER TIPS

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?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top