Tengo un QAction elemento que inicializo como sigue:

QAction* action = foo->addAction(tr("Some Action"));
connect(action, SIGNAL(triggered()), this, SLOT(onSomeAction()));

Y, a continuación, onSomeAction se ve algo como:

void MyClass::onSomeAction()
{
    QAction* caller = qobject_cast<QAction*>(sender());
    Q_ASSERT(caller != nullptr);

    //do some stuff with caller
}

Esto funciona bien, me da la caller objeto y soy capaz de usarlo como se esperaba. Entonces yo trate de C++11 forma de conectar el objeto como tal:

connect(action, &QAction::triggered, [this]()
{
    QAction* caller = qobject_cast<QAction*>(sender());
    Q_ASSERT(caller != nullptr);

    //do some stuff with caller
});

Pero caller es siempre nula y por lo tanto la Q_ASSERT desencadenantes. ¿Cómo puedo utilizar las lambdas para obtener el remitente?

  • Duplicado de stackoverflow.com/questions/14717755/…
  • No del todo, ya que aquí no es necesario ningún gimnasia como en el supuesto de duplicados. Aquí es un asunto trivial para capturar de manera explícita a disposición del remitente dado a connect como un argumento.
InformationsquelleAutor Addy | 2013-11-01

3 Comentarios

  1. 74

    La respuesta es simple: no se puede. O, más bien, no quiere (o necesita!) el uso de sender(). Simplemente captura y el uso action.

    //                               Important!
    //                                  vvvv
    connect(action, &QAction::triggered, this, [action, this]() {
        //use action as you wish
        ...
    });

    La especificación de this como el contexto del objeto para el functor asegura que el functor no se invoca si la acción o this (un QObject) dejan de existir. De lo contrario, el functor trataría de referencia colgando punteros.

    En general, el siguiente debe mantener cuando la captura de variables de contexto para un functor pasa a connect, con el fin de evitar el uso de colgantes punteros o referencias:

    1. Los punteros a los de origen y de destino de los objetos de connect puede ser capturado por valor, como en el anterior. Se garantiza que si el functor se invoca, ambos extremos de la conexión existe.

      connect(a, &A::foo, b, [a, b]{});

      Escenarios donde a y b se encuentran en diferentes subprocesos que requieren atención especial. No se puede garantizar que una vez que el functor es introducido, algunas hilo no eliminar cualquiera de los dos objetos.

      Es idiomático que un objeto es destruido en su thread(), o en cualquier hilo si thread() == nullptr. Desde un hilo del bucle de eventos invoca el functor, la nula hilo no es un problema para b – sin un hilo el functor no se invoca. Por desgracia, no hay ninguna garantía acerca de la vida de a en b‘s hilo. Por lo tanto es más seguro para capturar el estado de la acción por valor de lugar, de modo que a‘la vida no es una preocupación.

      //SAFE
      auto aName = a->objectName();       
      connect(a, &A::foo, b, [aName, b]{ qDebug() << aName; });
      //UNSAFE
      connect(a, &A::foo, b, [a,b]{ qDebug() << a->objectName(); });
    2. Raw punteros a otros objetos pueden ser capturados por valor si usted está absolutamente seguro de que la vida útil de los objetos que el punto a se superpone a la duración de la conexión.

      static C c;
      auto p = &c;
      connect(..., [p]{});
    3. Lo mismo ocurre con las referencias a objetos:

      static D d;
      connect(..., [&d]{});
    4. No copiable objetos que no derivan de QObject debe ser capturado a través de sus compartido punteros por valor.

      std::shared_ptr<E> e { new E };
      QSharedPointer<F> f { new F; }
      connect(..., [e,f]{});
    5. QObjects que viven en el mismo hilo que puede ser captada por un QPointer; su valor debe ser verificada antes de su uso en el functor.

      QPointer<QObject> g { this->parent(); }
      connect(..., [g]{ if (g) ... });
    6. QObjects que viven en otros hilos deben ser capturadas por un puntero o un débil puntero. Su padre debe ser desactivado antes de su destrucción, de lo contrario tendrás doble elimina:

      class I : public QObject {
        ...
        ~I() { setParent(nullptr); }
      };
      
      std::shared_ptr<I> i { new I };
      connect(..., [i]{ ... });
      
      std::weak_ptr<I> j { i };
      connect(..., [j]{ 
        auto jp = j.lock();
        if (jp) { ... }
      });
    • Esto es hermoso y tan sencillo! La respuesta estaba mirando a la derecha en mí. Gracias!
  2. 12

    El uso de expresiones lambda como ranuras es sencillo (por ejemplo para un evento de una QSpinbox):

    connect(spinboxObject, &QSpinBox::editingFinished, this, [this]() {<do something>});

    Pero esto sólo funciona si la señal no está sobrecargado (que significa que hay varias señales con el mismo nombre pero con diferentes argumentos).

    connect(spinboxObject, &QSpinBox::valueChange, this, [this]() {<do something>});

    da un error de compilación, ya que existen dos sobrecargado señales: valueChanged(int) y valueChanged(const QString&)
    Por lo que es necesario para calificar la versión que deben ser utilizados:

    connect(spinboxObject, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this](int newValue){ });

    Un poco más corto (o mejor legible) es el uso de QOverload:

    connect(spinboxObject, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int newValue) { });
  3. 0

    Sin «este» contexto, por ejemplo, desde main():

    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QLabel lbl{"Hello World!"};
        QPushButton btn;
        btn.show();
        lbl.show();
    
        QObject::connect(&btn, &QPushButton::clicked, [&lbl](){lbl.setText("Button clicked");});
    
        return a.exec();
    }

Dejar respuesta

Please enter your comment!
Please enter your name here