2009-05-04 4 views
1

Hier ist ein Beispiel für eine SpinBox, die ihre Änderungen in zugrunde liegenden Variablen schreibt. Das Hauptproblem, das ich habe, ist ValueChanged wird aufgerufen, wenn das Widget erstellt wird. Gibt es einen eleganteren Weg, dies zu tun? Ich finde es komisch, dass ich ein Widget mit sich selbst verbunden habe, aber valueChanged ist nicht virtuell.In Qt, wie implementiere ich ein Widget, das mit Variablen im Code konsistent bleibt

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; 
}; 

Antwort

1

Ich sehe nichts besonderes daran, ein Widget mit sich selbst zu verbinden. Es scheint eine gute Sache zu sein, eine einzige Methode zum Erkennen und Reagieren auf Datenaktualisierungen zu finden, da Sie beim Debuggen weniger Fehlerquellen haben. In Ihrem speziellen Fall verursacht es ein unerwünschtes Verhalten, aber im Allgemeinen ist es eine feine Lösung.

Jetzt, nachdem ich die Meinung geäußert habe, dass eine reflexive Verbindung nicht von Natur aus unelegant ist, werde ich eine weniger als "elegante" Lösung vorschlagen, um den Aufruf von valueChanged nach dem Bau zu verhindern. Sie können ein Flag verwenden, um zu bestimmen, ob das Objekt gerade erstellt wurde, und früh zurückkehren, um zu verhindern, dass der Code unmittelbar nach der Konstruktion ausgeführt wird. In Ihrem Beispiel:

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; 
}; 

Das ist nicht schlecht einer Lösung. Es wird Ihnen zumindest Ihr gewünschtes Verhalten geben, bis (und wenn) Sie eine elegantere Methode finden können.

+0

Danke. Dies ist, was ich getan habe, aber es ist äußerst beruhigend, unabhängige Bestätigung zu haben. valueChanged wird viel öfter aufgerufen, als ich dachte. Wenn Sie beispielsweise "setIndexWidget" aufrufen, wird valueChanged für das Widget aufgerufen. Der Bool hilft auch dort. Danke nochmal! –

0

Was wollen Sie hier erreichen? Yep, valueChanged ist nicht virtuell - warum sollte es sein, Ihre Objekte sollten direkt ihre eigenen Steckplätze mit irgendwelchen Signalen verbinden, auf die sie reagieren wollen, nein?

+0

Virtuelle Anrufe sind wesentlich effizienter als Signal/Slots. Es spielt jedoch keine große Rolle, wenn es um UI-getriebene Dinge geht. – MSalters

+0

Ich versuche, einen Parameterbereich zu erstellen, in dem die benutzerdefinierten Widgets auf dem Bereich in einer Struktur leben. Es erweist sich als viel schwieriger als ich dachte ... –

0

Ich sehe keine andere Alternative, als SIGNAL-SLOT-Verbindungen zu verwenden. Allerdings würde ich den Namen des Slots ändern, so dass er nicht den gleichen Namen wie das Signal hat.

Es ist faszinierend, wie der Slot aufgerufen wird, auch wenn noch keine Verbindung besteht. Ich vermute, dass das Ändern des Namens des Steckplatzes dieses Problem lösen wird.

+0

Zwei gute Punkte. Vielen Dank. Ich habe den Namen geändert, nur um sicher zu gehen. Und ich fand heraus, dass das Signal aufgerufen wurde, als das Widget mit setIndexWidget() in den Baum eingefügt wurde. –

+0

Es ist völlig in Ordnung, wenn valueChanged (int) bei der Konstruktion aufgerufen wird. Es ist immerhin ein Signal ** und kein Slot. –