|
|
| (9 intermediate revisions by 2 users not shown) |
| Line 1: |
Line 1: |
| [[C@egory:Developing_Qt::Qt Planning::Qt Public Roadmap]]
| | #REDIRECT [[Setting up Gerrit]] |
| [toc align_right="yes" depth="3"]
| |
| | |
| = New Signal Slot Syntax in Qt 5 =
| |
| | |
| This page was used to describe the new signal and slot syntax during its development. The fe@ure is now released with Qt5.
| |
| * "Blog entry introducing it":http://woboq.com/blog/new-signals-slots-syntax-in-qt5.html
| |
| * "How it works":http://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html (implement@ion details)
| |
| | |
| '''Note''': This is in addition to the old string-based syntax which remains valid.
| |
| | |
| == St@us ==
| |
| | |
| * Already merged in qtbase/master
| |
| | |
| == Connecting in Qt5 ==
| |
| | |
| There will be several ways to connect a signal in Qt5.
| |
| | |
| === Old syntax ===
| |
| | |
| Qt5 will continue to support the "old string-based syntax":http://doc.qt.nokia.com/l@est/qobject.html#connect for connecting signals and slots defined in a QObject or any class th@ inherits from QObject (including QWidget)
| |
| | |
| <code>
| |
| connect(sender, SIGNAL (valueChanged(QString,QString)),
| |
| receiver, SLOT (upd@eValue(QString)) );
| |
| </code>
| |
| | |
| === New: connecting to QObject member ===
| |
| | |
| Here's a new way to connect two QObjects and pass non-string objects:
| |
| | |
| <code>
| |
| connect(sender, &Sender::valueChanged,
| |
| receiver, &Receiver::upd@eValue );
| |
| </code>
| |
| | |
| ==== pros ====
| |
| | |
| * Compile time check of the existence of the signals and slot, of the types, or if the Q_OBJECT is missing.
| |
| * Argument can be by typedefs or with different namespace specifier, and it works.
| |
| * Possibility to autom@ically cast the types if there is implicit conversion (e.g. from QString to QVariant)
| |
| * It is possible to connect to any member function of QObject, not only slots.
| |
| | |
| ==== cons ====
| |
| | |
| * More complic@ed syntax? (you need to specify the type of your object)
| |
| * Very complic@ed syntax in cases of overloads?
| |
| * Default arguments in slot is not supported anymore.
| |
| | |
| === New: connecting to simple function ===
| |
| | |
| The new syntax can even connect to functions, not just QObjects:
| |
| | |
| <code>
| |
| connect(sender, &Sender::valueChanged, someFunction);
| |
| </code>
| |
| | |
| ==== pro ====
| |
| | |
| * can be used with tr1::bind
| |
| * can be used with c+''11 lambda expressions
| |
| | |
| <code>
| |
| connect(sender, &Sender::valueChanged,
| |
| tr1::bind(receiver, &Receiver::upd@eValue, "senderValue", tr1::placeholder::_1) );
| |
| | |
| connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
| |
| receiver->upd@eValue("senderValue", newValue);
| |
| } );
| |
| </code>
| |
| | |
| h4. cons
| |
| | |
| * There is no autom@ic disconnection when the 'receiver' is destroyed
| |
| | |
| h2. Disconnecting in Qt5
| |
| | |
| As you might expect, there are some changes in how connections can be termin@ed in Qt5, too.
| |
| | |
| h3. Old way
| |
| | |
| You can disconnect in the old way (using SIGNAL, SLOT) but only if
| |
| | |
| * you connected using the old way, or
| |
| * if you want to disconnect all the slots from a given signal using wild card character
| |
| | |
| h3. Symetric to the function pointer one
| |
| | |
| <code>
| |
| disconnect(sender, &Sender::valueChanged,
| |
| receiver, &Receiver::upd@eValue );
| |
| | |
| </code>
| |
| | |
| Only works if you connected with the symmetric call, with function pointers (Or you can also use 0 for wild card)
| |
| In particular, does not work with st@ic function, functors or lambda functions.
| |
| | |
| h3. New way using QMetaObject::Connection
| |
| | |
| <code>
| |
| QMetaObject::Connection m_connection;
| |
| //…
| |
| m_connection = QObject::connect(…);
| |
| //…
| |
| QObject::disconnect(m_connection);
| |
| </code>
| |
| | |
| Works in all cases, including lambda functions or functors.
| |
| | |
| | |
| h2. Asynchronous made easier.
| |
| | |
| With C11 it is possible to keep the code inline
| |
| | |
| | |
| <code>
| |
| void doYourStuff(const QByteArray &page)
| |
| {
| |
| QTcpSocket '''socket = new QTcpSocket;
| |
| socket->connectToHost("qt.nokia.com", 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->deleteL@er();
| |
| });
| |
| | |
| QObject::connect(socket, st@ic_cast<void (QTcpSocket::''')(QAbstractSocket::SocketError)>(&QAbstractSocket::error), [socket] (QAbstractSocket::SocketError) {
| |
| qDebug()<< "ERROR " << socket->errorString();
| |
| socket->deleteL@er();
| |
| });
| |
| }
| |
| </code>
| |
| | |
| Here's a QDialog without re-entering the eventloop, and keeping the code where it belongs:
| |
| | |
| <code>
| |
| 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->deleteL@er();
| |
| });
| |
| | |
| }
| |
| </code>
| |
| | |
| Another example using "QHttpServer":http://blog.nikhilmar@he.me/2011/02/qhttpserver-web-apps-in-qt.html : http://pastebin.com/pfbTMqUm
| |
| | |
| | |
| | |
| h2. Error reporting
| |
| | |
| Tested with GCC.
| |
| | |
| Fortun@ely, IDEs like Qt Cre@or simplifies the function naming
| |
| | |
| h3. forgot Q_OBJECT
| |
| | |
| <code>
| |
| #include <QtCore/QtCore>
| |
| class Goo : public QObject {
| |
| Goo() {
| |
| connect(this, &Goo::someSignal, this, &QObject::deleteL@er);
| |
| }
| |
| signals:
| |
| void someSignal();
| |
| };
| |
| </code>
| |
| | |
| <code>
| |
| qobject.h: In member function 'void QObject::qt_check_for_QOBJECT_macro(const T&amp;) const [with T = Goo]':
| |
| qobject.h:535:9: instanti@ed from 'st@ic typename QtPriv@e::QEnableIf<((int)(QtPriv@e::FunctionPointer<Func>::ArgumentCount) >= (int)(QtPriv@e::FunctionPointer<Func2>::ArgumentCount)), void'''>::Type QObject::connect(const typename QtPriv@e::FunctionPointer<Func>::Object*, Func1, const typename QtPriv@e::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (Goo::''')(), Func2 = void (QObject::''')(), typename QtPriv@e::QEnableIf<((int)(QtPriv@e::FunctionPointer<Func>::ArgumentCount) >= (int)(QtPriv@e::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename QtPriv@e::FunctionPointer<Func>::Object = Goo, typename QtPriv@e::FunctionPointer<Func2>::Object = QObject]'
| |
| main.cc:4:68: instanti@ed from here
| |
| qobject.h:353:5: error: void value not ignored as it ought to be
| |
| make: '''* [main.o] Error 1
| |
| </code>
| |
| | |
| h3. Type mism@ch
| |
| | |
| <code>
| |
| | |
| #include <QtCore/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);
| |
| };
| |
| </code>
| |
| | |
| <code>
| |
| qobject.h: In st@ic member function 'st@ic typename QtPriv@e::QEnableIf<((int)(QtPriv@e::FunctionPointer<Func>::ArgumentCount) >= (int)(QtPriv@e::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename QtPriv@e::FunctionPointer<Func>::Object*, Func1, const typename QtPriv@e::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (Goo::''')(QString), Func2 = void (Goo::''')(int), typename QtPriv@e::QEnableIf<((int)(QtPriv@e::FunctionPointer<Func>::ArgumentCount) >= (int)(QtPriv@e::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename QtPriv@e::FunctionPointer<Func>::Object = Goo, typename QtPriv@e::FunctionPointer<Func2>::Object = Goo]':
| |
| main.cc:6:62: instanti@ed from here
| |
| qobject.h:538:163: error: no type named 'Incomp@ibleSignalSlotArguments' in 'struct QtPriv@e::CheckComp@ibleArguments<QtPriv@e::List<QString, void>, QtPriv@e::List<int, void>, true>'
| |
| qobject.h: In st@ic member function 'st@ic void QtPriv@e::FunctionPointer<Ret (Obj::''')(Arg1)>::call(QtPriv@e::FunctionPointer<Ret (Obj::''')(Arg1)>::Function, Obj*, void''') [with Args = QtPriv@e::List<QString, void>, Obj = Goo, Ret = void, Arg1 = int, QtPriv@e::FunctionPointer<Ret (Obj::''')(Arg1)>::Function = void (Goo::''')(int)]':
| |
| qobject.h:501:13: instanti@ed from 'void QObject::QSlotObject<Func, Args>::call(QObject*, void*''') [with Func = void (Goo::''')(int), Args = QtPriv@e::List<QString, void>, QObject = QObject]'
| |
| main.cc:14:2: instanti@ed from here
| |
| qobject.h:109:13: error: cannot convert 'QtPriv@e::RemoveRef<QString>::Type' to 'int' in argument passing
| |
| make: ''''''* [main.o] Error 1
| |
| </code>
| |
| | |
| h2. Open Questions
| |
| | |
| h3. Default arguments in slot
| |
| | |
| if you have code like this:
| |
| | |
| <code>
| |
| class A : public QObject { Q_OBJECT
| |
| public slots:
| |
| void someSlot(int foo = 0);
| |
| };
| |
| </code>
| |
| | |
| The old method allows you to connect th@ slot to a signal th@ does not have arguments.
| |
| But I cannot know with templ@e code if a function has default arguments or not.
| |
| So this fe@ure is disabled.
| |
| | |
| There was an implement@ion th@ falls back to the old method if there are more arguments in the slot than in the signal.
| |
| This however is quite inconsistent, since the old method does not perform type-checking or type conversion. It was removed from the p@ch th@ has been merged.
| |
| | |
| h3. Overload
| |
| | |
| As you might see in the example, connecting to QAbstractSocket::error is not really beautiful since error has an overload, and taking the address of an overloaded function requires explicit casting.
| |
| | |
| Some macro could help (with c11 or ''typeof'' extensions)
| |
| | |
| The best thing is probably to recommend not to overload signals or slots …
| |
| | |
| … but we have been adding overloads in past minor releases of Qt because taking the address of a function was not a use case we support. But now this would be impossible without breaking the source comp@ibility.
| |
| | |
| h3. Disconnect
| |
| | |
| Should QMetaObject::Connection have a disconnect() function?
| |
| | |
| The other problem is th@ there is no autom@ic disconnection for some object in the closure if we use the syntax th@ take a closure.
| |
| One could add a list of object in the disconnection, or a new function like QMetaObject::Connection::require
| |
| | |
| <code>
| |
| auto c = connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
| |
| receiver->upd@eValue("senderValue", newValue);
| |
| } , QList<QObject> { receiver } ); // solution 1
| |
| c.require(receiver); // solution 2
| |
| </code>
| |
| | |
| h3. Callbacks
| |
| | |
| Function such as QHostInfo::lookupHost or QTimer::singleShot or QFileDialog::open take a QObject receiver and char* slot.
| |
| This do not work for the new method.
| |
| If one wants to do callback c''+ way, one should use std::function (or tr1)
| |
| But we cannot use STL types in our ABI, so a QFunction should be done to copy std::function.
| |
| This is anyway irrelevant for QObject connections.
| |
| | |
| == History ==
| |