zoukankan      html  css  js  c++  java
  • qt5中信号和槽的新语法

    qt5中的连接

    有下列几种方式可以连接到信号上

    旧语法

    qt5将继续支持旧的语法去连接,在QObject对象上定义信号和槽函数,及任何继承QObjec的对象(包含QWidget)。

    connect(sender, SIGNAL (valueChanged(QString,QString)),receiver, SLOT (updateValue(QString)) );
    

    新语法:连接到QObject成员

    下面是一种新的方式来连接两个QObjects:

    connect(sender, &Sender::valueChanged,receiver, &Receiver::updateValue ); 
    

    它支持:

    • 编译期间检查信号和槽是否存在,它们的类型,及Q_OBJECT是否丢失
    • 参数能被typedef或不同命名空间指定。
    • 如果有隐式转换的参数,会自动转换类型。比如QString到QVariant
    • 它可以连接QObject的任何成员方法,不仅仅是定义的槽。

    它不支持:

    • 更复杂的语法?你需要指定你的对象类型、
    • 非常复杂的语法,比如重载,参见后面。
    • 在槽的中默认参数不在被支持。

    新语法:连接到简单的函数

    新语法甚至能连接到函数,不仅仅是QObjects:

    connect(sender, &Sender::valueChanged, someFunction);
    

    支持:

    • 能和tr1::bind一起使用:

        connect(sender, &Sender::valueChanged,
          tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
      
    • 能和c++ 11 lambda表达式一起使用:

        connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
          receiver->updateValue("senderValue", newValue);
        });
      

    不支持:

    当receiver被销毁时,新语法不能自动断开信号和槽的连接。 因为它是没有跟QObject一起的伪函数。不管怎样,从5.2版本开始有一个重载函数,它添加一个上下文对象,当对象摧毁时,这个连接会破坏。这个上下文也被使用在线程关联性上: 这个lambda方法将会被调用在对象事件循环的线程中。

    qt5中断开连接

    如你可能预期的那样,在qt5中如何终止连接也会有一些新变化。

    旧方式

    你仍可以旧方式断开连接(使用SIGNAL, SLOT方式)。但仅限是

    • 你使用旧方式连接,或者
    • 如果你想使用通配符,从指定的信号中断开所有的槽

    对称的函数指针

    disconnect(sender, &Sender::valueChanged,
     receiver, &Receiver::updateValue );
    

    这只可以用在你使用同样方式的连接上,或者你也可以使用0作为通配。 在实际中,它也不适用于静态函数,仿函数,或lambda函数。

    使用QMetaObject::Connection的新方式

    QMetaObject::Connection m_connection;
    //…
    m_connection = QObject::connect(…);
    //…
    QObject::disconnect(m_connection);
    

    这适用于所有场景下,包括lambda函数和伪函数。

    更简单的异步

    随着C++ 11,它可以保持代码内联

    void doYourStuff(const QByteArray &page)
    {
     QTcpSocket *socket = new QTcpSocket;
     socket->connectToHost("qt.io", 80);
     QObject::connect(socket, &QTcpSocket::connected, [socket, page] () {
     socket->write(QByteArray("GET " + page + ""));
     });
     QObject::connect(socket, &QTcpSocket::readyRead, [socket] () {
     qDebug()<< "GOT DATA "<< socket->readAll();
     });
     QObject::connect(socket, &QTcpSocket::disconnected, [socket] () {
     qDebug()<< "DISCONNECTED ";
     socket->deleteLater();
     });
     
     QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>
    (&QAbstractSocket::error), [socket](QAbstractSocket::SocketError) {
     qDebug()<< "ERROR " << socket->errorString();
     socket->deleteLater();
     });
    }
    

    下面是一个不用重新进入事件循环的QDialog,保持代码属于:

    void Doc::saveDocument() {
     QFileDialog *dlg = new QFileDialog();
     dlg->open();
     QObject::connect(dlg, &QDialog::finished, [dlg, this](int result) {
     if (result) {
     QFile file(dlg->selectedFiles().first());
     // …
     }
     dlg->deleteLater();
     });
     
    }
    

    使用QHttpServer的另外例子。

    错误报告

    用GCC测试的、
    幸运的是,IDE能简化函数的命名,比如Qt Creator。

    忘记Q_OBJECT

    #include <QtCore>
    class Goo : public QObject {
     Goo() {
     connect(this, &Goo::someSignal, this, &QObject::deleteLater);
     }
    signals:
     void someSignal();
    };
    
    qobject.h: In member function 'void QObject::qt_check_for_QOBJECT_macro(const T&&) const [with T = Goo]':
    qobject.h:535:9: instantiated from 'static typename QtPrivate::QEnableIf<((int)
    (QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
    (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename
    QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*,
    Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(), Func2 = void (QObject::*)(), typename
    QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
    (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename 
    QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = QObject]'
    main.cc:4:68: instantiated from here
    qobject.h:353:5: error: void value not ignored as it ought to be
    make: '''* [main.o] Error 1
    

    拼写错误

    #include <QtCore>
    class Goo : public QObject {
    Q_OBJECT
    public:
     Goo() {
     connect(this, &Goo::someSignal, this, &Goo::someSlot1); //error
     connect(this, &Goo::someSignal, this, &Goo::someSlot2); //works
     }
    signals:
     void someSignal(QString);
    public:
     void someSlot1(int);
     void someSlot2(QVariant);
    };
    
    qobject.h: In static member function 'static typename QtPrivate::QEnableIf<((int)
    (QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
    (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename
    QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*,
    Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(QString), Func2 = void (Goo::*)(int), typename
    QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
    (QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename 
    QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = Goo]':
    main.cc:6:62: instantiated from here
    qobject.h:538:163: error: no type named 'IncompatibleSignalSlotArguments' in 'struct 
    QtPrivate::CheckCompatibleArguments<QtPrivate::List<QString, void>, QtPrivate::List<int, void>, true>'
    qobject.h: In static member function 'static void QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::call
    (QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function, Obj*, void*) [with Args = QtPrivate::List<QString, void>,
    Obj = Goo, Ret = void, Arg1 = int, QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function = void (Goo::*)(int)]':
    qobject.h:501:13: instantiated from 'void QObject::QSlotObject<Func, Args>::call(QObject*, void**) [with Func =
    void (Goo::*)(int), Args = QtPrivate::List<QString, void>, QObject = QObject]'
    main.cc:14:2: instantiated from here
    qobject.h:109:13: error: cannot conver
    

    开放式问题

    槽中的默认参数

    如果你有类似下面的代码:

    class A : public QObject { Q_OBJECT
     public slots:
     void someSlot(int foo = 0);
    };
    

    旧的连接方式允许你连接这个槽到信号上,不用带参数。 但是我不能从模板代码中知道一个函数是否带有默认参数。因此这个功能是被禁用的。 这里有个实现方法是,如果槽函数中参数数量多于信号函数中的参数数量时,退回到旧方式去连接。 不管怎样,这是相当不一致的,因此旧语法不再执行类型类型检查和类型转换。 它已经从分支中移除,并被合并。

    重载

    如你在上面例子中看到的那样,连接到QAbstractSocket::error,它不是真正完美的方式,因为error有一个重载。取得一个重载函数的地址需要隐式转换。比如一个连接如下所示:

    connect(mySpinBox, SIGNAL(valueChanged(int)), mySlider, SLOT(setValue(int));
    

    它不能简单的转换到:

    connect(mySpinBox, &QSpinBox::valueChanged, mySlider, &QSlider::setValue);
    

    因为 QSpinBox有2个信号,名字都叫valueChanged()带有不同的参数。 新方式要用下列代码替代:

    connect(mySpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), mySlider, &QSlider::setValue);
    

    最好的方式是尽量不要重载信号和槽。
    但是我们已经在过去的release版本中添加重载了,因为取得函数的地址不是我们支持的使用方式。 但是现在不破坏代码兼用性已经是不可能的。

    断开连接

    是否QMetaObject::Connection应该有一个disconnect()函数?
    其他的难题是,如果我们使用新语法,在一些对象关闭时,不能自动断开连接。一个方式是在断开连接中添加对象的集合,或者一个新函数例如QMetaObject::Connection::require

    auto c = connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
     receiver->updateValue("senderValue", newValue);
     } , QList<QObject> { receiver } ); // solution 1
    c.require(receiver); // solution 2
    

    方案2是否有效? 没有什么比得上QMetaObject::Connection::require()

    回调

    函数例如QHostInfo::lookupHost或QTimer::singleShot或QFileDialog::open 带有一个QObject接收者和 char* 的slot。 这在新方式中是不能用的。 如果你想用c++方式的回调,应该使用 std::function (or tr1)。但我们不能在我们的API中,使用STL类型,因此一个qt函数应该被完成当复制一个std::function时。 无论如何,这是和QObject连接是不相关的。

    译自:https://wiki.qt.io/New_Signal_Slot_Syntax

  • 相关阅读:
    今年暑假不AC
    亲和数
    改革春风吹满地
    hdu人见人爱A^B
    男人必看的10部电影
    富人和穷人之间的经典差异
    今日十句英文
    看透一个人起码要看的。
    人生经典20句
    解决ubuntu中mysql中文乱码问题
  • 原文地址:https://www.cnblogs.com/mushroom/p/5701330.html
Copyright © 2011-2022 走看看