2010-07-02 15 views
9

Básicamente, tengo múltiples señales de eventos que deseo conectar a la misma ranura. Lo que quiero saber es cómo puedo pasar parámetros basados ​​en cadenas a esa misma ranura para que la ranura sepa de qué proviene esta señal. Una alternativa es hacer tantas ranuras como señales y luego conectarlas de forma 1: 1, pero esto es eficiente, considerando que el código para todo el procesamiento es muy similar. Intenté hacer esto, pero yo estoy haciendo algunos errores:Argumentos de unión a señales/ranuras

connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); 
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1"))); 
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2"))); 

El error está relacionado con los parámetros que estoy pasando en los últimos 2 comandos .. Y backgroundTypeChoiceMade se declara así:

void backgroundTypeChoiceMade(QString); 

¿Puede alguien decirme cuál es el error en el código anterior?

Respuesta

4

No se pueden pasar constantes a connect() porque los parámetros efectivos se deducen en tiempo de ejecución, no de compilación.

Sin embargo, aunque esto va en contra del principio OO, puede usar QObject::sender() que da un puntero al emisor QObject.

Ejemplo a continuación:

void YourClass::YourClass() : 
    m_button1(new QPushButton()), 
    m_button2(new QPushButton()) 
{ 
    connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot())); 
    connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot())); 
} 

void YourClass::yourSlot() 
{ 
    if ((QPushButton* button = dynamic_cast<QPushButton*>(sender())) 
    { 
    // Now button points to a QPushButton* that you can compare with the pointers you already have 

    if (button == m_button1) 
    { 
     // Whatever 
    } else 
    if (button == m_button2) 
    { 
     // Whatever 
    } 
    } 
} 

Si tiene muchos botones, se puede también utilizar un QSignalMapper proporcionando un identificador para cada botón.

+1

También se podría usar el objectName-property: http://doc.trolltech.com/4.5/qobject.html#objectName-prop –

8

Puede usar QSignalMapper. Aunque QSignalMapper es la respuesta a su pregunta, creo que la respuesta de jon hanson es la forma en que debe tomarla. Obtienes un código mucho más limpio de esa manera.

+0

Creo que en este caso * QSignalMapper * es el camino a seguir. Imagine cambiar los botones a * QComboBox * o cualquier otro cambio de UI. Con QSignalMapper, cambio trivial en un archivo .cpp. Con ranuras separadas, se necesita un cambio de firma de clase. – hyde

5

¿Qué es ineficaz acerca del uso de ranuras separadas? Si hay algo en común en los manejadores de tragamonedas entonces muévelo a una función, p. extender el ejemplo de Ereon:

void YourClass::YourClass() : 
    m_button1(new QPushButton()), 
    m_button2(new QPushButton()) 
{ 
    connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1())); 
    connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2())); 
} 

void YourClass::common(int n) 
{ 
} 

void YourClass::yourSlot1() 
{ 
    common (1); 
} 

void YourClass::yourSlot2() 
{ 
    common (2); 
} 
+0

Estoy totalmente de acuerdo con eso. – ereOn

+0

Imagine si la IU debe cambiarse de manera arbitraria, y compare los cambios de código necesarios con este enfoque y el enfoque * QSignalMapper *. – hyde

+0

¿Y qué pasa si su UI cambia en tiempo de ejecución? De ninguna manera Jose. –

8

Cuatro métodos. Uno no apesta.

  1. QSignalMapper. Funciona, pero crea un código desordenado.
  2. Máquinas tragamonedas con nombre. Desordenada para cualquier número significativo de remitentes, y no funciona para remitentes generados dinámicamente (por ejemplo, botones en una lista).
  3. sender() -compare. Puede manejar remitentes dinámicos, pero todavía es feo.
  4. Subclase del remitente. No es una mierda Te da lo que siempre quisiste: señales parametrizadas.

Especialmente cuando utiliza un número pequeño de señales y tipos de emisor y cuando los remitentes se generan dinámicamente, la creación de subclases del remitente es la forma más limpia. Esto le permite sobrecargar las señales existentes para contener los parámetros que necesita.

y ahora, cableado hasta las señales y ranuras simplemente funciona:

Keypad::Keypad(QWidget *parent) : QWidget(parent) 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
     // KeypadButton keeps track of the identifier you give it 
     buttons[i] = new KeypadButton(i, this); 
     // And passes it as a signal parameter. Booyah. 
     connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int))); 
    } 
    createLayout(); 
} 

void Keypad::digitClicked(int digit) 
{ 
    // The slot can find the clicked button with ease: 
    dial(button[i]); // or whatever 
    //... 
} 

y el código adicional es fuera de la vista en una subclase que nunca tiene que tocar de nuevo.

Consulte http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach para ver una implementación de ejemplo de la subclase QPushButton para emitir señales clicked(int). También analiza los cuatro métodos: ranuras con nombre ("la solución trivial"), remitente(), subclases y mapeador de señales.

Advertencia: Obviamente funciona mejor para un pequeño número de tipos de remitentes. Pero ese suele ser el caso. Y en ese caso, vale la pena.

+1

Creo que debe evitar QSignalMapper cada vez que (razonablemente) pueda. Las mejores soluciones (que conducen a un código más limpio) son: 1) subclase y envíe su ID deseada en la señal como escribió @Foamy. Usamos esto en nuestro proyecto Qt siempre que podemos (pero no es una buena idea subclasificar solo para hacerlo) Y 2) obtener el remitente() en su ranura, tipearlo y compararlo con la ID/etiqueta que desee y el emisor tiene –

+0

@ViktorBenei Estoy de acuerdo, excepto que creo que vale la pena subclase solo para señales parametrizadas. Trabajar un poco más en subclases, pero mantiene el resto de tu código mucho más limpio. –

0

Si realmente no desea utilizar QSignalMapper, se podría hacer algo como esto:

class SignalForwarderWithString: public QObject 
{ 
    Q_OBJECT 
public: 
    SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {} 
    QString _data; 
signals: 
    void forward(QString); 
public slots: 
    void receive() { emit forward(_data); } 
}; 

... 
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); 

SignalForwarderWithString *sfws; 
sfws = new SignalForwarderWithString("button1", this); 
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString))); 
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); 

sfws = new SignalForwarderWithString("button2", this); 
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString))); 
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); 

pero QSignalMapper es tan fácil ...

QSignalMapper *mapper = new QSignalMapper(this); 
connect(button1, SIGNAL(clicked()), mapper, SLOT(map())); 
mapper->setMapping(button1, "button 1"); 
connect(button2, SIGNAL(clicked()), mapper, SLOT(map())); 
mapper->setMapping(button2, "button 2"); 
// you might have to tweak the argument type for your slot... 
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString))); 
0

Ahora puede realmente vincula un valor al conectarse. Qt5 agregó soporte para eso.

Ejemplo:

connect(sender, &Sender::valueChanged, 
    tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1)); 

Ver more info.

NB: por supuesto puede usar std :: bind o boost :: bind en lugar de tr1 :: bind.