| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Copyright (C) 2016 Intel Corporation. |
| ** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com> |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qobject.h" |
| #include "qobject_p.h" |
| #include "qmetaobject_p.h" |
| |
| #include "qabstracteventdispatcher.h" |
| #include "qabstracteventdispatcher_p.h" |
| #include "qcoreapplication.h" |
| #include "qcoreapplication_p.h" |
| #include "qvariant.h" |
| #include "qmetaobject.h" |
| #include <qregexp.h> |
| #if QT_CONFIG(regularexpression) |
| # include <qregularexpression.h> |
| #endif |
| #include <qthread.h> |
| #include <private/qthread_p.h> |
| #include <qdebug.h> |
| #include <qpair.h> |
| #include <qvarlengtharray.h> |
| #include <qscopeguard.h> |
| #include <qset.h> |
| #if QT_CONFIG(thread) |
| #include <qsemaphore.h> |
| #endif |
| #include <qsharedpointer.h> |
| |
| #include <private/qorderedmutexlocker_p.h> |
| #include <private/qhooks_p.h> |
| #include <qtcore_tracepoints_p.h> |
| |
| #include <new> |
| |
| #include <ctype.h> |
| #include <limits.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static int DIRECT_CONNECTION_ONLY = 0; |
| |
| Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set = Q_BASIC_ATOMIC_INITIALIZER(nullptr); |
| |
| void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set) |
| { |
| qt_signal_spy_callback_set.storeRelease(callback_set); |
| } |
| |
| QDynamicMetaObjectData::~QDynamicMetaObjectData() |
| { |
| } |
| |
| QAbstractDynamicMetaObject::~QAbstractDynamicMetaObject() |
| { |
| } |
| |
| static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) |
| { |
| int *types = new int [typeNames.count() + 1]; |
| Q_CHECK_PTR(types); |
| for (int i = 0; i < typeNames.count(); ++i) { |
| const QByteArray typeName = typeNames.at(i); |
| if (typeName.endsWith('*')) |
| types[i] = QMetaType::VoidStar; |
| else |
| types[i] = QMetaType::type(typeName); |
| |
| if (!types[i]) { |
| qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" |
| "(Make sure '%s' is registered using qRegisterMetaType().)", |
| typeName.constData(), typeName.constData()); |
| delete [] types; |
| return 0; |
| } |
| } |
| types[typeNames.count()] = 0; |
| |
| return types; |
| } |
| |
| static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc) |
| { |
| QScopedArrayPointer<int> types(new int [argc + 1]); |
| for (int i = 0; i < argc; ++i) { |
| const QArgumentType &type = argumentTypes[i]; |
| if (type.type()) |
| types[i] = type.type(); |
| else if (type.name().endsWith('*')) |
| types[i] = QMetaType::VoidStar; |
| else |
| types[i] = QMetaType::type(type.name()); |
| |
| if (!types[i]) { |
| qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" |
| "(Make sure '%s' is registered using qRegisterMetaType().)", |
| type.name().constData(), type.name().constData()); |
| return 0; |
| } |
| } |
| types[argc] = 0; |
| |
| return types.take(); |
| } |
| |
| static QBasicMutex _q_ObjectMutexPool[131]; |
| |
| /** |
| * \internal |
| * mutex to be locked when accessing the connectionlists or the senders list |
| */ |
| static inline QBasicMutex *signalSlotLock(const QObject *o) |
| { |
| return &_q_ObjectMutexPool[uint(quintptr(o)) % sizeof(_q_ObjectMutexPool)/sizeof(QBasicMutex)]; |
| } |
| |
| #if QT_VERSION < 0x60000 |
| extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) |
| {} |
| |
| extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) |
| {} |
| #endif |
| |
| void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = 0; |
| void (*QAbstractDeclarativeData::destroyed_qml1)(QAbstractDeclarativeData *, QObject *) = 0; |
| void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = 0; |
| void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = 0; |
| int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = 0; |
| bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = 0; |
| void (*QAbstractDeclarativeData::setWidgetParent)(QObject *, QObject *) = 0; |
| |
| /*! |
| \fn QObjectData::QObjectData() |
| \internal |
| */ |
| |
| |
| QObjectData::~QObjectData() {} |
| |
| QMetaObject *QObjectData::dynamicMetaObject() const |
| { |
| return metaObject->toDynamicMetaObject(q_ptr); |
| } |
| |
| QObjectPrivate::QObjectPrivate(int version) |
| : threadData(0), currentChildBeingDeleted(0) |
| { |
| #ifdef QT_BUILD_INTERNAL |
| // Don't check the version parameter in internal builds. |
| // This allows incompatible versions to be loaded, possibly for testing. |
| Q_UNUSED(version); |
| #else |
| if (Q_UNLIKELY(version != QObjectPrivateVersion)) |
| qFatal("Cannot mix incompatible Qt library (version 0x%x) with this library (version 0x%x)", |
| version, QObjectPrivateVersion); |
| #endif |
| |
| // QObjectData initialization |
| q_ptr = 0; |
| parent = 0; // no parent yet. It is set by setParent() |
| isWidget = false; // assume not a widget object |
| blockSig = false; // not blocking signals |
| wasDeleted = false; // double-delete catcher |
| isDeletingChildren = false; // set by deleteChildren() |
| sendChildEvents = true; // if we should send ChildAdded and ChildRemoved events to parent |
| receiveChildEvents = true; |
| postedEvents = 0; |
| extraData = 0; |
| metaObject = 0; |
| isWindow = false; |
| deleteLaterCalled = false; |
| } |
| |
| QObjectPrivate::~QObjectPrivate() |
| { |
| if (extraData && !extraData->runningTimers.isEmpty()) { |
| if (Q_LIKELY(threadData->thread.loadAcquire() == QThread::currentThread())) { |
| // unregister pending timers |
| if (threadData->hasEventDispatcher()) |
| threadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr); |
| |
| // release the timer ids back to the pool |
| for (int i = 0; i < extraData->runningTimers.size(); ++i) |
| QAbstractEventDispatcherPrivate::releaseTimerId(extraData->runningTimers.at(i)); |
| } else { |
| qWarning("QObject::~QObject: Timers cannot be stopped from another thread"); |
| } |
| } |
| |
| if (postedEvents) |
| QCoreApplication::removePostedEvents(q_ptr, 0); |
| |
| threadData->deref(); |
| |
| if (metaObject) metaObject->objectDestroyed(q_ptr); |
| |
| #ifndef QT_NO_USERDATA |
| if (extraData) |
| qDeleteAll(extraData->userData); |
| #endif |
| delete extraData; |
| } |
| |
| /*! |
| \internal |
| For a given metaobject, compute the signal offset, and the method offset (including signals) |
| */ |
| static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset) |
| { |
| *signalOffset = *methodOffset = 0; |
| const QMetaObject *m = metaobject->d.superdata; |
| while (m) { |
| const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m); |
| *methodOffset += d->methodCount; |
| Q_ASSERT(d->revision >= 4); |
| *signalOffset += d->signalCount; |
| m = m->d.superdata; |
| } |
| } |
| |
| // Used by QAccessibleWidget |
| bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const |
| { |
| Q_Q(const QObject); |
| int signal_index = signalIndex(signal); |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (signal_index < 0 || !cd) |
| return false; |
| QBasicMutexLocker locker(signalSlotLock(q)); |
| if (signal_index < cd->signalVectorCount()) { |
| const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); |
| |
| while (c) { |
| if (c->receiver.loadRelaxed() == receiver) |
| return true; |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| } |
| return false; |
| } |
| |
| // Used by QAccessibleWidget |
| QObjectList QObjectPrivate::receiverList(const char *signal) const |
| { |
| QObjectList returnValue; |
| int signal_index = signalIndex(signal); |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (signal_index < 0 || !cd) |
| return returnValue; |
| if (signal_index < cd->signalVectorCount()) { |
| const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); |
| |
| while (c) { |
| QObject *r = c->receiver.loadRelaxed(); |
| if (r) |
| returnValue << r; |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| } |
| return returnValue; |
| } |
| |
| // Used by QAccessibleWidget |
| QObjectList QObjectPrivate::senderList() const |
| { |
| QObjectList returnValue; |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (cd) { |
| QBasicMutexLocker locker(signalSlotLock(q_func())); |
| for (Connection *c = cd->senders; c; c = c->next) |
| returnValue << c->sender; |
| } |
| return returnValue; |
| } |
| |
| /*! |
| \internal |
| Add the connection \a c to the list of connections of the sender's object |
| for the specified \a signal |
| |
| The signalSlotLock() of the sender and receiver must be locked while calling |
| this function |
| |
| Will also add the connection in the sender's list of the receiver. |
| */ |
| void QObjectPrivate::addConnection(int signal, Connection *c) |
| { |
| Q_ASSERT(c->sender == q_ptr); |
| ensureConnectionData(); |
| ConnectionData *cd = connections.loadRelaxed(); |
| cd->resizeSignalVector(signal + 1); |
| |
| ConnectionList &connectionList = cd->connectionsForSignal(signal); |
| if (connectionList.last.loadRelaxed()) { |
| Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed()); |
| connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c); |
| } else { |
| connectionList.first.storeRelaxed(c); |
| } |
| c->id = ++cd->currentConnectionId; |
| c->prevConnectionList = connectionList.last.loadRelaxed(); |
| connectionList.last.storeRelaxed(c); |
| |
| QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed()); |
| rd->ensureConnectionData(); |
| |
| c->prev = &(rd->connections.loadRelaxed()->senders); |
| c->next = *c->prev; |
| *c->prev = c; |
| if (c->next) |
| c->next->prev = &c->next; |
| } |
| |
| void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c) |
| { |
| Q_ASSERT(c->receiver.loadRelaxed()); |
| ConnectionList &connections = signalVector.loadRelaxed()->at(c->signal_index); |
| c->receiver.storeRelaxed(nullptr); |
| QThreadData *td = c->receiverThreadData.loadRelaxed(); |
| if (td) |
| td->deref(); |
| c->receiverThreadData.storeRelaxed(nullptr); |
| |
| #ifndef QT_NO_DEBUG |
| bool found = false; |
| for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) { |
| if (cc == c) { |
| found = true; |
| break; |
| } |
| } |
| Q_ASSERT(found); |
| #endif |
| |
| // remove from the senders linked list |
| *c->prev = c->next; |
| if (c->next) |
| c->next->prev = c->prev; |
| c->prev = nullptr; |
| |
| if (connections.first.loadRelaxed() == c) |
| connections.first.storeRelaxed(c->nextConnectionList.loadRelaxed()); |
| if (connections.last.loadRelaxed() == c) |
| connections.last.storeRelaxed(c->prevConnectionList); |
| Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).first.loadRelaxed() != c); |
| Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).last.loadRelaxed() != c); |
| |
| // keep c->nextConnectionList intact, as it might still get accessed by activate |
| Connection *n = c->nextConnectionList.loadRelaxed(); |
| if (n) |
| n->prevConnectionList = c->prevConnectionList; |
| if (c->prevConnectionList) |
| c->prevConnectionList->nextConnectionList.storeRelaxed(n); |
| c->prevConnectionList = nullptr; |
| |
| Q_ASSERT(c != orphaned.loadRelaxed()); |
| // add c to orphanedConnections |
| c->nextInOrphanList = orphaned.loadRelaxed(); |
| orphaned.storeRelaxed(c); |
| |
| #ifndef QT_NO_DEBUG |
| found = false; |
| for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) { |
| if (cc == c) { |
| found = true; |
| break; |
| } |
| } |
| Q_ASSERT(!found); |
| #endif |
| |
| } |
| |
| void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender) |
| { |
| ConnectionOrSignalVector *c = nullptr; |
| { |
| QBasicMutexLocker l(signalSlotLock(sender)); |
| if (ref.loadAcquire() > 1) |
| return; |
| |
| // Since ref == 1, no activate() is in process since we locked the mutex. That implies, |
| // that nothing can reference the orphaned connection objects anymore and they can |
| // be safely deleted |
| c = orphaned.loadRelaxed(); |
| orphaned.storeRelaxed(nullptr); |
| } |
| deleteOrphaned(c); |
| } |
| |
| void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o) |
| { |
| while (o) { |
| QObjectPrivate::ConnectionOrSignalVector *next = nullptr; |
| if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) { |
| next = v->nextInOrphanList; |
| free(v); |
| } else { |
| QObjectPrivate::Connection *c = static_cast<Connection *>(o); |
| next = c->nextInOrphanList; |
| Q_ASSERT(!c->receiver.loadRelaxed()); |
| Q_ASSERT(!c->prev); |
| c->freeSlotObject(); |
| c->deref(); |
| } |
| o = next; |
| } |
| } |
| |
| /*! \internal |
| |
| Returns \c true if the signal with index \a signal_index from object \a sender is connected. |
| |
| \a signal_index must be the index returned by QObjectPrivate::signalIndex; |
| */ |
| bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative) const |
| { |
| if (checkDeclarative && isDeclarativeSignalConnected(signalIndex)) |
| return true; |
| |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (!cd) |
| return false; |
| SignalVector *signalVector = cd->signalVector.loadRelaxed(); |
| if (!signalVector) |
| return false; |
| |
| if (signalVector->at(-1).first.loadRelaxed()) |
| return true; |
| |
| if (signalIndex < uint(cd->signalVectorCount())) { |
| const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadRelaxed(); |
| while (c) { |
| if (c->receiver.loadRelaxed()) |
| return true; |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| } |
| return false; |
| } |
| |
| bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const |
| { |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (!cd) |
| return false; |
| SignalVector *signalVector = cd->signalVector.loadRelaxed(); |
| if (!signalVector) |
| return false; |
| |
| if (signalVector->at(-1).first.loadAcquire()) |
| return true; |
| |
| if (signalIndex < uint(cd->signalVectorCount())) { |
| const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadAcquire(); |
| return c != nullptr; |
| } |
| return false; |
| } |
| |
| /*! |
| \internal |
| */ |
| QAbstractMetaCallEvent::~QAbstractMetaCallEvent() |
| { |
| #if QT_CONFIG(thread) |
| if (semaphore_) |
| semaphore_->release(); |
| #endif |
| } |
| |
| /*! |
| \internal |
| */ |
| inline void QMetaCallEvent::allocArgs() |
| { |
| if (!d.nargs_) |
| return; |
| |
| constexpr size_t each = sizeof(void*) + sizeof(int); |
| void *const memory = d.nargs_ * each > sizeof(prealloc_) ? |
| calloc(d.nargs_, each) : prealloc_; |
| |
| Q_CHECK_PTR(memory); |
| d.args_ = static_cast<void **>(memory); |
| } |
| |
| /*! |
| \internal |
| |
| Used for blocking queued connections, just passes \a args through without |
| allocating any memory. |
| */ |
| QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, |
| QObjectPrivate::StaticMetaCallFunction callFunction, |
| const QObject *sender, int signalId, |
| void **args, QSemaphore *semaphore) |
| : QAbstractMetaCallEvent(sender, signalId, semaphore), |
| d({nullptr, args, callFunction, 0, method_offset, method_relative}), |
| prealloc_() |
| { |
| } |
| |
| /*! |
| \internal |
| |
| Used for blocking queued connections, just passes \a args through without |
| allocating any memory. |
| */ |
| QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO, |
| const QObject *sender, int signalId, |
| void **args, QSemaphore *semaphore) |
| : QAbstractMetaCallEvent(sender, signalId, semaphore), |
| d({slotO, args, nullptr, 0, 0, ushort(-1)}), |
| prealloc_() |
| { |
| if (d.slotObj_) |
| d.slotObj_->ref(); |
| } |
| |
| /*! |
| \internal |
| |
| Allocates memory for \a nargs; code creating an event needs to initialize |
| the void* and int arrays by accessing \a args() and \a types(), respectively. |
| */ |
| QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, |
| QObjectPrivate::StaticMetaCallFunction callFunction, |
| const QObject *sender, int signalId, |
| int nargs) |
| : QAbstractMetaCallEvent(sender, signalId), |
| d({nullptr, nullptr, callFunction, nargs, method_offset, method_relative}), |
| prealloc_() |
| { |
| allocArgs(); |
| } |
| |
| /*! |
| \internal |
| |
| Allocates memory for \a nargs; code creating an event needs to initialize |
| the void* and int arrays by accessing \a args() and \a types(), respectively. |
| */ |
| QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO, |
| const QObject *sender, int signalId, |
| int nargs) |
| : QAbstractMetaCallEvent(sender, signalId), |
| d({slotO, nullptr, nullptr, nargs, 0, ushort(-1)}), |
| prealloc_() |
| { |
| if (d.slotObj_) |
| d.slotObj_->ref(); |
| allocArgs(); |
| } |
| |
| /*! |
| \internal |
| */ |
| QMetaCallEvent::~QMetaCallEvent() |
| { |
| if (d.nargs_) { |
| int *typeIDs = types(); |
| for (int i = 0; i < d.nargs_; ++i) { |
| if (typeIDs[i] && d.args_[i]) |
| QMetaType::destroy(typeIDs[i], d.args_[i]); |
| } |
| if (reinterpret_cast<void*>(d.args_) != reinterpret_cast<void*>(prealloc_)) |
| free(d.args_); |
| } |
| if (d.slotObj_) |
| d.slotObj_->destroyIfLastRef(); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QMetaCallEvent::placeMetaCall(QObject *object) |
| { |
| if (d.slotObj_) { |
| d.slotObj_->call(object, d.args_); |
| } else if (d.callFunction_ && d.method_offset_ <= object->metaObject()->methodOffset()) { |
| d.callFunction_(object, QMetaObject::InvokeMetaMethod, d.method_relative_, d.args_); |
| } else { |
| QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, |
| d.method_offset_ + d.method_relative_, d.args_); |
| } |
| } |
| |
| /*! |
| \class QSignalBlocker |
| \brief Exception-safe wrapper around QObject::blockSignals(). |
| \since 5.3 |
| \ingroup objectmodel |
| \inmodule QtCore |
| |
| \reentrant |
| |
| QSignalBlocker can be used wherever you would otherwise use a |
| pair of calls to blockSignals(). It blocks signals in its |
| constructor and in the destructor it resets the state to what |
| it was before the constructor ran. |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 53 |
| is thus equivalent to |
| \snippet code/src_corelib_kernel_qobject.cpp 54 |
| |
| except the code using QSignalBlocker is safe in the face of |
| exceptions. |
| |
| \sa QMutexLocker, QEventLoopLocker |
| */ |
| |
| /*! |
| \fn QSignalBlocker::QSignalBlocker(QObject *object) |
| |
| Constructor. Calls \a{object}->blockSignals(true). |
| */ |
| |
| /*! |
| \fn QSignalBlocker::QSignalBlocker(QObject &object) |
| \overload |
| |
| Calls \a{object}.blockSignals(true). |
| */ |
| |
| /*! |
| \fn QSignalBlocker::QSignalBlocker(QSignalBlocker &&other) |
| |
| Move-constructs a signal blocker from \a other. \a other will have |
| a no-op destructor, while repsonsibility for restoring the |
| QObject::signalsBlocked() state is transferred to the new object. |
| */ |
| |
| /*! |
| \fn QSignalBlocker &QSignalBlocker::operator=(QSignalBlocker &&other) |
| |
| Move-assigns this signal blocker from \a other. \a other will have |
| a no-op destructor, while repsonsibility for restoring the |
| QObject::signalsBlocked() state is transferred to this object. |
| |
| The object's signals this signal blocker was blocking prior to |
| being moved to, if any, are unblocked \e except in the case where |
| both instances block the same object's signals and \c *this is |
| unblocked while \a other is not, at the time of the move. |
| */ |
| |
| /*! |
| \fn QSignalBlocker::~QSignalBlocker() |
| |
| Destructor. Restores the QObject::signalsBlocked() state to what it |
| was before the constructor ran, unless unblock() has been called |
| without a following reblock(), in which case it does nothing. |
| */ |
| |
| /*! |
| \fn void QSignalBlocker::reblock() |
| |
| Re-blocks signals after a previous unblock(). |
| |
| The numbers of reblock() and unblock() calls are not counted, so |
| every reblock() undoes any number of unblock() calls. |
| */ |
| |
| /*! |
| \fn void QSignalBlocker::unblock() |
| |
| Temporarily restores the QObject::signalsBlocked() state to what |
| it was before this QSignaBlocker's constructor ran. To undo, use |
| reblock(). |
| |
| The numbers of reblock() and unblock() calls are not counted, so |
| every unblock() undoes any number of reblock() calls. |
| */ |
| |
| /*! |
| \class QObject |
| \inmodule QtCore |
| \brief The QObject class is the base class of all Qt objects. |
| |
| \ingroup objectmodel |
| |
| \reentrant |
| |
| QObject is the heart of the Qt \l{Object Model}. The central |
| feature in this model is a very powerful mechanism for seamless |
| object communication called \l{signals and slots}. You can |
| connect a signal to a slot with connect() and destroy the |
| connection with disconnect(). To avoid never ending notification |
| loops you can temporarily block signals with blockSignals(). The |
| protected functions connectNotify() and disconnectNotify() make |
| it possible to track connections. |
| |
| QObjects organize themselves in \l {Object Trees & Ownership} |
| {object trees}. When you create a QObject with another object as |
| parent, the object will automatically add itself to the parent's |
| children() list. The parent takes ownership of the object; i.e., |
| it will automatically delete its children in its destructor. You |
| can look for an object by name and optionally type using |
| findChild() or findChildren(). |
| |
| Every object has an objectName() and its class name can be found |
| via the corresponding metaObject() (see QMetaObject::className()). |
| You can determine whether the object's class inherits another |
| class in the QObject inheritance hierarchy by using the |
| inherits() function. |
| |
| When an object is deleted, it emits a destroyed() signal. You can |
| catch this signal to avoid dangling references to QObjects. |
| |
| QObjects can receive events through event() and filter the events |
| of other objects. See installEventFilter() and eventFilter() for |
| details. A convenience handler, childEvent(), can be reimplemented |
| to catch child events. |
| |
| Last but not least, QObject provides the basic timer support in |
| Qt; see QTimer for high-level support for timers. |
| |
| Notice that the Q_OBJECT macro is mandatory for any object that |
| implements signals, slots or properties. You also need to run the |
| \l{moc}{Meta Object Compiler} on the source file. We strongly |
| recommend the use of this macro in all subclasses of QObject |
| regardless of whether or not they actually use signals, slots and |
| properties, since failure to do so may lead certain functions to |
| exhibit strange behavior. |
| |
| All Qt widgets inherit QObject. The convenience function |
| isWidgetType() returns whether an object is actually a widget. It |
| is much faster than |
| \l{qobject_cast()}{qobject_cast}<QWidget *>(\e{obj}) or |
| \e{obj}->\l{inherits()}{inherits}("QWidget"). |
| |
| Some QObject functions, e.g. children(), return a QObjectList. |
| QObjectList is a typedef for QList<QObject *>. |
| |
| \section1 Thread Affinity |
| |
| A QObject instance is said to have a \e{thread affinity}, or that |
| it \e{lives} in a certain thread. When a QObject receives a |
| \l{Qt::QueuedConnection}{queued signal} or a \l{The Event |
| System#Sending Events}{posted event}, the slot or event handler |
| will run in the thread that the object lives in. |
| |
| \note If a QObject has no thread affinity (that is, if thread() |
| returns zero), or if it lives in a thread that has no running event |
| loop, then it cannot receive queued signals or posted events. |
| |
| By default, a QObject lives in the thread in which it is created. |
| An object's thread affinity can be queried using thread() and |
| changed using moveToThread(). |
| |
| All QObjects must live in the same thread as their parent. Consequently: |
| |
| \list |
| \li setParent() will fail if the two QObjects involved live in |
| different threads. |
| \li When a QObject is moved to another thread, all its children |
| will be automatically moved too. |
| \li moveToThread() will fail if the QObject has a parent. |
| \li If QObjects are created within QThread::run(), they cannot |
| become children of the QThread object because the QThread does |
| not live in the thread that calls QThread::run(). |
| \endlist |
| |
| \note A QObject's member variables \e{do not} automatically become |
| its children. The parent-child relationship must be set by either |
| passing a pointer to the child's \l{QObject()}{constructor}, or by |
| calling setParent(). Without this step, the object's member variables |
| will remain in the old thread when moveToThread() is called. |
| |
| \target No copy constructor |
| \section1 No Copy Constructor or Assignment Operator |
| |
| QObject has neither a copy constructor nor an assignment operator. |
| This is by design. Actually, they are declared, but in a |
| \c{private} section with the macro Q_DISABLE_COPY(). In fact, all |
| Qt classes derived from QObject (direct or indirect) use this |
| macro to declare their copy constructor and assignment operator to |
| be private. The reasoning is found in the discussion on |
| \l{Identity vs Value} {Identity vs Value} on the Qt \l{Object |
| Model} page. |
| |
| The main consequence is that you should use pointers to QObject |
| (or to your QObject subclass) where you might otherwise be tempted |
| to use your QObject subclass as a value. For example, without a |
| copy constructor, you can't use a subclass of QObject as the value |
| to be stored in one of the container classes. You must store |
| pointers. |
| |
| \section1 Auto-Connection |
| |
| Qt's meta-object system provides a mechanism to automatically connect |
| signals and slots between QObject subclasses and their children. As long |
| as objects are defined with suitable object names, and slots follow a |
| simple naming convention, this connection can be performed at run-time |
| by the QMetaObject::connectSlotsByName() function. |
| |
| \l uic generates code that invokes this function to enable |
| auto-connection to be performed between widgets on forms created |
| with \e{Qt Designer}. More information about using auto-connection with \e{Qt Designer} is |
| given in the \l{Using a Designer UI File in Your Application} section of |
| the \e{Qt Designer} manual. |
| |
| \section1 Dynamic Properties |
| |
| From Qt 4.2, dynamic properties can be added to and removed from QObject |
| instances at run-time. Dynamic properties do not need to be declared at |
| compile-time, yet they provide the same advantages as static properties |
| and are manipulated using the same API - using property() to read them |
| and setProperty() to write them. |
| |
| From Qt 4.3, dynamic properties are supported by |
| \l{Qt Designer's Widget Editing Mode#The Property Editor}{Qt Designer}, |
| and both standard Qt widgets and user-created forms can be given dynamic |
| properties. |
| |
| \section1 Internationalization (I18n) |
| |
| All QObject subclasses support Qt's translation features, making it possible |
| to translate an application's user interface into different languages. |
| |
| To make user-visible text translatable, it must be wrapped in calls to |
| the tr() function. This is explained in detail in the |
| \l{Writing Source Code for Translation} document. |
| |
| \sa QMetaObject, QPointer, QObjectCleanupHandler, Q_DISABLE_COPY() |
| \sa {Object Trees & Ownership} |
| */ |
| |
| /***************************************************************************** |
| QObject member functions |
| *****************************************************************************/ |
| |
| // check the constructor's parent thread argument |
| static bool check_parent_thread(QObject *parent, |
| QThreadData *parentThreadData, |
| QThreadData *currentThreadData) |
| { |
| if (parent && parentThreadData != currentThreadData) { |
| QThread *parentThread = parentThreadData->thread.loadAcquire(); |
| QThread *currentThread = currentThreadData->thread.loadAcquire(); |
| qWarning("QObject: Cannot create children for a parent that is in a different thread.\n" |
| "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)", |
| parent->metaObject()->className(), |
| parent, |
| parentThread ? parentThread->metaObject()->className() : "QThread", |
| parentThread, |
| currentThread ? currentThread->metaObject()->className() : "QThread", |
| currentThread); |
| return false; |
| } |
| return true; |
| } |
| |
| /*! |
| Constructs an object with parent object \a parent. |
| |
| The parent of an object may be viewed as the object's owner. For |
| instance, a \l{QDialog}{dialog box} is the parent of the \uicontrol{OK} |
| and \uicontrol{Cancel} buttons it contains. |
| |
| The destructor of a parent object destroys all child objects. |
| |
| Setting \a parent to \nullptr constructs an object with no parent. If the |
| object is a widget, it will become a top-level window. |
| |
| \sa parent(), findChild(), findChildren() |
| */ |
| |
| QObject::QObject(QObject *parent) |
| : d_ptr(new QObjectPrivate) |
| { |
| Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself"); |
| |
| Q_D(QObject); |
| d_ptr->q_ptr = this; |
| d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); |
| d->threadData->ref(); |
| if (parent) { |
| QT_TRY { |
| if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) |
| parent = 0; |
| setParent(parent); |
| } QT_CATCH(...) { |
| d->threadData->deref(); |
| QT_RETHROW; |
| } |
| } |
| #if QT_VERSION < 0x60000 |
| qt_addObject(this); |
| #endif |
| if (Q_UNLIKELY(qtHookData[QHooks::AddQObject])) |
| reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this); |
| Q_TRACE(QObject_ctor, this); |
| } |
| |
| /*! |
| \internal |
| */ |
| QObject::QObject(QObjectPrivate &dd, QObject *parent) |
| : d_ptr(&dd) |
| { |
| Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself"); |
| |
| Q_D(QObject); |
| d_ptr->q_ptr = this; |
| d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); |
| d->threadData->ref(); |
| if (parent) { |
| QT_TRY { |
| if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) |
| parent = 0; |
| if (d->isWidget) { |
| if (parent) { |
| d->parent = parent; |
| d->parent->d_func()->children.append(this); |
| } |
| // no events sent here, this is done at the end of the QWidget constructor |
| } else { |
| setParent(parent); |
| } |
| } QT_CATCH(...) { |
| d->threadData->deref(); |
| QT_RETHROW; |
| } |
| } |
| #if QT_VERSION < 0x60000 |
| qt_addObject(this); |
| #endif |
| if (Q_UNLIKELY(qtHookData[QHooks::AddQObject])) |
| reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this); |
| Q_TRACE(QObject_ctor, this); |
| } |
| |
| /*! |
| Destroys the object, deleting all its child objects. |
| |
| All signals to and from the object are automatically disconnected, and |
| any pending posted events for the object are removed from the event |
| queue. However, it is often safer to use deleteLater() rather than |
| deleting a QObject subclass directly. |
| |
| \warning All child objects are deleted. If any of these objects |
| are on the stack or global, sooner or later your program will |
| crash. We do not recommend holding pointers to child objects from |
| outside the parent. If you still do, the destroyed() signal gives |
| you an opportunity to detect when an object is destroyed. |
| |
| \warning Deleting a QObject while pending events are waiting to |
| be delivered can cause a crash. You must not delete the QObject |
| directly if it exists in a different thread than the one currently |
| executing. Use deleteLater() instead, which will cause the event |
| loop to delete the object after all pending events have been |
| delivered to it. |
| |
| \sa deleteLater() |
| */ |
| |
| QObject::~QObject() |
| { |
| Q_D(QObject); |
| d->wasDeleted = true; |
| d->blockSig = 0; // unblock signals so we always emit destroyed() |
| |
| QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed(); |
| if (sharedRefcount) { |
| if (sharedRefcount->strongref.loadRelaxed() > 0) { |
| qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash."); |
| // but continue deleting, it's too late to stop anyway |
| } |
| |
| // indicate to all QWeakPointers that this QObject has now been deleted |
| sharedRefcount->strongref.storeRelaxed(0); |
| if (!sharedRefcount->weakref.deref()) |
| delete sharedRefcount; |
| } |
| |
| if (!d->isWidget && d->isSignalConnected(0)) { |
| emit destroyed(this); |
| } |
| |
| if (d->declarativeData) { |
| if (static_cast<QAbstractDeclarativeDataImpl*>(d->declarativeData)->ownedByQml1) { |
| if (QAbstractDeclarativeData::destroyed_qml1) |
| QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); |
| } else { |
| if (QAbstractDeclarativeData::destroyed) |
| QAbstractDeclarativeData::destroyed(d->declarativeData, this); |
| } |
| } |
| |
| QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); |
| if (cd) { |
| if (cd->currentSender) { |
| cd->currentSender->receiverDeleted(); |
| cd->currentSender = nullptr; |
| } |
| |
| QBasicMutex *signalSlotMutex = signalSlotLock(this); |
| QBasicMutexLocker locker(signalSlotMutex); |
| |
| // disconnect all receivers |
| int receiverCount = cd->signalVectorCount(); |
| for (int signal = -1; signal < receiverCount; ++signal) { |
| QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal); |
| |
| while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) { |
| Q_ASSERT(c->receiver.loadAcquire()); |
| |
| QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed()); |
| bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); |
| if (c->receiver.loadAcquire()) { |
| cd->removeConnection(c); |
| Q_ASSERT(connectionList.first.loadRelaxed() != c); |
| } |
| if (needToUnlock) |
| m->unlock(); |
| } |
| } |
| |
| /* Disconnect all senders: |
| */ |
| while (QObjectPrivate::Connection *node = cd->senders) { |
| Q_ASSERT(node->receiver.loadAcquire()); |
| QObject *sender = node->sender; |
| // Send disconnectNotify before removing the connection from sender's connection list. |
| // This ensures any eventual destructor of sender will block on getting receiver's lock |
| // and not finish until we release it. |
| sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index)); |
| QBasicMutex *m = signalSlotLock(sender); |
| bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); |
| //the node has maybe been removed while the mutex was unlocked in relock? |
| if (node != cd->senders) { |
| // We hold the wrong mutex |
| Q_ASSERT(needToUnlock); |
| m->unlock(); |
| continue; |
| } |
| |
| QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.loadRelaxed(); |
| Q_ASSERT(senderData); |
| |
| QtPrivate::QSlotObjectBase *slotObj = nullptr; |
| if (node->isSlotObject) { |
| slotObj = node->slotObj; |
| node->isSlotObject = false; |
| } |
| |
| senderData->removeConnection(node); |
| if (needToUnlock) |
| m->unlock(); |
| |
| if (slotObj) { |
| locker.unlock(); |
| slotObj->destroyIfLastRef(); |
| locker.relock(); |
| } |
| } |
| |
| // invalidate all connections on the object and make sure |
| // activate() will skip them |
| cd->currentConnectionId.storeRelaxed(0); |
| } |
| if (cd && !cd->ref.deref()) |
| delete cd; |
| d->connections.storeRelaxed(nullptr); |
| |
| if (!d->children.isEmpty()) |
| d->deleteChildren(); |
| |
| #if QT_VERSION < 0x60000 |
| qt_removeObject(this); |
| #endif |
| if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject])) |
| reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this); |
| |
| Q_TRACE(QObject_dtor, this); |
| |
| if (d->parent) // remove it from parent object |
| d->setParent_helper(0); |
| } |
| |
| QObjectPrivate::Connection::~Connection() |
| { |
| if (ownArgumentTypes) { |
| const int *v = argumentTypes.loadRelaxed(); |
| if (v != &DIRECT_CONNECTION_ONLY) |
| delete [] v; |
| } |
| if (isSlotObject) |
| slotObj->destroyIfLastRef(); |
| } |
| |
| |
| /*! |
| \fn const QMetaObject *QObject::metaObject() const |
| |
| Returns a pointer to the meta-object of this object. |
| |
| A meta-object contains information about a class that inherits |
| QObject, e.g. class name, superclass name, properties, signals and |
| slots. Every QObject subclass that contains the Q_OBJECT macro will have a |
| meta-object. |
| |
| The meta-object information is required by the signal/slot |
| connection mechanism and the property system. The inherits() |
| function also makes use of the meta-object. |
| |
| If you have no pointer to an actual object instance but still |
| want to access the meta-object of a class, you can use \l |
| staticMetaObject. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 1 |
| |
| \sa staticMetaObject |
| */ |
| |
| /*! |
| \variable QObject::staticMetaObject |
| |
| This variable stores the meta-object for the class. |
| |
| A meta-object contains information about a class that inherits |
| QObject, e.g. class name, superclass name, properties, signals and |
| slots. Every class that contains the Q_OBJECT macro will also have |
| a meta-object. |
| |
| The meta-object information is required by the signal/slot |
| connection mechanism and the property system. The inherits() |
| function also makes use of the meta-object. |
| |
| If you have a pointer to an object, you can use metaObject() to |
| retrieve the meta-object associated with that object. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 2 |
| |
| \sa metaObject() |
| */ |
| |
| /*! |
| \fn template <class T> T qobject_cast(QObject *object) |
| \fn template <class T> T qobject_cast(const QObject *object) |
| \relates QObject |
| |
| Returns the given \a object cast to type T if the object is of type |
| T (or of a subclass); otherwise returns \nullptr. If \a object is |
| \nullptr then it will also return \nullptr. |
| |
| The class T must inherit (directly or indirectly) QObject and be |
| declared with the \l Q_OBJECT macro. |
| |
| A class is considered to inherit itself. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 3 |
| |
| The qobject_cast() function behaves similarly to the standard C++ |
| \c dynamic_cast(), with the advantages that it doesn't require |
| RTTI support and it works across dynamic library boundaries. |
| |
| qobject_cast() can also be used in conjunction with interfaces; |
| see the \l{tools/plugandpaint/app}{Plug & Paint} example for details. |
| |
| \warning If T isn't declared with the Q_OBJECT macro, this |
| function's return value is undefined. |
| |
| \sa QObject::inherits() |
| */ |
| |
| /*! |
| \fn bool QObject::inherits(const char *className) const |
| |
| Returns \c true if this object is an instance of a class that |
| inherits \a className or a QObject subclass that inherits \a |
| className; otherwise returns \c false. |
| |
| A class is considered to inherit itself. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 4 |
| |
| If you need to determine whether an object is an instance of a particular |
| class for the purpose of casting it, consider using qobject_cast<Type *>(object) |
| instead. |
| |
| \sa metaObject(), qobject_cast() |
| */ |
| |
| /*! |
| \property QObject::objectName |
| |
| \brief the name of this object |
| |
| You can find an object by name (and type) using findChild(). |
| You can find a set of objects with findChildren(). |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 5 |
| |
| By default, this property contains an empty string. |
| |
| \sa metaObject(), QMetaObject::className() |
| */ |
| |
| QString QObject::objectName() const |
| { |
| Q_D(const QObject); |
| return d->extraData ? d->extraData->objectName : QString(); |
| } |
| |
| /* |
| Sets the object's name to \a name. |
| */ |
| void QObject::setObjectName(const QString &name) |
| { |
| Q_D(QObject); |
| if (!d->extraData) |
| d->extraData = new QObjectPrivate::ExtraData; |
| |
| if (d->extraData->objectName != name) { |
| d->extraData->objectName = name; |
| emit objectNameChanged(d->extraData->objectName, QPrivateSignal()); |
| } |
| } |
| |
| /*! \fn void QObject::objectNameChanged(const QString &objectName) |
| |
| This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName. |
| |
| \sa QObject::objectName |
| */ |
| |
| /*! |
| \fn bool QObject::isWidgetType() const |
| |
| Returns \c true if the object is a widget; otherwise returns \c false. |
| |
| Calling this function is equivalent to calling |
| \c{inherits("QWidget")}, except that it is much faster. |
| */ |
| |
| /*! |
| \fn bool QObject::isWindowType() const |
| |
| Returns \c true if the object is a window; otherwise returns \c false. |
| |
| Calling this function is equivalent to calling |
| \c{inherits("QWindow")}, except that it is much faster. |
| */ |
| |
| /*! |
| This virtual function receives events to an object and should |
| return true if the event \a e was recognized and processed. |
| |
| The event() function can be reimplemented to customize the |
| behavior of an object. |
| |
| Make sure you call the parent event class implementation |
| for all the events you did not handle. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 52 |
| |
| \sa installEventFilter(), timerEvent(), QCoreApplication::sendEvent(), |
| QCoreApplication::postEvent() |
| */ |
| |
| bool QObject::event(QEvent *e) |
| { |
| switch (e->type()) { |
| case QEvent::Timer: |
| timerEvent((QTimerEvent*)e); |
| break; |
| |
| case QEvent::ChildAdded: |
| case QEvent::ChildPolished: |
| case QEvent::ChildRemoved: |
| childEvent((QChildEvent*)e); |
| break; |
| |
| case QEvent::DeferredDelete: |
| qDeleteInEventHandler(this); |
| break; |
| |
| case QEvent::MetaCall: |
| { |
| QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e); |
| |
| if (!d_func()->connections.loadRelaxed()) { |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| d_func()->ensureConnectionData(); |
| } |
| QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId()); |
| |
| mce->placeMetaCall(this); |
| break; |
| } |
| |
| case QEvent::ThreadChange: { |
| Q_D(QObject); |
| QThreadData *threadData = d->threadData; |
| QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed(); |
| if (eventDispatcher) { |
| QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this); |
| if (!timers.isEmpty()) { |
| // do not to release our timer ids back to the pool (since the timer ids are moving to a new thread). |
| eventDispatcher->unregisterTimers(this); |
| QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection, |
| Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers)))); |
| } |
| } |
| break; |
| } |
| |
| default: |
| if (e->type() >= QEvent::User) { |
| customEvent(e); |
| break; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /*! |
| \fn void QObject::timerEvent(QTimerEvent *event) |
| |
| This event handler can be reimplemented in a subclass to receive |
| timer events for the object. |
| |
| QTimer provides a higher-level interface to the timer |
| functionality, and also more general information about timers. The |
| timer event is passed in the \a event parameter. |
| |
| \sa startTimer(), killTimer(), event() |
| */ |
| |
| void QObject::timerEvent(QTimerEvent *) |
| { |
| } |
| |
| |
| /*! |
| This event handler can be reimplemented in a subclass to receive |
| child events. The event is passed in the \a event parameter. |
| |
| QEvent::ChildAdded and QEvent::ChildRemoved events are sent to |
| objects when children are added or removed. In both cases you can |
| only rely on the child being a QObject, or if isWidgetType() |
| returns \c true, a QWidget. (This is because, in the |
| \l{QEvent::ChildAdded}{ChildAdded} case, the child is not yet |
| fully constructed, and in the \l{QEvent::ChildRemoved}{ChildRemoved} |
| case it might have been destructed already). |
| |
| QEvent::ChildPolished events are sent to widgets when children |
| are polished, or when polished children are added. If you receive |
| a child polished event, the child's construction is usually |
| completed. However, this is not guaranteed, and multiple polish |
| events may be delivered during the execution of a widget's |
| constructor. |
| |
| For every child widget, you receive one |
| \l{QEvent::ChildAdded}{ChildAdded} event, zero or more |
| \l{QEvent::ChildPolished}{ChildPolished} events, and one |
| \l{QEvent::ChildRemoved}{ChildRemoved} event. |
| |
| The \l{QEvent::ChildPolished}{ChildPolished} event is omitted if |
| a child is removed immediately after it is added. If a child is |
| polished several times during construction and destruction, you |
| may receive several child polished events for the same child, |
| each time with a different virtual table. |
| |
| \sa event() |
| */ |
| |
| void QObject::childEvent(QChildEvent * /* event */) |
| { |
| } |
| |
| |
| /*! |
| This event handler can be reimplemented in a subclass to receive |
| custom events. Custom events are user-defined events with a type |
| value at least as large as the QEvent::User item of the |
| QEvent::Type enum, and is typically a QEvent subclass. The event |
| is passed in the \a event parameter. |
| |
| \sa event(), QEvent |
| */ |
| void QObject::customEvent(QEvent * /* event */) |
| { |
| } |
| |
| |
| |
| /*! |
| Filters events if this object has been installed as an event |
| filter for the \a watched object. |
| |
| In your reimplementation of this function, if you want to filter |
| the \a event out, i.e. stop it being handled further, return |
| true; otherwise return false. |
| |
| Example: |
| \snippet code/src_corelib_kernel_qobject.cpp 6 |
| |
| Notice in the example above that unhandled events are passed to |
| the base class's eventFilter() function, since the base class |
| might have reimplemented eventFilter() for its own internal |
| purposes. |
| |
| Some events, such as \l QEvent::ShortcutOverride must be explicitly |
| accepted (by calling \l {QEvent::}{accept()} on them) in order to prevent |
| propagation. |
| |
| \warning If you delete the receiver object in this function, be |
| sure to return true. Otherwise, Qt will forward the event to the |
| deleted object and the program might crash. |
| |
| \sa installEventFilter() |
| */ |
| |
| bool QObject::eventFilter(QObject * /* watched */, QEvent * /* event */) |
| { |
| return false; |
| } |
| |
| /*! |
| \fn bool QObject::signalsBlocked() const |
| |
| Returns \c true if signals are blocked; otherwise returns \c false. |
| |
| Signals are not blocked by default. |
| |
| \sa blockSignals(), QSignalBlocker |
| */ |
| |
| /*! |
| If \a block is true, signals emitted by this object are blocked |
| (i.e., emitting a signal will not invoke anything connected to it). |
| If \a block is false, no such blocking will occur. |
| |
| The return value is the previous value of signalsBlocked(). |
| |
| Note that the destroyed() signal will be emitted even if the signals |
| for this object have been blocked. |
| |
| Signals emitted while being blocked are not buffered. |
| |
| \sa signalsBlocked(), QSignalBlocker |
| */ |
| |
| bool QObject::blockSignals(bool block) noexcept |
| { |
| Q_D(QObject); |
| bool previous = d->blockSig; |
| d->blockSig = block; |
| return previous; |
| } |
| |
| /*! |
| Returns the thread in which the object lives. |
| |
| \sa moveToThread() |
| */ |
| QThread *QObject::thread() const |
| { |
| return d_func()->threadData->thread.loadAcquire(); |
| } |
| |
| /*! |
| Changes the thread affinity for this object and its children. The |
| object cannot be moved if it has a parent. Event processing will |
| continue in the \a targetThread. |
| |
| To move an object to the main thread, use QApplication::instance() |
| to retrieve a pointer to the current application, and then use |
| QApplication::thread() to retrieve the thread in which the |
| application lives. For example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 7 |
| |
| If \a targetThread is \nullptr, all event processing for this object |
| and its children stops, as they are no longer associated with any |
| thread. |
| |
| Note that all active timers for the object will be reset. The |
| timers are first stopped in the current thread and restarted (with |
| the same interval) in the \a targetThread. As a result, constantly |
| moving an object between threads can postpone timer events |
| indefinitely. |
| |
| A QEvent::ThreadChange event is sent to this object just before |
| the thread affinity is changed. You can handle this event to |
| perform any special processing. Note that any new events that are |
| posted to this object will be handled in the \a targetThread, |
| provided it is not \nullptr: when it is \nullptr, no event processing |
| for this object or its children can happen, as they are no longer |
| associated with any thread. |
| |
| \warning This function is \e not thread-safe; the current thread |
| must be same as the current thread affinity. In other words, this |
| function can only "push" an object from the current thread to |
| another thread, it cannot "pull" an object from any arbitrary |
| thread to the current thread. There is one exception to this rule |
| however: objects with no thread affinity can be "pulled" to the |
| current thread. |
| |
| \sa thread() |
| */ |
| void QObject::moveToThread(QThread *targetThread) |
| { |
| Q_D(QObject); |
| |
| if (d->threadData->thread.loadAcquire() == targetThread) { |
| // object is already in this thread |
| return; |
| } |
| |
| if (d->parent != 0) { |
| qWarning("QObject::moveToThread: Cannot move objects with a parent"); |
| return; |
| } |
| if (d->isWidget) { |
| qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread"); |
| return; |
| } |
| |
| QThreadData *currentData = QThreadData::current(); |
| QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr; |
| if (d->threadData->thread.loadAcquire() == 0 && currentData == targetData) { |
| // one exception to the rule: we allow moving objects with no thread affinity to the current thread |
| currentData = d->threadData; |
| } else if (d->threadData != currentData) { |
| qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n" |
| "Cannot move to target thread (%p)\n", |
| currentData->thread.loadRelaxed(), d->threadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr); |
| |
| #ifdef Q_OS_MAC |
| qWarning("You might be loading two sets of Qt binaries into the same process. " |
| "Check that all plugins are compiled against the right Qt binaries. Export " |
| "DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded."); |
| #endif |
| |
| return; |
| } |
| |
| // prepare to move |
| d->moveToThread_helper(); |
| |
| if (!targetData) |
| targetData = new QThreadData(0); |
| |
| // make sure nobody adds/removes connections to this object while we're moving it |
| QMutexLocker l(signalSlotLock(this)); |
| |
| QOrderedMutexLocker locker(¤tData->postEventList.mutex, |
| &targetData->postEventList.mutex); |
| |
| // keep currentData alive (since we've got it locked) |
| currentData->ref(); |
| |
| // move the object |
| d_func()->setThreadData_helper(currentData, targetData); |
| |
| locker.unlock(); |
| |
| // now currentData can commit suicide if it wants to |
| currentData->deref(); |
| } |
| |
| void QObjectPrivate::moveToThread_helper() |
| { |
| Q_Q(QObject); |
| QEvent e(QEvent::ThreadChange); |
| QCoreApplication::sendEvent(q, &e); |
| for (int i = 0; i < children.size(); ++i) { |
| QObject *child = children.at(i); |
| child->d_func()->moveToThread_helper(); |
| } |
| } |
| |
| void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData) |
| { |
| Q_Q(QObject); |
| |
| // move posted events |
| int eventsMoved = 0; |
| for (int i = 0; i < currentData->postEventList.size(); ++i) { |
| const QPostEvent &pe = currentData->postEventList.at(i); |
| if (!pe.event) |
| continue; |
| if (pe.receiver == q) { |
| // move this post event to the targetList |
| targetData->postEventList.addEvent(pe); |
| const_cast<QPostEvent &>(pe).event = 0; |
| ++eventsMoved; |
| } |
| } |
| if (eventsMoved > 0 && targetData->hasEventDispatcher()) { |
| targetData->canWait = false; |
| targetData->eventDispatcher.loadRelaxed()->wakeUp(); |
| } |
| |
| // the current emitting thread shouldn't restore currentSender after calling moveToThread() |
| ConnectionData *cd = connections.loadRelaxed(); |
| if (cd) { |
| if (cd->currentSender) { |
| cd->currentSender->receiverDeleted(); |
| cd->currentSender = nullptr; |
| } |
| |
| // adjust the receiverThreadId values in the Connections |
| if (cd) { |
| auto *c = cd->senders; |
| while (c) { |
| QObject *r = c->receiver.loadRelaxed(); |
| if (r) { |
| Q_ASSERT(r == q); |
| targetData->ref(); |
| QThreadData *old = c->receiverThreadData.loadRelaxed(); |
| if (old) |
| old->deref(); |
| c->receiverThreadData.storeRelaxed(targetData); |
| } |
| c = c->next; |
| } |
| } |
| |
| } |
| |
| // set new thread data |
| targetData->ref(); |
| threadData->deref(); |
| threadData = targetData; |
| |
| for (int i = 0; i < children.size(); ++i) { |
| QObject *child = children.at(i); |
| child->d_func()->setThreadData_helper(currentData, targetData); |
| } |
| } |
| |
| void QObjectPrivate::_q_reregisterTimers(void *pointer) |
| { |
| Q_Q(QObject); |
| QList<QAbstractEventDispatcher::TimerInfo> *timerList = reinterpret_cast<QList<QAbstractEventDispatcher::TimerInfo> *>(pointer); |
| QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed(); |
| for (int i = 0; i < timerList->size(); ++i) { |
| const QAbstractEventDispatcher::TimerInfo &ti = timerList->at(i); |
| eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q); |
| } |
| delete timerList; |
| } |
| |
| |
| // |
| // The timer flag hasTimer is set when startTimer is called. |
| // It is not reset when killing the timer because more than |
| // one timer might be active. |
| // |
| |
| /*! |
| Starts a timer and returns a timer identifier, or returns zero if |
| it could not start a timer. |
| |
| A timer event will occur every \a interval milliseconds until |
| killTimer() is called. If \a interval is 0, then the timer event |
| occurs once every time there are no more window system events to |
| process. |
| |
| The virtual timerEvent() function is called with the QTimerEvent |
| event parameter class when a timer event occurs. Reimplement this |
| function to get timer events. |
| |
| If multiple timers are running, the QTimerEvent::timerId() can be |
| used to find out which timer was activated. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 8 |
| |
| Note that QTimer's accuracy depends on the underlying operating system and |
| hardware. The \a timerType argument allows you to customize the accuracy of |
| the timer. See Qt::TimerType for information on the different timer types. |
| Most platforms support an accuracy of 20 milliseconds; some provide more. |
| If Qt is unable to deliver the requested number of timer events, it will |
| silently discard some. |
| |
| The QTimer class provides a high-level programming interface with |
| single-shot timers and timer signals instead of events. There is |
| also a QBasicTimer class that is more lightweight than QTimer and |
| less clumsy than using timer IDs directly. |
| |
| \sa timerEvent(), killTimer(), QTimer::singleShot() |
| */ |
| |
| int QObject::startTimer(int interval, Qt::TimerType timerType) |
| { |
| Q_D(QObject); |
| |
| if (Q_UNLIKELY(interval < 0)) { |
| qWarning("QObject::startTimer: Timers cannot have negative intervals"); |
| return 0; |
| } |
| if (Q_UNLIKELY(!d->threadData->hasEventDispatcher())) { |
| qWarning("QObject::startTimer: Timers can only be used with threads started with QThread"); |
| return 0; |
| } |
| if (Q_UNLIKELY(thread() != QThread::currentThread())) { |
| qWarning("QObject::startTimer: Timers cannot be started from another thread"); |
| return 0; |
| } |
| int timerId = d->threadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this); |
| if (!d->extraData) |
| d->extraData = new QObjectPrivate::ExtraData; |
| d->extraData->runningTimers.append(timerId); |
| return timerId; |
| } |
| |
| /*! |
| \since 5.9 |
| \overload |
| \fn int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType) |
| |
| Starts a timer and returns a timer identifier, or returns zero if |
| it could not start a timer. |
| |
| A timer event will occur every \a time interval until killTimer() |
| is called. If \a time is equal to \c{std::chrono::duration::zero()}, |
| then the timer event occurs once every time there are no more window |
| system events to process. |
| |
| The virtual timerEvent() function is called with the QTimerEvent |
| event parameter class when a timer event occurs. Reimplement this |
| function to get timer events. |
| |
| If multiple timers are running, the QTimerEvent::timerId() can be |
| used to find out which timer was activated. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 8 |
| |
| Note that QTimer's accuracy depends on the underlying operating system and |
| hardware. The \a timerType argument allows you to customize the accuracy of |
| the timer. See Qt::TimerType for information on the different timer types. |
| Most platforms support an accuracy of 20 milliseconds; some provide more. |
| If Qt is unable to deliver the requested number of timer events, it will |
| silently discard some. |
| |
| The QTimer class provides a high-level programming interface with |
| single-shot timers and timer signals instead of events. There is |
| also a QBasicTimer class that is more lightweight than QTimer and |
| less clumsy than using timer IDs directly. |
| |
| \sa timerEvent(), killTimer(), QTimer::singleShot() |
| */ |
| |
| /*! |
| Kills the timer with timer identifier, \a id. |
| |
| The timer identifier is returned by startTimer() when a timer |
| event is started. |
| |
| \sa timerEvent(), startTimer() |
| */ |
| |
| void QObject::killTimer(int id) |
| { |
| Q_D(QObject); |
| if (Q_UNLIKELY(thread() != QThread::currentThread())) { |
| qWarning("QObject::killTimer: Timers cannot be stopped from another thread"); |
| return; |
| } |
| if (id) { |
| int at = d->extraData ? d->extraData->runningTimers.indexOf(id) : -1; |
| if (at == -1) { |
| // timer isn't owned by this object |
| qWarning("QObject::killTimer(): Error: timer id %d is not valid for object %p (%s, %ls), timer has not been killed", |
| id, |
| this, |
| metaObject()->className(), |
| qUtf16Printable(objectName())); |
| return; |
| } |
| |
| if (d->threadData->hasEventDispatcher()) |
| d->threadData->eventDispatcher.loadRelaxed()->unregisterTimer(id); |
| |
| d->extraData->runningTimers.remove(at); |
| QAbstractEventDispatcherPrivate::releaseTimerId(id); |
| } |
| } |
| |
| |
| /*! |
| \fn QObject *QObject::parent() const |
| |
| Returns a pointer to the parent object. |
| |
| \sa children() |
| */ |
| |
| /*! |
| \fn const QObjectList &QObject::children() const |
| |
| Returns a list of child objects. |
| The QObjectList class is defined in the \c{<QObject>} header |
| file as the following: |
| |
| \quotefromfile kernel/qobject.h |
| \skipto /typedef .*QObjectList/ |
| \printuntil QObjectList |
| |
| The first child added is the \l{QList::first()}{first} object in |
| the list and the last child added is the \l{QList::last()}{last} |
| object in the list, i.e. new children are appended at the end. |
| |
| Note that the list order changes when QWidget children are |
| \l{QWidget::raise()}{raised} or \l{QWidget::lower()}{lowered}. A |
| widget that is raised becomes the last object in the list, and a |
| widget that is lowered becomes the first object in the list. |
| |
| \sa findChild(), findChildren(), parent(), setParent() |
| */ |
| |
| |
| /*! |
| \fn template<typename T> T *QObject::findChild(const QString &name, Qt::FindChildOptions options) const |
| |
| Returns the child of this object that can be cast into type T and |
| that is called \a name, or \nullptr if there is no such object. |
| Omitting the \a name argument causes all object names to be matched. |
| The search is performed recursively, unless \a options specifies the |
| option FindDirectChildrenOnly. |
| |
| If there is more than one child matching the search, the most |
| direct ancestor is returned. If there are several direct |
| ancestors, it is undefined which one will be returned. In that |
| case, findChildren() should be used. |
| |
| This example returns a child \c{QPushButton} of \c{parentWidget} |
| named \c{"button1"}, even if the button isn't a direct child of |
| the parent: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 10 |
| |
| This example returns a \c{QListWidget} child of \c{parentWidget}: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 11 |
| |
| This example returns a child \c{QPushButton} of \c{parentWidget} |
| (its direct parent) named \c{"button1"}: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 41 |
| |
| This example returns a \c{QListWidget} child of \c{parentWidget}, |
| its direct parent: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 42 |
| |
| \sa findChildren() |
| */ |
| |
| /*! |
| \fn template<typename T> QList<T> QObject::findChildren(const QString &name, Qt::FindChildOptions options) const |
| |
| Returns all children of this object with the given \a name that can be |
| cast to type T, or an empty list if there are no such objects. |
| Omitting the \a name argument causes all object names to be matched. |
| The search is performed recursively, unless \a options specifies the |
| option FindDirectChildrenOnly. |
| |
| The following example shows how to find a list of child \c{QWidget}s of |
| the specified \c{parentWidget} named \c{widgetname}: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 12 |
| |
| This example returns all \c{QPushButton}s that are children of \c{parentWidget}: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 13 |
| |
| This example returns all \c{QPushButton}s that are immediate children of \c{parentWidget}: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 43 |
| |
| \sa findChild() |
| */ |
| |
| /*! |
| \fn template<typename T> QList<T> QObject::findChildren(const QRegExp ®Exp, Qt::FindChildOptions options) const |
| \overload findChildren() |
| \obsolete |
| |
| Returns the children of this object that can be cast to type T |
| and that have names matching the regular expression \a regExp, |
| or an empty list if there are no such objects. |
| The search is performed recursively, unless \a options specifies the |
| option FindDirectChildrenOnly. |
| |
| Use the findChildren overload taking a QRegularExpression instead. |
| */ |
| |
| /*! |
| \fn QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const |
| \overload findChildren() |
| |
| \since 5.0 |
| |
| Returns the children of this object that can be cast to type T |
| and that have names matching the regular expression \a re, |
| or an empty list if there are no such objects. |
| The search is performed recursively, unless \a options specifies the |
| option FindDirectChildrenOnly. |
| */ |
| |
| /*! |
| \fn template<typename T> T qFindChild(const QObject *obj, const QString &name) |
| \relates QObject |
| \overload qFindChildren() |
| \obsolete |
| |
| This function is equivalent to |
| \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name). |
| |
| \note This function was provided as a workaround for MSVC 6 |
| which did not support member template functions. It is advised |
| to use the other form in new code. |
| |
| \sa QObject::findChild() |
| */ |
| |
| /*! |
| \fn template<typename T> QList<T> qFindChildren(const QObject *obj, const QString &name) |
| \relates QObject |
| \overload qFindChildren() |
| \obsolete |
| |
| This function is equivalent to |
| \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name). |
| |
| \note This function was provided as a workaround for MSVC 6 |
| which did not support member template functions. It is advised |
| to use the other form in new code. |
| |
| \sa QObject::findChildren() |
| */ |
| |
| /*! |
| \fn template<typename T> QList<T> qFindChildren(const QObject *obj, const QRegExp ®Exp) |
| \relates QObject |
| \overload qFindChildren() |
| |
| This function is equivalent to |
| \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a regExp). |
| |
| \note This function was provided as a workaround for MSVC 6 |
| which did not support member template functions. It is advised |
| to use the other form in new code. |
| |
| \sa QObject::findChildren() |
| */ |
| |
| /*! |
| \internal |
| */ |
| void qt_qFindChildren_helper(const QObject *parent, const QString &name, |
| const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options) |
| { |
| if (!parent || !list) |
| return; |
| const QObjectList &children = parent->children(); |
| QObject *obj; |
| for (int i = 0; i < children.size(); ++i) { |
| obj = children.at(i); |
| if (mo.cast(obj)) { |
| if (name.isNull() || obj->objectName() == name) |
| list->append(obj); |
| } |
| if (options & Qt::FindChildrenRecursively) |
| qt_qFindChildren_helper(obj, name, mo, list, options); |
| } |
| } |
| |
| #ifndef QT_NO_REGEXP |
| /*! |
| \internal |
| */ |
| void qt_qFindChildren_helper(const QObject *parent, const QRegExp &re, |
| const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options) |
| { |
| if (!parent || !list) |
| return; |
| const QObjectList &children = parent->children(); |
| QRegExp reCopy = re; |
| QObject *obj; |
| for (int i = 0; i < children.size(); ++i) { |
| obj = children.at(i); |
| if (mo.cast(obj) && reCopy.indexIn(obj->objectName()) != -1) |
| list->append(obj); |
| |
| if (options & Qt::FindChildrenRecursively) |
| qt_qFindChildren_helper(obj, re, mo, list, options); |
| } |
| } |
| #endif // QT_NO_REGEXP |
| |
| #if QT_CONFIG(regularexpression) |
| /*! |
| \internal |
| */ |
| void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re, |
| const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options) |
| { |
| if (!parent || !list) |
| return; |
| const QObjectList &children = parent->children(); |
| QObject *obj; |
| for (int i = 0; i < children.size(); ++i) { |
| obj = children.at(i); |
| if (mo.cast(obj)) { |
| QRegularExpressionMatch m = re.match(obj->objectName()); |
| if (m.hasMatch()) |
| list->append(obj); |
| } |
| if (options & Qt::FindChildrenRecursively) |
| qt_qFindChildren_helper(obj, re, mo, list, options); |
| } |
| } |
| #endif // QT_CONFIG(regularexpression) |
| |
| /*! |
| \internal |
| */ |
| QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options) |
| { |
| if (!parent) |
| return 0; |
| const QObjectList &children = parent->children(); |
| QObject *obj; |
| int i; |
| for (i = 0; i < children.size(); ++i) { |
| obj = children.at(i); |
| if (mo.cast(obj) && (name.isNull() || obj->objectName() == name)) |
| return obj; |
| } |
| if (options & Qt::FindChildrenRecursively) { |
| for (i = 0; i < children.size(); ++i) { |
| obj = qt_qFindChild_helper(children.at(i), name, mo, options); |
| if (obj) |
| return obj; |
| } |
| } |
| return 0; |
| } |
| |
| /*! |
| Makes the object a child of \a parent. |
| |
| \sa parent(), children() |
| */ |
| void QObject::setParent(QObject *parent) |
| { |
| Q_D(QObject); |
| Q_ASSERT(!d->isWidget); |
| d->setParent_helper(parent); |
| } |
| |
| void QObjectPrivate::deleteChildren() |
| { |
| Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()", "isDeletingChildren already set, did this function recurse?"); |
| isDeletingChildren = true; |
| // delete children objects |
| // don't use qDeleteAll as the destructor of the child might |
| // delete siblings |
| for (int i = 0; i < children.count(); ++i) { |
| currentChildBeingDeleted = children.at(i); |
| children[i] = 0; |
| delete currentChildBeingDeleted; |
| } |
| children.clear(); |
| currentChildBeingDeleted = 0; |
| isDeletingChildren = false; |
| } |
| |
| void QObjectPrivate::setParent_helper(QObject *o) |
| { |
| Q_Q(QObject); |
| Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself"); |
| #ifdef QT_DEBUG |
| const auto checkForParentChildLoops = qScopeGuard([&](){ |
| int depth = 0; |
| auto p = parent; |
| while (p) { |
| if (++depth == CheckForParentChildLoopsWarnDepth) { |
| qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; " |
| "this is undefined behavior", |
| q, q->metaObject()->className(), qPrintable(q->objectName())); |
| } |
| p = p->parent(); |
| } |
| }); |
| #endif |
| |
| if (o == parent) |
| return; |
| |
| if (parent) { |
| QObjectPrivate *parentD = parent->d_func(); |
| if (parentD->isDeletingChildren && wasDeleted |
| && parentD->currentChildBeingDeleted == q) { |
| // don't do anything since QObjectPrivate::deleteChildren() already |
| // cleared our entry in parentD->children. |
| } else { |
| const int index = parentD->children.indexOf(q); |
| if (parentD->isDeletingChildren) { |
| parentD->children[index] = 0; |
| } else { |
| parentD->children.removeAt(index); |
| if (sendChildEvents && parentD->receiveChildEvents) { |
| QChildEvent e(QEvent::ChildRemoved, q); |
| QCoreApplication::sendEvent(parent, &e); |
| } |
| } |
| } |
| } |
| parent = o; |
| if (parent) { |
| // object hierarchies are constrained to a single thread |
| if (threadData != parent->d_func()->threadData) { |
| qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread"); |
| parent = 0; |
| return; |
| } |
| parent->d_func()->children.append(q); |
| if(sendChildEvents && parent->d_func()->receiveChildEvents) { |
| if (!isWidget) { |
| QChildEvent e(QEvent::ChildAdded, q); |
| QCoreApplication::sendEvent(parent, &e); |
| } |
| } |
| } |
| if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged) |
| QAbstractDeclarativeData::parentChanged(declarativeData, q, o); |
| } |
| |
| /*! |
| \fn void QObject::installEventFilter(QObject *filterObj) |
| |
| Installs an event filter \a filterObj on this object. For example: |
| \snippet code/src_corelib_kernel_qobject.cpp 14 |
| |
| An event filter is an object that receives all events that are |
| sent to this object. The filter can either stop the event or |
| forward it to this object. The event filter \a filterObj receives |
| events via its eventFilter() function. The eventFilter() function |
| must return true if the event should be filtered, (i.e. stopped); |
| otherwise it must return false. |
| |
| If multiple event filters are installed on a single object, the |
| filter that was installed last is activated first. |
| |
| Here's a \c KeyPressEater class that eats the key presses of its |
| monitored objects: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 15 |
| |
| And here's how to install it on two widgets: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 16 |
| |
| The QShortcut class, for example, uses this technique to intercept |
| shortcut key presses. |
| |
| \warning If you delete the receiver object in your eventFilter() |
| function, be sure to return true. If you return false, Qt sends |
| the event to the deleted object and the program will crash. |
| |
| Note that the filtering object must be in the same thread as this |
| object. If \a filterObj is in a different thread, this function does |
| nothing. If either \a filterObj or this object are moved to a different |
| thread after calling this function, the event filter will not be |
| called until both objects have the same thread affinity again (it |
| is \e not removed). |
| |
| \sa removeEventFilter(), eventFilter(), event() |
| */ |
| |
| void QObject::installEventFilter(QObject *obj) |
| { |
| Q_D(QObject); |
| if (!obj) |
| return; |
| if (d->threadData != obj->d_func()->threadData) { |
| qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread."); |
| return; |
| } |
| |
| if (!d->extraData) |
| d->extraData = new QObjectPrivate::ExtraData; |
| |
| // clean up unused items in the list |
| d->extraData->eventFilters.removeAll((QObject*)0); |
| d->extraData->eventFilters.removeAll(obj); |
| d->extraData->eventFilters.prepend(obj); |
| } |
| |
| /*! |
| Removes an event filter object \a obj from this object. The |
| request is ignored if such an event filter has not been installed. |
| |
| All event filters for this object are automatically removed when |
| this object is destroyed. |
| |
| It is always safe to remove an event filter, even during event |
| filter activation (i.e. from the eventFilter() function). |
| |
| \sa installEventFilter(), eventFilter(), event() |
| */ |
| |
| void QObject::removeEventFilter(QObject *obj) |
| { |
| Q_D(QObject); |
| if (d->extraData) { |
| for (int i = 0; i < d->extraData->eventFilters.count(); ++i) { |
| if (d->extraData->eventFilters.at(i) == obj) |
| d->extraData->eventFilters[i] = 0; |
| } |
| } |
| } |
| |
| |
| /*! |
| \fn void QObject::destroyed(QObject *obj) |
| |
| This signal is emitted immediately before the object \a obj is |
| destroyed, after any instances of QPointer have been notified, |
| and cannot be blocked. |
| |
| All the objects's children are destroyed immediately after this |
| signal is emitted. |
| |
| \sa deleteLater(), QPointer |
| */ |
| |
| /*! |
| \threadsafe |
| |
| Schedules this object for deletion. |
| |
| The object will be deleted when control returns to the event |
| loop. If the event loop is not running when this function is |
| called (e.g. deleteLater() is called on an object before |
| QCoreApplication::exec()), the object will be deleted once the |
| event loop is started. If deleteLater() is called after the main event loop |
| has stopped, the object will not be deleted. |
| Since Qt 4.8, if deleteLater() is called on an object that lives in a |
| thread with no running event loop, the object will be destroyed when the |
| thread finishes. |
| |
| Note that entering and leaving a new event loop (e.g., by opening a modal |
| dialog) will \e not perform the deferred deletion; for the object to be |
| deleted, the control must return to the event loop from which deleteLater() |
| was called. This does not apply to objects deleted while a previous, nested |
| event loop was still running: the Qt event loop will delete those objects |
| as soon as the new nested event loop starts. |
| |
| \b{Note:} It is safe to call this function more than once; when the |
| first deferred deletion event is delivered, any pending events for the |
| object are removed from the event queue. |
| |
| \sa destroyed(), QPointer |
| */ |
| void QObject::deleteLater() |
| { |
| QCoreApplication::postEvent(this, new QDeferredDeleteEvent()); |
| } |
| |
| /*! |
| \fn QString QObject::tr(const char *sourceText, const char *disambiguation, int n) |
| \reentrant |
| |
| Returns a translated version of \a sourceText, optionally based on a |
| \a disambiguation string and value of \a n for strings containing plurals; |
| otherwise returns QString::fromUtf8(\a sourceText) if no appropriate |
| translated string is available. |
| |
| Example: |
| \snippet ../widgets/mainwindows/sdi/mainwindow.cpp implicit tr context |
| \dots |
| |
| If the same \a sourceText is used in different roles within the |
| same context, an additional identifying string may be passed in |
| \a disambiguation (\nullptr by default). In Qt 4.4 and earlier, this was |
| the preferred way to pass comments to translators. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 17 |
| \dots |
| |
| See \l{Writing Source Code for Translation} for a detailed description of |
| Qt's translation mechanisms in general, and the |
| \l{Writing Source Code for Translation#Disambiguation}{Disambiguation} |
| section for information on disambiguation. |
| |
| \warning This method is reentrant only if all translators are |
| installed \e before calling this method. Installing or removing |
| translators while performing translations is not supported. Doing |
| so will probably result in crashes or other undesirable behavior. |
| |
| \sa QCoreApplication::translate(), {Internationalization with Qt} |
| */ |
| |
| /*! |
| \fn QString QObject::trUtf8(const char *sourceText, const char *disambiguation, int n) |
| \reentrant |
| \obsolete |
| |
| Returns a translated version of \a sourceText, or |
| QString::fromUtf8(\a sourceText) if there is no appropriate |
| version. It is otherwise identical to tr(\a sourceText, \a |
| disambiguation, \a n). |
| |
| \warning This method is reentrant only if all translators are |
| installed \e before calling this method. Installing or removing |
| translators while performing translations is not supported. Doing |
| so will probably result in crashes or other undesirable behavior. |
| |
| \warning For portability reasons, we recommend that you use |
| escape sequences for specifying non-ASCII characters in string |
| literals to trUtf8(). For example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 20 |
| |
| \sa tr(), QCoreApplication::translate(), {Internationalization with Qt} |
| */ |
| |
| |
| |
| |
| /***************************************************************************** |
| Signals and slots |
| *****************************************************************************/ |
| |
| |
| const char *qFlagLocation(const char *method) |
| { |
| QThreadData *currentThreadData = QThreadData::current(false); |
| if (currentThreadData != 0) |
| currentThreadData->flaggedSignatures.store(method); |
| return method; |
| } |
| |
| static int extract_code(const char *member) |
| { |
| // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE |
| return (((int)(*member) - '0') & 0x3); |
| } |
| |
| static const char * extract_location(const char *member) |
| { |
| if (QThreadData::current()->flaggedSignatures.contains(member)) { |
| // signature includes location information after the first null-terminator |
| const char *location = member + qstrlen(member) + 1; |
| if (*location != '\0') |
| return location; |
| } |
| return 0; |
| } |
| |
| static bool check_signal_macro(const QObject *sender, const char *signal, |
| const char *func, const char *op) |
| { |
| int sigcode = extract_code(signal); |
| if (sigcode != QSIGNAL_CODE) { |
| if (sigcode == QSLOT_CODE) |
| qWarning("QObject::%s: Attempt to %s non-signal %s::%s", |
| func, op, sender->metaObject()->className(), signal+1); |
| else |
| qWarning("QObject::%s: Use the SIGNAL macro to %s %s::%s", |
| func, op, sender->metaObject()->className(), signal); |
| return false; |
| } |
| return true; |
| } |
| |
| static bool check_method_code(int code, const QObject *object, |
| const char *method, const char *func) |
| { |
| if (code != QSLOT_CODE && code != QSIGNAL_CODE) { |
| qWarning("QObject::%s: Use the SLOT or SIGNAL macro to " |
| "%s %s::%s", func, func, object->metaObject()->className(), method); |
| return false; |
| } |
| return true; |
| } |
| |
| static void err_method_notfound(const QObject *object, |
| const char *method, const char *func) |
| { |
| const char *type = "method"; |
| switch (extract_code(method)) { |
| case QSLOT_CODE: type = "slot"; break; |
| case QSIGNAL_CODE: type = "signal"; break; |
| } |
| const char *loc = extract_location(method); |
| if (strchr(method,')') == 0) // common typing mistake |
| qWarning("QObject::%s: Parentheses expected, %s %s::%s%s%s", |
| func, type, object->metaObject()->className(), method+1, |
| loc ? " in ": "", loc ? loc : ""); |
| else |
| qWarning("QObject::%s: No such %s %s::%s%s%s", |
| func, type, object->metaObject()->className(), method+1, |
| loc ? " in ": "", loc ? loc : ""); |
| |
| } |
| |
| |
| static void err_info_about_objects(const char * func, |
| const QObject * sender, |
| const QObject * receiver) |
| { |
| QString a = sender ? sender->objectName() : QString(); |
| QString b = receiver ? receiver->objectName() : QString(); |
| if (!a.isEmpty()) |
| qWarning("QObject::%s: (sender name: '%s')", func, a.toLocal8Bit().data()); |
| if (!b.isEmpty()) |
| qWarning("QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data()); |
| } |
| |
| /*! |
| Returns a pointer to the object that sent the signal, if called in |
| a slot activated by a signal; otherwise it returns \nullptr. The pointer |
| is valid only during the execution of the slot that calls this |
| function from this object's thread context. |
| |
| The pointer returned by this function becomes invalid if the |
| sender is destroyed, or if the slot is disconnected from the |
| sender's signal. |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, getting access to the sender might be useful |
| when many signals are connected to a single slot. |
| |
| \warning As mentioned above, the return value of this function is |
| not valid when the slot is called via a Qt::DirectConnection from |
| a thread different from this object's thread. Do not use this |
| function in this type of scenario. |
| |
| \sa senderSignalIndex() |
| */ |
| |
| QObject *QObject::sender() const |
| { |
| Q_D(const QObject); |
| |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); |
| if (!cd || !cd->currentSender) |
| return nullptr; |
| |
| for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) { |
| if (c->sender == cd->currentSender->sender) |
| return cd->currentSender->sender; |
| } |
| |
| return 0; |
| } |
| |
| /*! |
| \since 4.8 |
| |
| Returns the meta-method index of the signal that called the currently |
| executing slot, which is a member of the class returned by sender(). |
| If called outside of a slot activated by a signal, -1 is returned. |
| |
| For signals with default parameters, this function will always return |
| the index with all parameters, regardless of which was used with |
| connect(). For example, the signal \c {destroyed(QObject *obj = \nullptr)} |
| will have two different indexes (with and without the parameter), but |
| this function will always return the index with a parameter. This does |
| not apply when overloading signals with different parameters. |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, getting access to the signal index might be useful |
| when many signals are connected to a single slot. |
| |
| \warning The return value of this function is not valid when the slot |
| is called via a Qt::DirectConnection from a thread different from this |
| object's thread. Do not use this function in this type of scenario. |
| |
| \sa sender(), QMetaObject::indexOfSignal(), QMetaObject::method() |
| */ |
| |
| int QObject::senderSignalIndex() const |
| { |
| Q_D(const QObject); |
| |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); |
| if (!cd || !cd->currentSender) |
| return -1; |
| |
| for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) { |
| if (c->sender == cd->currentSender->sender) { |
| // Convert from signal range to method range |
| return QMetaObjectPrivate::signal(c->sender->metaObject(), cd->currentSender->signal).methodIndex(); |
| } |
| } |
| |
| return -1; |
| } |
| |
| /*! |
| Returns the number of receivers connected to the \a signal. |
| |
| Since both slots and signals can be used as receivers for signals, |
| and the same connections can be made many times, the number of |
| receivers is the same as the number of connections made from this |
| signal. |
| |
| When calling this function, you can use the \c SIGNAL() macro to |
| pass a specific signal: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 21 |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, it might be useful when you need to perform |
| expensive initialization only if something is connected to a |
| signal. |
| |
| \sa isSignalConnected() |
| */ |
| |
| int QObject::receivers(const char *signal) const |
| { |
| Q_D(const QObject); |
| int receivers = 0; |
| if (signal) { |
| QByteArray signal_name = QMetaObject::normalizedSignature(signal); |
| signal = signal_name; |
| #ifndef QT_NO_DEBUG |
| if (!check_signal_macro(this, signal, "receivers", "bind")) |
| return 0; |
| #endif |
| signal++; // skip code |
| int signal_index = d->signalIndex(signal); |
| if (signal_index < 0) { |
| #ifndef QT_NO_DEBUG |
| err_method_notfound(this, signal-1, "receivers"); |
| #endif |
| return 0; |
| } |
| |
| if (!d->isSignalConnected(signal_index)) |
| return receivers; |
| |
| if (d->declarativeData && QAbstractDeclarativeData::receivers) { |
| receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this, |
| signal_index); |
| } |
| |
| QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| if (cd && signal_index < cd->signalVectorCount()) { |
| const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); |
| while (c) { |
| receivers += c->receiver.loadRelaxed() ? 1 : 0; |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| } |
| } |
| return receivers; |
| } |
| |
| /*! |
| \since 5.0 |
| Returns \c true if the \a signal is connected to at least one receiver, |
| otherwise returns \c false. |
| |
| \a signal must be a signal member of this object, otherwise the behaviour |
| is undefined. |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 49 |
| |
| As the code snippet above illustrates, you can use this function |
| to avoid emitting a signal that nobody listens to. |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, it might be useful when you need to perform |
| expensive initialization only if something is connected to a |
| signal. |
| */ |
| bool QObject::isSignalConnected(const QMetaMethod &signal) const |
| { |
| Q_D(const QObject); |
| if (!signal.mobj) |
| return false; |
| |
| Q_ASSERT_X(signal.mobj->cast(this) && signal.methodType() == QMetaMethod::Signal, |
| "QObject::isSignalConnected" , "the parameter must be a signal member of the object"); |
| uint signalIndex = (signal.handle - QMetaObjectPrivate::get(signal.mobj)->methodData)/5; |
| |
| if (signal.mobj->d.data[signal.handle + 4] & MethodCloned) |
| signalIndex = QMetaObjectPrivate::originalClone(signal.mobj, signalIndex); |
| |
| signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj); |
| |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| return d->isSignalConnected(signalIndex, true); |
| } |
| |
| /*! |
| \internal |
| |
| This helper function calculates signal and method index for the given |
| member in the specified class. |
| |
| \list |
| \li If member.mobj is \nullptr then both signalIndex and methodIndex are set to -1. |
| |
| \li If specified member is not a member of obj instance class (or one of |
| its parent classes) then both signalIndex and methodIndex are set to -1. |
| \endlist |
| |
| This function is used by QObject::connect and QObject::disconnect which |
| are working with QMetaMethod. |
| |
| \a signalIndex is set to the signal index of member. If the member |
| specified is not signal this variable is set to -1. |
| |
| \a methodIndex is set to the method index of the member. If the |
| member is not a method of the object specified by the \a obj argument this |
| variable is set to -1. |
| */ |
| void QMetaObjectPrivate::memberIndexes(const QObject *obj, |
| const QMetaMethod &member, |
| int *signalIndex, int *methodIndex) |
| { |
| *signalIndex = -1; |
| *methodIndex = -1; |
| if (!obj || !member.mobj) |
| return; |
| const QMetaObject *m = obj->metaObject(); |
| // Check that member is member of obj class |
| while (m != 0 && m != member.mobj) |
| m = m->d.superdata; |
| if (!m) |
| return; |
| *signalIndex = *methodIndex = (member.handle - get(member.mobj)->methodData)/5; |
| |
| int signalOffset; |
| int methodOffset; |
| computeOffsets(m, &signalOffset, &methodOffset); |
| |
| *methodIndex += methodOffset; |
| if (member.methodType() == QMetaMethod::Signal) { |
| *signalIndex = originalClone(m, *signalIndex); |
| *signalIndex += signalOffset; |
| } else { |
| *signalIndex = -1; |
| } |
| } |
| |
| #ifndef QT_NO_DEBUG |
| static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal, |
| const QMetaObject *receiver, const QMetaMethod &method) |
| { |
| if (signal.attributes() & QMetaMethod::Compatibility) { |
| if (!(method.attributes() & QMetaMethod::Compatibility)) |
| qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", |
| sender->className(), signal.methodSignature().constData()); |
| } else if ((method.attributes() & QMetaMethod::Compatibility) && |
| method.methodType() == QMetaMethod::Signal) { |
| qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", |
| sender->className(), signal.methodSignature().constData(), |
| receiver->className(), method.methodSignature().constData()); |
| } |
| } |
| #endif |
| |
| /*! |
| \threadsafe |
| |
| Creates a connection of the given \a type from the \a signal in |
| the \a sender object to the \a method in the \a receiver object. |
| Returns a handle to the connection that can be used to disconnect |
| it later. |
| |
| You must use the \c SIGNAL() and \c SLOT() macros when specifying |
| the \a signal and the \a method, for example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 22 |
| |
| This example ensures that the label always displays the current |
| scroll bar value. Note that the signal and slots parameters must not |
| contain any variable names, only the type. E.g. the following would |
| not work and return false: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 23 |
| |
| A signal can also be connected to another signal: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 24 |
| |
| In this example, the \c MyWidget constructor relays a signal from |
| a private member variable, and makes it available under a name |
| that relates to \c MyWidget. |
| |
| A signal can be connected to many slots and signals. Many signals |
| can be connected to one slot. |
| |
| If a signal is connected to several slots, the slots are activated |
| in the same order in which the connections were made, when the |
| signal is emitted. |
| |
| The function returns a QMetaObject::Connection that represents |
| a handle to a connection if it successfully |
| connects the signal to the slot. The connection handle will be invalid |
| if it cannot create the connection, for example, if QObject is unable |
| to verify the existence of either \a signal or \a method, or if their |
| signatures aren't compatible. |
| You can check if the handle is valid by casting it to a bool. |
| |
| By default, a signal is emitted for every connection you make; |
| two signals are emitted for duplicate connections. You can break |
| all of these connections with a single disconnect() call. |
| If you pass the Qt::UniqueConnection \a type, the connection will only |
| be made if it is not a duplicate. If there is already a duplicate |
| (exact same signal to the exact same slot on the same objects), |
| the connection will fail and connect will return an invalid QMetaObject::Connection. |
| |
| \note Qt::UniqueConnections do not work for lambdas, non-member functions |
| and functors; they only apply to connecting to member functions. |
| |
| The optional \a type parameter describes the type of connection |
| to establish. In particular, it determines whether a particular |
| signal is delivered to a slot immediately or queued for delivery |
| at a later time. If the signal is queued, the parameters must be |
| of types that are known to Qt's meta-object system, because Qt |
| needs to copy the arguments to store them in an event behind the |
| scenes. If you try to use a queued connection and get the error |
| message |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 25 |
| |
| call qRegisterMetaType() to register the data type before you |
| establish the connection. |
| |
| \sa disconnect(), sender(), qRegisterMetaType(), Q_DECLARE_METATYPE(), |
| {Differences between String-Based and Functor-Based Connections} |
| */ |
| QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, |
| const QObject *receiver, const char *method, |
| Qt::ConnectionType type) |
| { |
| if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) { |
| qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", |
| sender ? sender->metaObject()->className() : "(nullptr)", |
| (signal && *signal) ? signal+1 : "(nullptr)", |
| receiver ? receiver->metaObject()->className() : "(nullptr)", |
| (method && *method) ? method+1 : "(nullptr)"); |
| return QMetaObject::Connection(0); |
| } |
| QByteArray tmp_signal_name; |
| |
| if (!check_signal_macro(sender, signal, "connect", "bind")) |
| return QMetaObject::Connection(0); |
| const QMetaObject *smeta = sender->metaObject(); |
| const char *signal_arg = signal; |
| ++signal; //skip code |
| QArgumentTypeArray signalTypes; |
| Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7); |
| QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); |
| int signal_index = QMetaObjectPrivate::indexOfSignalRelative( |
| &smeta, signalName, signalTypes.size(), signalTypes.constData()); |
| if (signal_index < 0) { |
| // check for normalized signatures |
| tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); |
| signal = tmp_signal_name.constData() + 1; |
| |
| signalTypes.clear(); |
| signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); |
| smeta = sender->metaObject(); |
| signal_index = QMetaObjectPrivate::indexOfSignalRelative( |
| &smeta, signalName, signalTypes.size(), signalTypes.constData()); |
| } |
| if (signal_index < 0) { |
| err_method_notfound(sender, signal_arg, "connect"); |
| err_info_about_objects("connect", sender, receiver); |
| return QMetaObject::Connection(0); |
| } |
| signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); |
| signal_index += QMetaObjectPrivate::signalOffset(smeta); |
| |
| QByteArray tmp_method_name; |
| int membcode = extract_code(method); |
| |
| if (!check_method_code(membcode, receiver, method, "connect")) |
| return QMetaObject::Connection(0); |
| const char *method_arg = method; |
| ++method; // skip code |
| |
| QArgumentTypeArray methodTypes; |
| QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); |
| const QMetaObject *rmeta = receiver->metaObject(); |
| int method_index_relative = -1; |
| Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7); |
| switch (membcode) { |
| case QSLOT_CODE: |
| method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( |
| &rmeta, methodName, methodTypes.size(), methodTypes.constData()); |
| break; |
| case QSIGNAL_CODE: |
| method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( |
| &rmeta, methodName, methodTypes.size(), methodTypes.constData()); |
| break; |
| } |
| if (method_index_relative < 0) { |
| // check for normalized methods |
| tmp_method_name = QMetaObject::normalizedSignature(method); |
| method = tmp_method_name.constData(); |
| |
| methodTypes.clear(); |
| methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); |
| // rmeta may have been modified above |
| rmeta = receiver->metaObject(); |
| switch (membcode) { |
| case QSLOT_CODE: |
| method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( |
| &rmeta, methodName, methodTypes.size(), methodTypes.constData()); |
| break; |
| case QSIGNAL_CODE: |
| method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( |
| &rmeta, methodName, methodTypes.size(), methodTypes.constData()); |
| break; |
| } |
| } |
| |
| if (method_index_relative < 0) { |
| err_method_notfound(receiver, method_arg, "connect"); |
| err_info_about_objects("connect", sender, receiver); |
| return QMetaObject::Connection(0); |
| } |
| |
| if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(), |
| methodTypes.size(), methodTypes.constData())) { |
| qWarning("QObject::connect: Incompatible sender/receiver arguments" |
| "\n %s::%s --> %s::%s", |
| sender->metaObject()->className(), signal, |
| receiver->metaObject()->className(), method); |
| return QMetaObject::Connection(0); |
| } |
| |
| int *types = 0; |
| if ((type == Qt::QueuedConnection) |
| && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) { |
| return QMetaObject::Connection(0); |
| } |
| |
| #ifndef QT_NO_DEBUG |
| QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index); |
| QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset()); |
| check_and_warn_compat(smeta, smethod, rmeta, rmethod); |
| #endif |
| QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect( |
| sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types)); |
| return handle; |
| } |
| |
| /*! |
| \since 4.8 |
| |
| Creates a connection of the given \a type from the \a signal in |
| the \a sender object to the \a method in the \a receiver object. |
| Returns a handle to the connection that can be used to disconnect |
| it later. |
| |
| The Connection handle will be invalid if it cannot create the |
| connection, for example, the parameters were invalid. |
| You can check if the QMetaObject::Connection is valid by casting it to a bool. |
| |
| This function works in the same way as |
| \c {connect(const QObject *sender, const char *signal, |
| const QObject *receiver, const char *method, |
| Qt::ConnectionType type)} |
| but it uses QMetaMethod to specify signal and method. |
| |
| \sa connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) |
| */ |
| QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, |
| const QObject *receiver, const QMetaMethod &method, |
| Qt::ConnectionType type) |
| { |
| if (sender == nullptr |
| || receiver == nullptr |
| || signal.methodType() != QMetaMethod::Signal |
| || method.methodType() == QMetaMethod::Constructor) { |
| qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", |
| sender ? sender->metaObject()->className() : "(nullptr)", |
| signal.methodSignature().constData(), |
| receiver ? receiver->metaObject()->className() : "(nullptr)", |
| method.methodSignature().constData() ); |
| return QMetaObject::Connection(0); |
| } |
| |
| int signal_index; |
| int method_index; |
| { |
| int dummy; |
| QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); |
| QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); |
| } |
| |
| const QMetaObject *smeta = sender->metaObject(); |
| const QMetaObject *rmeta = receiver->metaObject(); |
| if (signal_index == -1) { |
| qWarning("QObject::connect: Can't find signal %s on instance of class %s", |
| signal.methodSignature().constData(), smeta->className()); |
| return QMetaObject::Connection(0); |
| } |
| if (method_index == -1) { |
| qWarning("QObject::connect: Can't find method %s on instance of class %s", |
| method.methodSignature().constData(), rmeta->className()); |
| return QMetaObject::Connection(0); |
| } |
| |
| if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(), method.methodSignature().constData())) { |
| qWarning("QObject::connect: Incompatible sender/receiver arguments" |
| "\n %s::%s --> %s::%s", |
| smeta->className(), signal.methodSignature().constData(), |
| rmeta->className(), method.methodSignature().constData()); |
| return QMetaObject::Connection(0); |
| } |
| |
| int *types = 0; |
| if ((type == Qt::QueuedConnection) |
| && !(types = queuedConnectionTypes(signal.parameterTypes()))) |
| return QMetaObject::Connection(0); |
| |
| #ifndef QT_NO_DEBUG |
| check_and_warn_compat(smeta, signal, rmeta, method); |
| #endif |
| QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect( |
| sender, signal_index, signal.enclosingMetaObject(), receiver, method_index, 0, type, types)); |
| return handle; |
| } |
| |
| /*! |
| \fn bool QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const |
| \overload connect() |
| \threadsafe |
| |
| Connects \a signal from the \a sender object to this object's \a |
| method. |
| |
| Equivalent to connect(\a sender, \a signal, \c this, \a method, \a type). |
| |
| Every connection you make emits a signal, so duplicate connections emit |
| two signals. You can break a connection using disconnect(). |
| |
| \sa disconnect() |
| */ |
| |
| /*! |
| \threadsafe |
| |
| Disconnects \a signal in object \a sender from \a method in object |
| \a receiver. Returns \c true if the connection is successfully broken; |
| otherwise returns \c false. |
| |
| A signal-slot connection is removed when either of the objects |
| involved are destroyed. |
| |
| disconnect() is typically used in three ways, as the following |
| examples demonstrate. |
| \list 1 |
| \li Disconnect everything connected to an object's signals: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 26 |
| |
| equivalent to the non-static overloaded function |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 27 |
| |
| \li Disconnect everything connected to a specific signal: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 28 |
| |
| equivalent to the non-static overloaded function |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 29 |
| |
| \li Disconnect a specific receiver: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 30 |
| |
| equivalent to the non-static overloaded function |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 31 |
| |
| \endlist |
| |
| \nullptr may be used as a wildcard, meaning "any signal", "any receiving |
| object", or "any slot in the receiving object", respectively. |
| |
| The \a sender may never be \nullptr. (You cannot disconnect signals |
| from more than one object in a single call.) |
| |
| If \a signal is \nullptr, it disconnects \a receiver and \a method from |
| any signal. If not, only the specified signal is disconnected. |
| |
| If \a receiver is \nullptr, it disconnects anything connected to \a |
| signal. If not, slots in objects other than \a receiver are not |
| disconnected. |
| |
| If \a method is \nullptr, it disconnects anything that is connected to \a |
| receiver. If not, only slots named \a method will be disconnected, |
| and all other slots are left alone. The \a method must be \nullptr |
| if \a receiver is left out, so you cannot disconnect a |
| specifically-named slot on all objects. |
| |
| \sa connect() |
| */ |
| bool QObject::disconnect(const QObject *sender, const char *signal, |
| const QObject *receiver, const char *method) |
| { |
| if (sender == nullptr || (receiver == nullptr && method != nullptr)) { |
| qWarning("QObject::disconnect: Unexpected nullptr parameter"); |
| return false; |
| } |
| |
| const char *signal_arg = signal; |
| QByteArray signal_name; |
| bool signal_found = false; |
| if (signal) { |
| QT_TRY { |
| signal_name = QMetaObject::normalizedSignature(signal); |
| signal = signal_name.constData(); |
| } QT_CATCH (const std::bad_alloc &) { |
| // if the signal is already normalized, we can continue. |
| if (sender->metaObject()->indexOfSignal(signal + 1) == -1) |
| QT_RETHROW; |
| } |
| |
| if (!check_signal_macro(sender, signal, "disconnect", "unbind")) |
| return false; |
| signal++; // skip code |
| } |
| |
| QByteArray method_name; |
| const char *method_arg = method; |
| int membcode = -1; |
| bool method_found = false; |
| if (method) { |
| QT_TRY { |
| method_name = QMetaObject::normalizedSignature(method); |
| method = method_name.constData(); |
| } QT_CATCH(const std::bad_alloc &) { |
| // if the method is already normalized, we can continue. |
| if (receiver->metaObject()->indexOfMethod(method + 1) == -1) |
| QT_RETHROW; |
| } |
| |
| membcode = extract_code(method); |
| if (!check_method_code(membcode, receiver, method, "disconnect")) |
| return false; |
| method++; // skip code |
| } |
| |
| /* We now iterate through all the sender's and receiver's meta |
| * objects in order to also disconnect possibly shadowed signals |
| * and slots with the same signature. |
| */ |
| bool res = false; |
| const QMetaObject *smeta = sender->metaObject(); |
| QByteArray signalName; |
| QArgumentTypeArray signalTypes; |
| Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7); |
| if (signal) |
| signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); |
| QByteArray methodName; |
| QArgumentTypeArray methodTypes; |
| Q_ASSERT(!receiver || QMetaObjectPrivate::get(receiver->metaObject())->revision >= 7); |
| if (method) |
| methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); |
| do { |
| int signal_index = -1; |
| if (signal) { |
| signal_index = QMetaObjectPrivate::indexOfSignalRelative( |
| &smeta, signalName, signalTypes.size(), signalTypes.constData()); |
| if (signal_index < 0) |
| break; |
| signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); |
| signal_index += QMetaObjectPrivate::signalOffset(smeta); |
| signal_found = true; |
| } |
| |
| if (!method) { |
| res |= QMetaObjectPrivate::disconnect(sender, signal_index, smeta, receiver, -1, 0); |
| } else { |
| const QMetaObject *rmeta = receiver->metaObject(); |
| do { |
| int method_index = QMetaObjectPrivate::indexOfMethod( |
| rmeta, methodName, methodTypes.size(), methodTypes.constData()); |
| if (method_index >= 0) |
| while (method_index < rmeta->methodOffset()) |
| rmeta = rmeta->superClass(); |
| if (method_index < 0) |
| break; |
| res |= QMetaObjectPrivate::disconnect(sender, signal_index, smeta, receiver, method_index, 0); |
| method_found = true; |
| } while ((rmeta = rmeta->superClass())); |
| } |
| } while (signal && (smeta = smeta->superClass())); |
| |
| if (signal && !signal_found) { |
| err_method_notfound(sender, signal_arg, "disconnect"); |
| err_info_about_objects("disconnect", sender, receiver); |
| } else if (method && !method_found) { |
| err_method_notfound(receiver, method_arg, "disconnect"); |
| err_info_about_objects("disconnect", sender, receiver); |
| } |
| if (res) { |
| if (!signal) |
| const_cast<QObject*>(sender)->disconnectNotify(QMetaMethod()); |
| } |
| return res; |
| } |
| |
| /*! |
| \since 4.8 |
| |
| Disconnects \a signal in object \a sender from \a method in object |
| \a receiver. Returns \c true if the connection is successfully broken; |
| otherwise returns \c false. |
| |
| This function provides the same possibilities like |
| \c {disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) } |
| but uses QMetaMethod to represent the signal and the method to be disconnected. |
| |
| Additionally this function returnsfalse and no signals and slots disconnected |
| if: |
| \list 1 |
| |
| \li \a signal is not a member of sender class or one of its parent classes. |
| |
| \li \a method is not a member of receiver class or one of its parent classes. |
| |
| \li \a signal instance represents not a signal. |
| |
| \endlist |
| |
| QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object". |
| In the same way \nullptr can be used for \a receiver in the meaning "any receiving object". |
| In this case method should also be QMetaMethod(). \a sender parameter should be never \nullptr. |
| |
| \sa disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) |
| */ |
| bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, |
| const QObject *receiver, const QMetaMethod &method) |
| { |
| if (sender == nullptr || (receiver == nullptr && method.mobj != nullptr)) { |
| qWarning("QObject::disconnect: Unexpected nullptr parameter"); |
| return false; |
| } |
| if (signal.mobj) { |
| if(signal.methodType() != QMetaMethod::Signal) { |
| qWarning("QObject::%s: Attempt to %s non-signal %s::%s", |
| "disconnect","unbind", |
| sender->metaObject()->className(), signal.methodSignature().constData()); |
| return false; |
| } |
| } |
| if (method.mobj) { |
| if(method.methodType() == QMetaMethod::Constructor) { |
| qWarning("QObject::disconect: cannot use constructor as argument %s::%s", |
| receiver->metaObject()->className(), method.methodSignature().constData()); |
| return false; |
| } |
| } |
| |
| // Reconstructing SIGNAL() macro result for signal.methodSignature() string |
| QByteArray signalSignature; |
| if (signal.mobj) { |
| signalSignature.reserve(signal.methodSignature().size()+1); |
| signalSignature.append((char)(QSIGNAL_CODE + '0')); |
| signalSignature.append(signal.methodSignature()); |
| } |
| |
| int signal_index; |
| int method_index; |
| { |
| int dummy; |
| QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); |
| QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); |
| } |
| // If we are here sender is not nullptr. If signal is not nullptr while signal_index |
| // is -1 then this signal is not a member of sender. |
| if (signal.mobj && signal_index == -1) { |
| qWarning("QObject::disconect: signal %s not found on class %s", |
| signal.methodSignature().constData(), sender->metaObject()->className()); |
| return false; |
| } |
| // If this condition is true then method is not a member of receeiver. |
| if (receiver && method.mobj && method_index == -1) { |
| qWarning("QObject::disconect: method %s not found on class %s", |
| method.methodSignature().constData(), receiver->metaObject()->className()); |
| return false; |
| } |
| |
| if (!QMetaObjectPrivate::disconnect(sender, signal_index, signal.mobj, receiver, method_index, 0)) |
| return false; |
| |
| if (!signal.isValid()) { |
| // The signal is a wildcard, meaning all signals were disconnected. |
| // QMetaObjectPrivate::disconnect() doesn't call disconnectNotify() |
| // per connection in this case. Call it once now, with an invalid |
| // QMetaMethod as argument, as documented. |
| const_cast<QObject*>(sender)->disconnectNotify(signal); |
| } |
| return true; |
| } |
| |
| /*! |
| \threadsafe |
| |
| \fn bool QObject::disconnect(const char *signal, const QObject *receiver, const char *method) const |
| \overload disconnect() |
| |
| Disconnects \a signal from \a method of \a receiver. |
| |
| A signal-slot connection is removed when either of the objects |
| involved are destroyed. |
| */ |
| |
| /*! |
| \fn bool QObject::disconnect(const QObject *receiver, const char *method) const |
| \overload disconnect() |
| |
| Disconnects all signals in this object from \a receiver's \a |
| method. |
| |
| A signal-slot connection is removed when either of the objects |
| involved are destroyed. |
| */ |
| |
| |
| /*! |
| \since 5.0 |
| |
| This virtual function is called when something has been connected |
| to \a signal in this object. |
| |
| If you want to compare \a signal with a specific signal, you can |
| use QMetaMethod::fromSignal() as follows: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 32 |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, it might be useful when you need to perform |
| expensive initialization only if something is connected to a |
| signal. |
| |
| \warning This function is called from the thread which performs the |
| connection, which may be a different thread from the thread in |
| which this object lives. |
| |
| \sa connect(), disconnectNotify() |
| */ |
| |
| void QObject::connectNotify(const QMetaMethod &signal) |
| { |
| Q_UNUSED(signal); |
| } |
| |
| /*! |
| \since 5.0 |
| |
| This virtual function is called when something has been |
| disconnected from \a signal in this object. |
| |
| See connectNotify() for an example of how to compare |
| \a signal with a specific signal. |
| |
| If all signals were disconnected from this object (e.g., the |
| signal argument to disconnect() was \nullptr), disconnectNotify() |
| is only called once, and the \a signal will be an invalid |
| QMetaMethod (QMetaMethod::isValid() returns \c false). |
| |
| \warning This function violates the object-oriented principle of |
| modularity. However, it might be useful for optimizing access to |
| expensive resources. |
| |
| \warning This function is called from the thread which performs the |
| disconnection, which may be a different thread from the thread in |
| which this object lives. This function may also be called with a QObject |
| internal mutex locked. It is therefore not allowed to re-enter any |
| of any QObject functions from your reimplementation and if you lock |
| a mutex in your reimplementation, make sure that you don't call QObject |
| functions with that mutex held in other places or it will result in |
| a deadlock. |
| |
| \sa disconnect(), connectNotify() |
| */ |
| |
| void QObject::disconnectNotify(const QMetaMethod &signal) |
| { |
| Q_UNUSED(signal); |
| } |
| |
| /* |
| \internal |
| convert a signal index from the method range to the signal range |
| */ |
| static int methodIndexToSignalIndex(const QMetaObject **base, int signal_index) |
| { |
| if (signal_index < 0) |
| return signal_index; |
| const QMetaObject *metaObject = *base; |
| while (metaObject && metaObject->methodOffset() > signal_index) |
| metaObject = metaObject->superClass(); |
| |
| if (metaObject) { |
| int signalOffset, methodOffset; |
| computeOffsets(metaObject, &signalOffset, &methodOffset); |
| if (signal_index < metaObject->methodCount()) |
| signal_index = QMetaObjectPrivate::originalClone(metaObject, signal_index - methodOffset) + signalOffset; |
| else |
| signal_index = signal_index - methodOffset + signalOffset; |
| *base = metaObject; |
| } |
| return signal_index; |
| } |
| |
| /*! |
| \internal |
| \a types is a 0-terminated vector of meta types for queued |
| connections. |
| |
| if \a signal_index is -1, then we effectively connect *all* signals |
| from the sender to the receiver's slot |
| */ |
| QMetaObject::Connection QMetaObject::connect(const QObject *sender, int signal_index, |
| const QObject *receiver, int method_index, int type, int *types) |
| { |
| const QMetaObject *smeta = sender->metaObject(); |
| signal_index = methodIndexToSignalIndex(&smeta, signal_index); |
| return Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, |
| receiver, method_index, |
| 0, //FIXME, we could speed this connection up by computing the relative index |
| type, types)); |
| } |
| |
| /*! |
| \internal |
| Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex |
| |
| method_index is relative to the rmeta metaobject, if rmeta is \nullptr, then it is absolute index |
| |
| the QObjectPrivate::Connection* has a refcount of 2, so it must be passed to a QMetaObject::Connection |
| */ |
| QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, |
| int signal_index, const QMetaObject *smeta, |
| const QObject *receiver, int method_index, |
| const QMetaObject *rmeta, int type, int *types) |
| { |
| QObject *s = const_cast<QObject *>(sender); |
| QObject *r = const_cast<QObject *>(receiver); |
| |
| int method_offset = rmeta ? rmeta->methodOffset() : 0; |
| Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6); |
| QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr; |
| |
| QOrderedMutexLocker locker(signalSlotLock(sender), |
| signalSlotLock(receiver)); |
| |
| QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed(); |
| if (type & Qt::UniqueConnection && scd) { |
| if (scd->signalVectorCount() > signal_index) { |
| const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); |
| |
| int method_index_absolute = method_index + method_offset; |
| |
| while (c2) { |
| if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute) |
| return nullptr; |
| c2 = c2->nextConnectionList.loadRelaxed(); |
| } |
| } |
| type &= Qt::UniqueConnection - 1; |
| } |
| |
| std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection}; |
| c->sender = s; |
| c->signal_index = signal_index; |
| c->receiver.storeRelaxed(r); |
| QThreadData *td = r->d_func()->threadData; |
| td->ref(); |
| c->receiverThreadData.storeRelaxed(td); |
| c->method_relative = method_index; |
| c->method_offset = method_offset; |
| c->connectionType = type; |
| c->isSlotObject = false; |
| c->argumentTypes.storeRelaxed(types); |
| c->callFunction = callFunction; |
| |
| QObjectPrivate::get(s)->addConnection(signal_index, c.get()); |
| |
| locker.unlock(); |
| QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index); |
| if (smethod.isValid()) |
| s->connectNotify(smethod); |
| |
| return c.release(); |
| } |
| |
| /*! |
| \internal |
| */ |
| bool QMetaObject::disconnect(const QObject *sender, int signal_index, |
| const QObject *receiver, int method_index) |
| { |
| const QMetaObject *smeta = sender->metaObject(); |
| signal_index = methodIndexToSignalIndex(&smeta, signal_index); |
| return QMetaObjectPrivate::disconnect(sender, signal_index, smeta, |
| receiver, method_index, 0); |
| } |
| |
| /*! |
| \internal |
| |
| Disconnect a single signal connection. If QMetaObject::connect() has been called |
| multiple times for the same sender, signal_index, receiver and method_index only |
| one of these connections will be removed. |
| */ |
| bool QMetaObject::disconnectOne(const QObject *sender, int signal_index, |
| const QObject *receiver, int method_index) |
| { |
| const QMetaObject *smeta = sender->metaObject(); |
| signal_index = methodIndexToSignalIndex(&smeta, signal_index); |
| return QMetaObjectPrivate::disconnect(sender, signal_index, smeta, |
| receiver, method_index, 0, |
| QMetaObjectPrivate::DisconnectOne); |
| } |
| |
| /*! |
| \internal |
| Helper function to remove the connection from the senders list and set the receivers to \nullptr |
| */ |
| bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connections, int signalIndex, |
| const QObject *receiver, int method_index, void **slot, |
| QBasicMutex *senderMutex, DisconnectType disconnectType) |
| { |
| bool success = false; |
| |
| auto &connectionList = connections->connectionsForSignal(signalIndex); |
| auto *c = connectionList.first.loadRelaxed(); |
| while (c) { |
| QObject *r = c->receiver.loadRelaxed(); |
| if (r && (receiver == nullptr || (r == receiver |
| && (method_index < 0 || (!c->isSlotObject && c->method() == method_index)) |
| && (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) { |
| bool needToUnlock = false; |
| QBasicMutex *receiverMutex = nullptr; |
| if (r) { |
| receiverMutex = signalSlotLock(r); |
| // need to relock this receiver and sender in the correct order |
| needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex); |
| } |
| if (c->receiver.loadRelaxed()) |
| connections->removeConnection(c); |
| |
| if (needToUnlock) |
| receiverMutex->unlock(); |
| |
| success = true; |
| |
| if (disconnectType == DisconnectOne) |
| return success; |
| } |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| return success; |
| } |
| |
| /*! |
| \internal |
| Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex |
| */ |
| bool QMetaObjectPrivate::disconnect(const QObject *sender, |
| int signal_index, const QMetaObject *smeta, |
| const QObject *receiver, int method_index, void **slot, |
| DisconnectType disconnectType) |
| { |
| if (!sender) |
| return false; |
| |
| QObject *s = const_cast<QObject *>(sender); |
| |
| QBasicMutex *senderMutex = signalSlotLock(sender); |
| QBasicMutexLocker locker(senderMutex); |
| |
| QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed(); |
| if (!scd) |
| return false; |
| |
| bool success = false; |
| { |
| // prevent incoming connections changing the connections->receivers while unlocked |
| QObjectPrivate::ConnectionDataPointer connections(scd); |
| |
| if (signal_index < 0) { |
| // remove from all connection lists |
| for (int sig_index = -1; sig_index < scd->signalVectorCount(); ++sig_index) { |
| if (disconnectHelper(connections.data(), sig_index, receiver, method_index, slot, senderMutex, disconnectType)) |
| success = true; |
| } |
| } else if (signal_index < scd->signalVectorCount()) { |
| if (disconnectHelper(connections.data(), signal_index, receiver, method_index, slot, senderMutex, disconnectType)) |
| success = true; |
| } |
| } |
| |
| locker.unlock(); |
| if (success) { |
| scd->cleanOrphanedConnections(s); |
| |
| QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index); |
| if (smethod.isValid()) |
| s->disconnectNotify(smethod); |
| } |
| |
| return success; |
| } |
| |
| /*! |
| \fn void QMetaObject::connectSlotsByName(QObject *object) |
| |
| Searches recursively for all child objects of the given \a object, and connects |
| matching signals from them to slots of \a object that follow the following form: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 33 |
| |
| Let's assume our object has a child object of type \c{QPushButton} with |
| the \l{QObject::objectName}{object name} \c{button1}. The slot to catch the |
| button's \c{clicked()} signal would be: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 34 |
| |
| If \a object itself has a properly set object name, its own signals are also |
| connected to its respective slots. |
| |
| \sa QObject::setObjectName() |
| */ |
| void QMetaObject::connectSlotsByName(QObject *o) |
| { |
| if (!o) |
| return; |
| const QMetaObject *mo = o->metaObject(); |
| Q_ASSERT(mo); |
| const QObjectList list = // list of all objects to look for matching signals including... |
| o->findChildren<QObject *>(QString()) // all children of 'o'... |
| << o; // and the object 'o' itself |
| |
| // for each method/slot of o ... |
| for (int i = 0; i < mo->methodCount(); ++i) { |
| const QByteArray slotSignature = mo->method(i).methodSignature(); |
| const char *slot = slotSignature.constData(); |
| Q_ASSERT(slot); |
| |
| // ...that starts with "on_", ... |
| if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') |
| continue; |
| |
| // ...we check each object in our list, ... |
| bool foundIt = false; |
| for(int j = 0; j < list.count(); ++j) { |
| const QObject *co = list.at(j); |
| const QByteArray coName = co->objectName().toLatin1(); |
| |
| // ...discarding those whose objectName is not fitting the pattern "on_<objectName>_...", ... |
| if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size()+3] != '_') |
| continue; |
| |
| const char *signal = slot + coName.size() + 4; // the 'signal' part of the slot name |
| |
| // ...for the presence of a matching signal "on_<objectName>_<signal>". |
| const QMetaObject *smeta; |
| int sigIndex = co->d_func()->signalIndex(signal, &smeta); |
| if (sigIndex < 0) { |
| // if no exactly fitting signal (name + complete parameter type list) could be found |
| // look for just any signal with the correct name and at least the slot's parameter list. |
| // Note: if more than one of thoses signals exist, the one that gets connected is |
| // chosen 'at random' (order of declaration in source file) |
| QList<QByteArray> compatibleSignals; |
| const QMetaObject *smo = co->metaObject(); |
| int sigLen = qstrlen(signal) - 1; // ignore the trailing ')' |
| for (int k = QMetaObjectPrivate::absoluteSignalCount(smo)-1; k >= 0; --k) { |
| const QMetaMethod method = QMetaObjectPrivate::signal(smo, k); |
| if (!qstrncmp(method.methodSignature().constData(), signal, sigLen)) { |
| smeta = method.enclosingMetaObject(); |
| sigIndex = k; |
| compatibleSignals.prepend(method.methodSignature()); |
| } |
| } |
| if (compatibleSignals.size() > 1) |
| qWarning() << "QMetaObject::connectSlotsByName: Connecting slot" << slot |
| << "with the first of the following compatible signals:" << compatibleSignals; |
| } |
| |
| if (sigIndex < 0) |
| continue; |
| |
| // we connect it... |
| if (Connection(QMetaObjectPrivate::connect(co, sigIndex, smeta, o, i))) { |
| foundIt = true; |
| // ...and stop looking for further objects with the same name. |
| // Note: the Designer will make sure each object name is unique in the above |
| // 'list' but other code may create two child objects with the same name. In |
| // this case one is chosen 'at random'. |
| break; |
| } |
| } |
| if (foundIt) { |
| // we found our slot, now skip all overloads |
| while (mo->method(i + 1).attributes() & QMetaMethod::Cloned) |
| ++i; |
| } else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) { |
| // check if the slot has the following signature: "on_..._...(..." |
| int iParen = slotSignature.indexOf('('); |
| int iLastUnderscore = slotSignature.lastIndexOf('_', iParen-1); |
| if (iLastUnderscore > 3) |
| qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot); |
| } |
| } |
| } |
| |
| /*! |
| \internal |
| |
| \a signal must be in the signal index range (see QObjectPrivate::signalIndex()). |
| */ |
| static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) |
| { |
| const int *argumentTypes = c->argumentTypes.loadRelaxed(); |
| if (!argumentTypes) { |
| QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal); |
| argumentTypes = queuedConnectionTypes(m.parameterTypes()); |
| if (!argumentTypes) // cannot queue arguments |
| argumentTypes = &DIRECT_CONNECTION_ONLY; |
| if (!c->argumentTypes.testAndSetOrdered(0, argumentTypes)) { |
| if (argumentTypes != &DIRECT_CONNECTION_ONLY) |
| delete [] argumentTypes; |
| argumentTypes = c->argumentTypes.loadRelaxed(); |
| } |
| } |
| if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate |
| return; |
| int nargs = 1; // include return type |
| while (argumentTypes[nargs-1]) |
| ++nargs; |
| |
| QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed())); |
| if (!c->receiver.loadRelaxed()) { |
| // the connection has been disconnected before we got the lock |
| return; |
| } |
| if (c->isSlotObject) |
| c->slotObj->ref(); |
| locker.unlock(); |
| |
| QMetaCallEvent *ev = c->isSlotObject ? |
| new QMetaCallEvent(c->slotObj, sender, signal, nargs) : |
| new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs); |
| |
| void **args = ev->args(); |
| int *types = ev->types(); |
| |
| types[0] = 0; // return type |
| args[0] = nullptr; // return value |
| |
| if (nargs > 1) { |
| for (int n = 1; n < nargs; ++n) |
| types[n] = argumentTypes[n-1]; |
| |
| for (int n = 1; n < nargs; ++n) |
| args[n] = QMetaType::create(types[n], argv[n]); |
| } |
| |
| locker.relock(); |
| if (c->isSlotObject) |
| c->slotObj->destroyIfLastRef(); |
| if (!c->receiver.loadRelaxed()) { |
| // the connection has been disconnected while we were unlocked |
| locker.unlock(); |
| delete ev; |
| return; |
| } |
| |
| QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev); |
| } |
| |
| template <bool callbacks_enabled> |
| void doActivate(QObject *sender, int signal_index, void **argv) |
| { |
| QObjectPrivate *sp = QObjectPrivate::get(sender); |
| |
| if (sp->blockSig) |
| return; |
| |
| Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index); |
| |
| if (sp->isDeclarativeSignalConnected(signal_index) |
| && QAbstractDeclarativeData::signalEmitted) { |
| Q_TRACE_SCOPE(QMetaObject_activate_declarative_signal, sender, signal_index); |
| QAbstractDeclarativeData::signalEmitted(sp->declarativeData, sender, |
| signal_index, argv); |
| } |
| |
| const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr; |
| |
| void *empty_argv[] = { nullptr }; |
| if (!argv) |
| argv = empty_argv; |
| |
| if (!sp->maybeSignalConnected(signal_index)) { |
| // The possible declarative connection is done, and nothing else is connected |
| if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr) |
| signal_spy_set->signal_begin_callback(sender, signal_index, argv); |
| if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr) |
| signal_spy_set->signal_end_callback(sender, signal_index); |
| return; |
| } |
| |
| if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr) |
| signal_spy_set->signal_begin_callback(sender, signal_index, argv); |
| |
| bool senderDeleted = false; |
| { |
| Q_ASSERT(sp->connections.loadAcquire()); |
| QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed()); |
| QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed(); |
| |
| const QObjectPrivate::ConnectionList *list; |
| if (signal_index < signalVector->count()) |
| list = &signalVector->at(signal_index); |
| else |
| list = &signalVector->at(-1); |
| |
| Qt::HANDLE currentThreadId = QThread::currentThreadId(); |
| bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed(); |
| |
| // We need to check against the highest connection id to ensure that signals added |
| // during the signal emission are not emitted in this emission. |
| uint highestConnectionId = connections->currentConnectionId.loadRelaxed(); |
| do { |
| QObjectPrivate::Connection *c = list->first.loadRelaxed(); |
| if (!c) |
| continue; |
| |
| do { |
| QObject * const receiver = c->receiver.loadRelaxed(); |
| if (!receiver) |
| continue; |
| |
| QThreadData *td = c->receiverThreadData.loadRelaxed(); |
| if (!td) |
| continue; |
| |
| bool receiverInSameThread; |
| if (inSenderThread) { |
| receiverInSameThread = currentThreadId == td->threadId.loadRelaxed(); |
| } else { |
| // need to lock before reading the threadId, because moveToThread() could interfere |
| QMutexLocker lock(signalSlotLock(receiver)); |
| receiverInSameThread = currentThreadId == td->threadId.loadRelaxed(); |
| } |
| |
| |
| // determine if this connection should be sent immediately or |
| // put into the event queue |
| if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) |
| || (c->connectionType == Qt::QueuedConnection)) { |
| queued_activate(sender, signal_index, c, argv); |
| continue; |
| #if QT_CONFIG(thread) |
| } else if (c->connectionType == Qt::BlockingQueuedConnection) { |
| if (receiverInSameThread) { |
| qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " |
| "Sender is %s(%p), receiver is %s(%p)", |
| sender->metaObject()->className(), sender, |
| receiver->metaObject()->className(), receiver); |
| } |
| QSemaphore semaphore; |
| { |
| QBasicMutexLocker locker(signalSlotLock(sender)); |
| if (!c->receiver.loadAcquire()) |
| continue; |
| QMetaCallEvent *ev = c->isSlotObject ? |
| new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) : |
| new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, |
| sender, signal_index, argv, &semaphore); |
| QCoreApplication::postEvent(receiver, ev); |
| } |
| semaphore.acquire(); |
| continue; |
| #endif |
| } |
| |
| QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index); |
| |
| if (c->isSlotObject) { |
| c->slotObj->ref(); |
| |
| struct Deleter { |
| void operator()(QtPrivate::QSlotObjectBase *slot) const { |
| if (slot) slot->destroyIfLastRef(); |
| } |
| }; |
| const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> obj{c->slotObj}; |
| |
| { |
| Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get()); |
| obj->call(receiver, argv); |
| } |
| } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) { |
| //we compare the vtable to make sure we are not in the destructor of the object. |
| const int method_relative = c->method_relative; |
| const auto callFunction = c->callFunction; |
| const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0; |
| if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) |
| signal_spy_set->slot_begin_callback(receiver, methodIndex, argv); |
| |
| { |
| Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex); |
| callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv); |
| } |
| |
| if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr) |
| signal_spy_set->slot_end_callback(receiver, methodIndex); |
| } else { |
| const int method = c->method_relative + c->method_offset; |
| |
| if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) { |
| signal_spy_set->slot_begin_callback(receiver, method, argv); |
| } |
| |
| { |
| Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method); |
| QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv); |
| } |
| |
| if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr) |
| signal_spy_set->slot_end_callback(receiver, method); |
| } |
| } while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId); |
| |
| } while (list != &signalVector->at(-1) && |
| //start over for all signals; |
| ((list = &signalVector->at(-1)), true)); |
| |
| if (connections->currentConnectionId.loadRelaxed() == 0) |
| senderDeleted = true; |
| } |
| if (!senderDeleted) { |
| sp->connections.loadRelaxed()->cleanOrphanedConnections(sender); |
| |
| if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr) |
| signal_spy_set->signal_end_callback(sender, signal_index); |
| } |
| } |
| |
| /*! |
| \internal |
| */ |
| void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, |
| void **argv) |
| { |
| int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m); |
| |
| if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed())) |
| doActivate<true>(sender, signal_index, argv); |
| else |
| doActivate<false>(sender, signal_index, argv); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv) |
| { |
| int signal_index = signalOffset + local_signal_index; |
| |
| if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed())) |
| doActivate<true>(sender, signal_index, argv); |
| else |
| doActivate<false>(sender, signal_index, argv); |
| } |
| |
| /*! |
| \internal |
| signal_index comes from indexOfMethod() |
| */ |
| void QMetaObject::activate(QObject *sender, int signal_index, void **argv) |
| { |
| const QMetaObject *mo = sender->metaObject(); |
| while (mo->methodOffset() > signal_index) |
| mo = mo->superClass(); |
| activate(sender, mo, signal_index - mo->methodOffset(), argv); |
| } |
| |
| /*! |
| \internal |
| Returns the signal index used in the internal connections->receivers vector. |
| |
| It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod |
| while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots. |
| |
| If \a meta is not \nullptr, it is set to the meta-object where the signal was found. |
| */ |
| int QObjectPrivate::signalIndex(const char *signalName, |
| const QMetaObject **meta) const |
| { |
| Q_Q(const QObject); |
| const QMetaObject *base = q->metaObject(); |
| Q_ASSERT(QMetaObjectPrivate::get(base)->revision >= 7); |
| QArgumentTypeArray types; |
| QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signalName, types); |
| int relative_index = QMetaObjectPrivate::indexOfSignalRelative( |
| &base, name, types.size(), types.constData()); |
| if (relative_index < 0) |
| return relative_index; |
| relative_index = QMetaObjectPrivate::originalClone(base, relative_index); |
| if (meta) |
| *meta = base; |
| return relative_index + QMetaObjectPrivate::signalOffset(base); |
| } |
| |
| /***************************************************************************** |
| Properties |
| *****************************************************************************/ |
| |
| #ifndef QT_NO_PROPERTIES |
| |
| /*! |
| Sets the value of the object's \a name property to \a value. |
| |
| If the property is defined in the class using Q_PROPERTY then |
| true is returned on success and false otherwise. If the property |
| is not defined using Q_PROPERTY, and therefore not listed in the |
| meta-object, it is added as a dynamic property and false is returned. |
| |
| Information about all available properties is provided through the |
| metaObject() and dynamicPropertyNames(). |
| |
| Dynamic properties can be queried again using property() and can be |
| removed by setting the property value to an invalid QVariant. |
| Changing the value of a dynamic property causes a QDynamicPropertyChangeEvent |
| to be sent to the object. |
| |
| \b{Note:} Dynamic properties starting with "_q_" are reserved for internal |
| purposes. |
| |
| \sa property(), metaObject(), dynamicPropertyNames(), QMetaProperty::write() |
| */ |
| bool QObject::setProperty(const char *name, const QVariant &value) |
| { |
| Q_D(QObject); |
| const QMetaObject* meta = metaObject(); |
| if (!name || !meta) |
| return false; |
| |
| int id = meta->indexOfProperty(name); |
| if (id < 0) { |
| if (!d->extraData) |
| d->extraData = new QObjectPrivate::ExtraData; |
| |
| const int idx = d->extraData->propertyNames.indexOf(name); |
| |
| if (!value.isValid()) { |
| if (idx == -1) |
| return false; |
| d->extraData->propertyNames.removeAt(idx); |
| d->extraData->propertyValues.removeAt(idx); |
| } else { |
| if (idx == -1) { |
| d->extraData->propertyNames.append(name); |
| d->extraData->propertyValues.append(value); |
| } else { |
| if (value.userType() == d->extraData->propertyValues.at(idx).userType() |
| && value == d->extraData->propertyValues.at(idx)) |
| return false; |
| d->extraData->propertyValues[idx] = value; |
| } |
| } |
| |
| QDynamicPropertyChangeEvent ev(name); |
| QCoreApplication::sendEvent(this, &ev); |
| |
| return false; |
| } |
| QMetaProperty p = meta->property(id); |
| #ifndef QT_NO_DEBUG |
| if (!p.isWritable()) |
| qWarning("%s::setProperty: Property \"%s\" invalid," |
| " read-only or does not exist", metaObject()->className(), name); |
| #endif |
| return p.write(this, value); |
| } |
| |
| /*! |
| Returns the value of the object's \a name property. |
| |
| If no such property exists, the returned variant is invalid. |
| |
| Information about all available properties is provided through the |
| metaObject() and dynamicPropertyNames(). |
| |
| \sa setProperty(), QVariant::isValid(), metaObject(), dynamicPropertyNames() |
| */ |
| QVariant QObject::property(const char *name) const |
| { |
| Q_D(const QObject); |
| const QMetaObject* meta = metaObject(); |
| if (!name || !meta) |
| return QVariant(); |
| |
| int id = meta->indexOfProperty(name); |
| if (id < 0) { |
| if (!d->extraData) |
| return QVariant(); |
| const int i = d->extraData->propertyNames.indexOf(name); |
| return d->extraData->propertyValues.value(i); |
| } |
| QMetaProperty p = meta->property(id); |
| #ifndef QT_NO_DEBUG |
| if (!p.isReadable()) |
| qWarning("%s::property: Property \"%s\" invalid or does not exist", |
| metaObject()->className(), name); |
| #endif |
| return p.read(this); |
| } |
| |
| /*! |
| \since 4.2 |
| |
| Returns the names of all properties that were dynamically added to |
| the object using setProperty(). |
| */ |
| QList<QByteArray> QObject::dynamicPropertyNames() const |
| { |
| Q_D(const QObject); |
| if (d->extraData) |
| return d->extraData->propertyNames; |
| return QList<QByteArray>(); |
| } |
| |
| #endif // QT_NO_PROPERTIES |
| |
| |
| /***************************************************************************** |
| QObject debugging output routines. |
| *****************************************************************************/ |
| |
| static void dumpRecursive(int level, const QObject *object) |
| { |
| if (object) { |
| QByteArray buf; |
| buf.fill(' ', level / 2 * 8); |
| if (level % 2) |
| buf += " "; |
| QString name = object->objectName(); |
| QString flags = QLatin1String(""); |
| #if 0 |
| if (qApp->focusWidget() == object) |
| flags += 'F'; |
| if (object->isWidgetType()) { |
| QWidget * w = (QWidget *)object; |
| if (w->isVisible()) { |
| QString t("<%1,%2,%3,%4>"); |
| flags += t.arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height()); |
| } else { |
| flags += 'I'; |
| } |
| } |
| #endif |
| qDebug("%s%s::%s %s", (const char*)buf, object->metaObject()->className(), name.toLocal8Bit().data(), |
| flags.toLatin1().data()); |
| QObjectList children = object->children(); |
| if (!children.isEmpty()) { |
| for (int i = 0; i < children.size(); ++i) |
| dumpRecursive(level+1, children.at(i)); |
| } |
| } |
| } |
| |
| #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| /*! |
| \overload |
| \obsolete |
| |
| Dumps a tree of children to the debug output. |
| |
| \sa dumpObjectInfo() |
| */ |
| |
| void QObject::dumpObjectTree() |
| { |
| const_cast<const QObject *>(this)->dumpObjectTree(); |
| } |
| #endif |
| |
| /*! |
| Dumps a tree of children to the debug output. |
| |
| \note before Qt 5.9, this function was not const. |
| |
| \sa dumpObjectInfo() |
| */ |
| |
| void QObject::dumpObjectTree() const |
| { |
| dumpRecursive(0, this); |
| } |
| |
| #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| /*! |
| \overload |
| \obsolete |
| |
| Dumps information about signal connections, etc. for this object |
| to the debug output. |
| |
| \sa dumpObjectTree() |
| */ |
| |
| void QObject::dumpObjectInfo() |
| { |
| const_cast<const QObject *>(this)->dumpObjectInfo(); |
| } |
| #endif |
| |
| /*! |
| Dumps information about signal connections, etc. for this object |
| to the debug output. |
| |
| \note before Qt 5.9, this function was not const. |
| |
| \sa dumpObjectTree() |
| */ |
| |
| void QObject::dumpObjectInfo() const |
| { |
| qDebug("OBJECT %s::%s", metaObject()->className(), |
| objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); |
| |
| Q_D(const QObject); |
| QBasicMutexLocker locker(signalSlotLock(this)); |
| |
| // first, look for connections where this object is the sender |
| qDebug(" SIGNALS OUT"); |
| |
| QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); |
| if (cd && cd->signalVectorCount()) { |
| QObjectPrivate::SignalVector *signalVector = cd->signalVector.loadRelaxed(); |
| for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) { |
| const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.loadRelaxed(); |
| if (!c) |
| continue; |
| const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index); |
| qDebug(" signal: %s", signal.methodSignature().constData()); |
| |
| // receivers |
| while (c) { |
| if (!c->receiver.loadRelaxed()) { |
| qDebug(" <Disconnected receiver>"); |
| c = c->nextConnectionList.loadRelaxed(); |
| continue; |
| } |
| if (c->isSlotObject) { |
| qDebug(" <functor or function pointer>"); |
| c = c->nextConnectionList.loadRelaxed(); |
| continue; |
| } |
| const QMetaObject *receiverMetaObject = c->receiver.loadRelaxed()->metaObject(); |
| const QMetaMethod method = receiverMetaObject->method(c->method()); |
| qDebug(" --> %s::%s %s", |
| receiverMetaObject->className(), |
| c->receiver.loadRelaxed()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.loadRelaxed()->objectName()), |
| method.methodSignature().constData()); |
| c = c->nextConnectionList.loadRelaxed(); |
| } |
| } |
| } else { |
| qDebug( " <None>" ); |
| } |
| |
| // now look for connections where this object is the receiver |
| qDebug(" SIGNALS IN"); |
| |
| if (cd && cd->senders) { |
| for (QObjectPrivate::Connection *s = cd->senders; s; s = s->next) { |
| QByteArray slotName = QByteArrayLiteral("<unknown>"); |
| if (!s->isSlotObject) { |
| const QMetaMethod slot = metaObject()->method(s->method()); |
| slotName = slot.methodSignature(); |
| } |
| qDebug(" <-- %s::%s %s", |
| s->sender->metaObject()->className(), |
| s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), |
| slotName.constData()); |
| } |
| } else { |
| qDebug(" <None>"); |
| } |
| } |
| |
| #ifndef QT_NO_USERDATA |
| static QBasicAtomicInteger<uint> user_data_registration = Q_BASIC_ATOMIC_INITIALIZER(0); |
| |
| /*! |
| \internal |
| */ |
| uint QObject::registerUserData() |
| { |
| return user_data_registration.fetchAndAddRelaxed(1); |
| } |
| |
| /*! |
| \fn QObjectUserData::QObjectUserData() |
| \internal |
| */ |
| |
| /*! |
| \internal |
| */ |
| QObjectUserData::~QObjectUserData() |
| { |
| } |
| |
| /*! |
| \internal |
| */ |
| void QObject::setUserData(uint id, QObjectUserData* data) |
| { |
| Q_D(QObject); |
| if (!d->extraData) |
| d->extraData = new QObjectPrivate::ExtraData; |
| |
| if (d->extraData->userData.size() <= (int) id) |
| d->extraData->userData.resize((int) id + 1); |
| d->extraData->userData[id] = data; |
| } |
| |
| /*! |
| \internal |
| */ |
| QObjectUserData* QObject::userData(uint id) const |
| { |
| Q_D(const QObject); |
| if (!d->extraData) |
| return 0; |
| if ((int)id < d->extraData->userData.size()) |
| return d->extraData->userData.at(id); |
| return 0; |
| } |
| |
| #endif // QT_NO_USERDATA |
| |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| QDebug operator<<(QDebug dbg, const QObject *o) |
| { |
| QDebugStateSaver saver(dbg); |
| if (!o) |
| return dbg << "QObject(0x0)"; |
| dbg.nospace() << o->metaObject()->className() << '(' << (const void *)o; |
| if (!o->objectName().isEmpty()) |
| dbg << ", name = " << o->objectName(); |
| dbg << ')'; |
| return dbg; |
| } |
| #endif |
| |
| /*! |
| \macro Q_CLASSINFO(Name, Value) |
| \relates QObject |
| |
| This macro associates extra information to the class, which is available |
| using QObject::metaObject(). Qt makes only limited use of this feature, in |
| the \l{Active Qt}, \l{Qt D-Bus} and \l{Qt QML module}{Qt QML}. |
| |
| The extra information takes the form of a \a Name string and a \a Value |
| literal string. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 35 |
| |
| \sa QMetaObject::classInfo() |
| \sa QAxFactory |
| \sa {Using Qt D-Bus Adaptors} |
| \sa {Extending QML} |
| */ |
| |
| /*! |
| \macro Q_INTERFACES(...) |
| \relates QObject |
| |
| This macro tells Qt which interfaces the class implements. This |
| is used when implementing plugins. |
| |
| Example: |
| |
| \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 1 |
| \dots |
| \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 3 |
| |
| See the \l{tools/plugandpaint/plugins/basictools}{Plug & Paint |
| Basic Tools} example for details. |
| |
| \sa Q_DECLARE_INTERFACE(), Q_PLUGIN_METADATA(), {How to Create Qt Plugins} |
| */ |
| |
| /*! |
| \macro Q_PROPERTY(...) |
| \relates QObject |
| |
| This macro is used for declaring properties in classes that |
| inherit QObject. Properties behave like class data members, but |
| they have additional features accessible through the \l |
| {Meta-Object System}. |
| |
| \snippet code/doc_src_properties.cpp 0 |
| |
| The property name and type and the \c READ function are required. |
| The type can be any type supported by QVariant, or it can be a |
| user-defined type. The other items are optional, but a \c WRITE |
| function is common. The attributes default to true except \c USER, |
| which defaults to false. |
| |
| For example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 37 |
| |
| For more details about how to use this macro, and a more detailed |
| example of its use, see the discussion on \l {Qt's Property System}. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| /*! |
| \macro Q_ENUMS(...) |
| \relates QObject |
| \obsolete |
| |
| In new code, you should prefer the use of the Q_ENUM() macro, which makes the |
| type available also to the meta type system. |
| For instance, QMetaEnum::fromType() will not work with types declared with Q_ENUMS(). |
| |
| This macro registers one or several enum types to the meta-object |
| system. |
| |
| If you want to register an enum that is declared in another class, |
| the enum must be fully qualified with the name of the class |
| defining it. In addition, the class \e defining the enum has to |
| inherit QObject as well as declare the enum using Q_ENUMS(). |
| |
| \sa {Qt's Property System} |
| */ |
| |
| /*! |
| \macro Q_FLAGS(...) |
| \relates QObject |
| \obsolete |
| |
| This macro registers one or several \l{QFlags}{flags types} with the |
| meta-object system. It is typically used in a class definition to declare |
| that values of a given enum can be used as flags and combined using the |
| bitwise OR operator. |
| |
| \note This macro takes care of registering individual flag values |
| with the meta-object system, so it is unnecessary to use Q_ENUMS() |
| in addition to this macro. |
| |
| In new code, you should prefer the use of the Q_FLAG() macro, which makes the |
| type available also to the meta type system. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| /*! |
| \macro Q_ENUM(...) |
| \relates QObject |
| \since 5.5 |
| |
| This macro registers an enum type with the meta-object system. |
| It must be placed after the enum declaration in a class that has the Q_OBJECT or the |
| Q_GADGET macro. For namespaces use \l Q_ENUM_NS() instead. |
| |
| For example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 38 |
| |
| Enumerations that are declared with Q_ENUM have their QMetaEnum registered in the |
| enclosing QMetaObject. You can also use QMetaEnum::fromType() to get the QMetaEnum. |
| |
| Registered enumerations are automatically registered also to the Qt meta |
| type system, making them known to QMetaType without the need to use |
| Q_DECLARE_METATYPE(). This will enable useful features; for example, if used |
| in a QVariant, you can convert them to strings. Likewise, passing them to |
| QDebug will print out their names. |
| |
| Mind that the enum values are stored as signed \c int in the meta object system. |
| Registering enumerations with values outside the range of values valid for \c int |
| will lead to overflows and potentially undefined behavior when accessing them through |
| the meta object system. QML, for example, does access registered enumerations through |
| the meta object system. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| |
| /*! |
| \macro Q_FLAG(...) |
| \relates QObject |
| \since 5.5 |
| |
| This macro registers a single \l{QFlags}{flags type} with the |
| meta-object system. It is typically used in a class definition to declare |
| that values of a given enum can be used as flags and combined using the |
| bitwise OR operator. For namespaces use \l Q_FLAG_NS() instead. |
| |
| The macro must be placed after the enum declaration. |
| |
| For example, in QLibrary, the \l{QLibrary::LoadHints}{LoadHints} flag is |
| declared in the following way: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 39 |
| |
| The declaration of the flags themselves is performed in the public section |
| of the QLibrary class itself, using the \l Q_DECLARE_FLAGS() macro. |
| |
| \note The Q_FLAG macro takes care of registering individual flag values |
| with the meta-object system, so it is unnecessary to use Q_ENUM() |
| in addition to this macro. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| /*! |
| \macro Q_ENUM_NS(...) |
| \relates QObject |
| \since 5.8 |
| |
| This macro registers an enum type with the meta-object system. |
| It must be placed after the enum declaration in a namespace that |
| has the Q_NAMESPACE macro. It is the same as \l Q_ENUM but in a |
| namespace. |
| |
| Enumerations that are declared with Q_ENUM_NS have their QMetaEnum |
| registered in the enclosing QMetaObject. You can also use |
| QMetaEnum::fromType() to get the QMetaEnum. |
| |
| Registered enumerations are automatically registered also to the Qt meta |
| type system, making them known to QMetaType without the need to use |
| Q_DECLARE_METATYPE(). This will enable useful features; for example, if |
| used in a QVariant, you can convert them to strings. Likewise, passing them |
| to QDebug will print out their names. |
| |
| Mind that the enum values are stored as signed \c int in the meta object system. |
| Registering enumerations with values outside the range of values valid for \c int |
| will lead to overflows and potentially undefined behavior when accessing them through |
| the meta object system. QML, for example, does access registered enumerations through |
| the meta object system. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| |
| /*! |
| \macro Q_FLAG_NS(...) |
| \relates QObject |
| \since 5.8 |
| |
| This macro registers a single \l{QFlags}{flags type} with the |
| meta-object system. It is used in a namespace that has the |
| Q_NAMESPACE macro, to declare that values of a given enum can be |
| used as flags and combined using the bitwise OR operator. |
| It is the same as \l Q_FLAG but in a namespace. |
| |
| The macro must be placed after the enum declaration. |
| |
| \note The Q_FLAG_NS macro takes care of registering individual flag |
| values with the meta-object system, so it is unnecessary to use |
| Q_ENUM_NS() in addition to this macro. |
| |
| \sa {Qt's Property System} |
| */ |
| |
| |
| /*! |
| \macro Q_OBJECT |
| \relates QObject |
| |
| The Q_OBJECT macro must appear in the private section of a class |
| definition that declares its own signals and slots or that uses |
| other services provided by Qt's meta-object system. |
| |
| For example: |
| |
| \snippet signalsandslots/signalsandslots.h 1 |
| \codeline |
| \snippet signalsandslots/signalsandslots.h 2 |
| \snippet signalsandslots/signalsandslots.h 3 |
| |
| \note This macro requires the class to be a subclass of QObject. Use |
| Q_GADGET instead of Q_OBJECT to enable the meta object system's support |
| for enums in a class that is not a QObject subclass. |
| |
| \sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System} |
| */ |
| |
| /*! |
| \macro Q_GADGET |
| \relates QObject |
| |
| The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes |
| that do not inherit from QObject but still want to use some of the |
| reflection capabilities offered by QMetaObject. Just like the Q_OBJECT |
| macro, it must appear in the private section of a class definition. |
| |
| Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have |
| signals or slots. |
| |
| Q_GADGET makes a class member, \c{staticMetaObject}, available. |
| \c{staticMetaObject} is of type QMetaObject and provides access to the |
| enums declared with Q_ENUMS. |
| */ |
| |
| /*! |
| \macro Q_NAMESPACE |
| \relates QObject |
| \since 5.8 |
| |
| The Q_NAMESPACE macro can be used to add QMetaObject capabilities |
| to a namespace. |
| |
| Q_NAMESPACEs can have Q_CLASSINFO, Q_ENUM_NS, Q_FLAG_NS, but they |
| cannot have Q_ENUM, Q_FLAG, Q_PROPERTY, Q_INVOKABLE, signals nor slots. |
| |
| Q_NAMESPACE makes an external variable, \c{staticMetaObject}, available. |
| \c{staticMetaObject} is of type QMetaObject and provides access to the |
| enums declared with Q_ENUM_NS/Q_FLAG_NS. |
| |
| \sa Q_NAMESPACE_EXPORT |
| */ |
| |
| /*! |
| \macro Q_NAMESPACE_EXPORT(EXPORT_MACRO) |
| \relates QObject |
| \since 5.14 |
| |
| The Q_NAMESPACE_EXPORT macro can be used to add QMetaObject capabilities |
| to a namespace. |
| |
| It works exactly like the Q_NAMESPACE macro. However, the external |
| \c{staticMetaObject} variable that gets defined in the namespace |
| is declared with the supplied \a EXPORT_MACRO qualifier. This is |
| useful if the object needs to be exported from a dynamic library. |
| |
| \sa Q_NAMESPACE, {Creating Shared Libraries} |
| */ |
| |
| /*! |
| \macro Q_SIGNALS |
| \relates QObject |
| |
| Use this macro to replace the \c signals keyword in class |
| declarations, when you want to use Qt Signals and Slots with a |
| \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. |
| |
| The macro is normally used when \c no_keywords is specified with |
| the \c CONFIG variable in the \c .pro file, but it can be used |
| even when \c no_keywords is \e not specified. |
| */ |
| |
| /*! |
| \macro Q_SIGNAL |
| \relates QObject |
| |
| This is an additional macro that allows you to mark a single |
| function as a signal. It can be quite useful, especially when you |
| use a 3rd-party source code parser which doesn't understand a \c |
| signals or \c Q_SIGNALS groups. |
| |
| Use this macro to replace the \c signals keyword in class |
| declarations, when you want to use Qt Signals and Slots with a |
| \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. |
| |
| The macro is normally used when \c no_keywords is specified with |
| the \c CONFIG variable in the \c .pro file, but it can be used |
| even when \c no_keywords is \e not specified. |
| */ |
| |
| /*! |
| \macro Q_SLOTS |
| \relates QObject |
| |
| Use this macro to replace the \c slots keyword in class |
| declarations, when you want to use Qt Signals and Slots with a |
| \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. |
| |
| The macro is normally used when \c no_keywords is specified with |
| the \c CONFIG variable in the \c .pro file, but it can be used |
| even when \c no_keywords is \e not specified. |
| */ |
| |
| /*! |
| \macro Q_SLOT |
| \relates QObject |
| |
| This is an additional macro that allows you to mark a single |
| function as a slot. It can be quite useful, especially when you |
| use a 3rd-party source code parser which doesn't understand a \c |
| slots or \c Q_SLOTS groups. |
| |
| Use this macro to replace the \c slots keyword in class |
| declarations, when you want to use Qt Signals and Slots with a |
| \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. |
| |
| The macro is normally used when \c no_keywords is specified with |
| the \c CONFIG variable in the \c .pro file, but it can be used |
| even when \c no_keywords is \e not specified. |
| */ |
| |
| /*! |
| \macro Q_EMIT |
| \relates QObject |
| |
| Use this macro to replace the \c emit keyword for emitting |
| signals, when you want to use Qt Signals and Slots with a |
| \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. |
| |
| The macro is normally used when \c no_keywords is specified with |
| the \c CONFIG variable in the \c .pro file, but it can be used |
| even when \c no_keywords is \e not specified. |
| */ |
| |
| /*! |
| \macro Q_INVOKABLE |
| \relates QObject |
| |
| Apply this macro to declarations of member functions to allow them to |
| be invoked via the meta-object system. The macro is written before |
| the return type, as shown in the following example: |
| |
| \snippet qmetaobject-invokable/window.h Window class with invokable method |
| |
| The \c invokableMethod() function is marked up using Q_INVOKABLE, causing |
| it to be registered with the meta-object system and enabling it to be |
| invoked using QMetaObject::invokeMethod(). |
| Since \c normalMethod() function is not registered in this way, it cannot |
| be invoked using QMetaObject::invokeMethod(). |
| |
| If an invokable member function returns a pointer to a QObject or a |
| subclass of QObject and it is invoked from QML, special ownership rules |
| apply. See \l{qtqml-cppintegration-data.html}{Data Type Conversion Between QML and C++} |
| for more information. |
| */ |
| |
| /*! |
| \macro Q_REVISION |
| \relates QObject |
| |
| Apply this macro to declarations of member functions to tag them with a |
| revision number in the meta-object system. The macro is written before |
| the return type, as shown in the following example: |
| |
| \snippet qmetaobject-revision/window.h Window class with revision |
| |
| This is useful when using the meta-object system to dynamically expose |
| objects to another API, as you can match the version expected by multiple |
| versions of the other API. Consider the following simplified example: |
| |
| \snippet qmetaobject-revision/main.cpp Window class using revision |
| |
| Using the same Window class as the previous example, the newProperty and |
| newMethod would only be exposed in this code when the expected version is |
| 1 or greater. |
| |
| Since all methods are considered to be in revision 0 if untagged, a tag |
| of Q_REVISION(0) is invalid and ignored. |
| |
| This tag is not used by the meta-object system itself. Currently this is only |
| used by the QtQml module. |
| |
| For a more generic string tag, see \l QMetaMethod::tag() |
| |
| \sa QMetaMethod::revision() |
| */ |
| |
| /*! |
| \macro Q_SET_OBJECT_NAME(Object) |
| \relates QObject |
| \since 5.0 |
| |
| This macro assigns \a Object the objectName "Object". |
| |
| It doesn't matter whether \a Object is a pointer or not, the |
| macro figures that out by itself. |
| |
| \sa QObject::objectName() |
| */ |
| |
| /*! |
| \macro QT_NO_NARROWING_CONVERSIONS_IN_CONNECT |
| \relates QObject |
| \since 5.8 |
| |
| Defining this macro will disable narrowing and floating-point-to-integral |
| conversions between the arguments carried by a signal and the arguments |
| accepted by a slot, when the signal and the slot are connected using the |
| PMF-based syntax. |
| |
| \sa QObject::connect |
| */ |
| |
| /*! |
| \typedef QObjectList |
| \relates QObject |
| |
| Synonym for QList<QObject *>. |
| */ |
| |
| void qDeleteInEventHandler(QObject *o) |
| { |
| delete o; |
| } |
| |
| /*! |
| \fn template<typename PointerToMemberFunction> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type) |
| \overload connect() |
| \threadsafe |
| |
| Creates a connection of the given \a type from the \a signal in |
| the \a sender object to the \a method in the \a receiver object. |
| Returns a handle to the connection that can be used to disconnect |
| it later. |
| |
| The signal must be a function declared as a signal in the header. |
| The slot function can be any member function that can be connected |
| to the signal. |
| A slot can be connected to a given signal if the signal has at |
| least as many arguments as the slot, and there is an implicit |
| conversion between the types of the corresponding arguments in the |
| signal and the slot. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 44 |
| |
| This example ensures that the label always displays the current |
| line edit text. |
| |
| A signal can be connected to many slots and signals. Many signals |
| can be connected to one slot. |
| |
| If a signal is connected to several slots, the slots are activated |
| in the same order as the order the connection was made, when the |
| signal is emitted |
| |
| The function returns an handle to a connection if it successfully |
| connects the signal to the slot. The Connection handle will be invalid |
| if it cannot create the connection, for example, if QObject is unable |
| to verify the existence of \a signal (if it was not declared as a signal) |
| You can check if the QMetaObject::Connection is valid by casting it to a bool. |
| |
| By default, a signal is emitted for every connection you make; |
| two signals are emitted for duplicate connections. You can break |
| all of these connections with a single disconnect() call. |
| If you pass the Qt::UniqueConnection \a type, the connection will only |
| be made if it is not a duplicate. If there is already a duplicate |
| (exact same signal to the exact same slot on the same objects), |
| the connection will fail and connect will return an invalid QMetaObject::Connection. |
| |
| The optional \a type parameter describes the type of connection |
| to establish. In particular, it determines whether a particular |
| signal is delivered to a slot immediately or queued for delivery |
| at a later time. If the signal is queued, the parameters must be |
| of types that are known to Qt's meta-object system, because Qt |
| needs to copy the arguments to store them in an event behind the |
| scenes. If you try to use a queued connection and get the error |
| message |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 25 |
| |
| make sure to declare the argument type with Q_DECLARE_METATYPE |
| |
| Overloaded functions can be resolved with help of \l qOverload. |
| |
| \sa {Differences between String-Based and Functor-Based Connections} |
| */ |
| |
| /*! |
| \fn template<typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor) |
| |
| \threadsafe |
| \overload connect() |
| |
| Creates a connection from \a signal in |
| \a sender object to \a functor, and returns a handle to the connection |
| |
| The signal must be a function declared as a signal in the header. |
| The slot function can be any function or functor that can be connected |
| to the signal. |
| A function can be connected to a given signal if the signal has at |
| least as many argument as the slot. A functor can be connected to a signal |
| if they have exactly the same number of arguments. There must exist implicit |
| conversion between the types of the corresponding arguments in the |
| signal and the slot. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 45 |
| |
| Lambda expressions can also be used: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 46 |
| |
| The connection will automatically disconnect if the sender is destroyed. |
| However, you should take care that any objects used within the functor |
| are still alive when the signal is emitted. |
| |
| Overloaded functions can be resolved with help of \l qOverload. |
| |
| */ |
| |
| /*! |
| \fn template<typename PointerToMemberFunction, typename Functor> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type) |
| |
| \threadsafe |
| \overload connect() |
| |
| \since 5.2 |
| |
| Creates a connection of a given \a type from \a signal in |
| \a sender object to \a functor to be placed in a specific event |
| loop of \a context, and returns a handle to the connection. |
| |
| \note Qt::UniqueConnections do not work for lambdas, non-member functions |
| and functors; they only apply to connecting to member functions. |
| |
| The signal must be a function declared as a signal in the header. |
| The slot function can be any function or functor that can be connected |
| to the signal. |
| A function can be connected to a given signal if the signal has at |
| least as many argument as the slot. A functor can be connected to a signal |
| if they have exactly the same number of arguments. There must exist implicit |
| conversion between the types of the corresponding arguments in the |
| signal and the slot. |
| |
| Example: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 50 |
| |
| Lambda expressions can also be used: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 51 |
| |
| The connection will automatically disconnect if the sender or the context |
| is destroyed. |
| However, you should take care that any objects used within the functor |
| are still alive when the signal is emitted. |
| |
| Overloaded functions can be resolved with help of \l qOverload. |
| */ |
| |
| /*! |
| \internal |
| |
| Implementation of the template version of connect |
| |
| \a sender is the sender object |
| \a signal is a pointer to a pointer to a member signal of the sender |
| \a receiver is the receiver object, may not be \nullptr, will be equal to sender when |
| connecting to a static function or a functor |
| \a slot a pointer only used when using Qt::UniqueConnection |
| \a type the Qt::ConnctionType passed as argument to connect |
| \a types an array of integer with the metatype id of the parameter of the signal |
| to be used with queued connection |
| must stay valid at least for the whole time of the connection, this function |
| do not take ownership. typically static data. |
| If \nullptr, then the types will be computed when the signal is emit in a queued |
| connection from the types from the signature. |
| \a senderMetaObject is the metaobject used to lookup the signal, the signal must be in |
| this metaobject |
| */ |
| QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal, |
| const QObject *receiver, void **slot, |
| QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, |
| const int *types, const QMetaObject *senderMetaObject) |
| { |
| if (!signal) { |
| qWarning("QObject::connect: invalid nullptr parameter"); |
| if (slotObj) |
| slotObj->destroyIfLastRef(); |
| return QMetaObject::Connection(); |
| } |
| |
| int signal_index = -1; |
| void *args[] = { &signal_index, signal }; |
| for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) { |
| senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); |
| if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount) |
| break; |
| } |
| if (!senderMetaObject) { |
| qWarning("QObject::connect: signal not found in %s", sender->metaObject()->className()); |
| slotObj->destroyIfLastRef(); |
| return QMetaObject::Connection(0); |
| } |
| signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); |
| return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject); |
| } |
| |
| /*! |
| \internal |
| |
| Internal version of connect used by the template version of QObject::connect (called via connectImpl) and |
| also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative |
| to the number of signals. |
| */ |
| QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index, |
| const QObject *receiver, void **slot, |
| QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, |
| const int *types, const QMetaObject *senderMetaObject) |
| { |
| if (!sender || !receiver || !slotObj || !senderMetaObject) { |
| const char *senderString = sender ? sender->metaObject()->className() |
| : senderMetaObject ? senderMetaObject->className() |
| : "Unknown"; |
| const char *receiverString = receiver ? receiver->metaObject()->className() |
| : "Unknown"; |
| qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString); |
| if (slotObj) |
| slotObj->destroyIfLastRef(); |
| return QMetaObject::Connection(); |
| } |
| |
| QObject *s = const_cast<QObject *>(sender); |
| QObject *r = const_cast<QObject *>(receiver); |
| |
| QOrderedMutexLocker locker(signalSlotLock(sender), |
| signalSlotLock(receiver)); |
| |
| if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) { |
| QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed(); |
| if (connections->signalVectorCount() > signal_index) { |
| const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); |
| |
| while (c2) { |
| if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) { |
| slotObj->destroyIfLastRef(); |
| return QMetaObject::Connection(); |
| } |
| c2 = c2->nextConnectionList.loadRelaxed(); |
| } |
| } |
| type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection); |
| } |
| |
| std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection}; |
| c->sender = s; |
| c->signal_index = signal_index; |
| QThreadData *td = r->d_func()->threadData; |
| td->ref(); |
| c->receiverThreadData.storeRelaxed(td); |
| c->receiver.storeRelaxed(r); |
| c->slotObj = slotObj; |
| c->connectionType = type; |
| c->isSlotObject = true; |
| if (types) { |
| c->argumentTypes.storeRelaxed(types); |
| c->ownArgumentTypes = false; |
| } |
| |
| QObjectPrivate::get(s)->addConnection(signal_index, c.get()); |
| QMetaObject::Connection ret(c.release()); |
| locker.unlock(); |
| |
| QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index); |
| Q_ASSERT(method.isValid()); |
| s->connectNotify(method); |
| |
| return ret; |
| } |
| |
| /*! |
| Disconnect a connection. |
| |
| If the \a connection is invalid or has already been disconnected, do nothing |
| and return false. |
| |
| \sa connect() |
| */ |
| bool QObject::disconnect(const QMetaObject::Connection &connection) |
| { |
| QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr); |
| |
| if (!c) |
| return false; |
| QObject *receiver = c->receiver.loadRelaxed(); |
| if (!receiver) |
| return false; |
| |
| QBasicMutex *senderMutex = signalSlotLock(c->sender); |
| QBasicMutex *receiverMutex = signalSlotLock(receiver); |
| |
| QObjectPrivate::ConnectionData *connections; |
| { |
| QOrderedMutexLocker locker(senderMutex, receiverMutex); |
| |
| // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime |
| receiver = c->receiver.loadRelaxed(); |
| if (!receiver) |
| return false; |
| |
| connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed(); |
| Q_ASSERT(connections); |
| connections->removeConnection(c); |
| } |
| |
| connections->cleanOrphanedConnections(c->sender); |
| |
| c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), |
| c->signal_index)); |
| |
| const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr; |
| c->deref(); // has been removed from the QMetaObject::Connection object |
| |
| return true; |
| } |
| |
| /*! \fn template<typename PointerToMemberFunction> bool QObject::disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method) |
| \overload diconnect() |
| \threadsafe |
| |
| Disconnects \a signal in object \a sender from \a method in object |
| \a receiver. Returns \c true if the connection is successfully broken; |
| otherwise returns \c false. |
| |
| A signal-slot connection is removed when either of the objects |
| involved are destroyed. |
| |
| disconnect() is typically used in three ways, as the following |
| examples demonstrate. |
| \list 1 |
| \li Disconnect everything connected to an object's signals: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 26 |
| |
| \li Disconnect everything connected to a specific signal: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 47 |
| |
| \li Disconnect a specific receiver: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 30 |
| |
| \li Disconnect a connection from one specific signal to a specific slot: |
| |
| \snippet code/src_corelib_kernel_qobject.cpp 48 |
| |
| |
| \endlist |
| |
| \nullptr may be used as a wildcard, meaning "any signal", "any receiving |
| object", or "any slot in the receiving object", respectively. |
| |
| The \a sender may never be \nullptr. (You cannot disconnect signals |
| from more than one object in a single call.) |
| |
| If \a signal is \nullptr, it disconnects \a receiver and \a method from |
| any signal. If not, only the specified signal is disconnected. |
| |
| If \a receiver is \nullptr, it disconnects anything connected to \a |
| signal. If not, slots in objects other than \a receiver are not |
| disconnected. |
| |
| If \a method is \nullptr, it disconnects anything that is connected to \a |
| receiver. If not, only slots named \a method will be disconnected, |
| and all other slots are left alone. The \a method must be \nullptr |
| if \a receiver is left out, so you cannot disconnect a |
| specifically-named slot on all objects. |
| |
| \note It is not possible to use this overload to diconnect signals |
| connected to functors or lambda expressions. That is because it is not |
| possible to compare them. Instead, use the overload that takes a |
| QMetaObject::Connection |
| |
| \sa connect() |
| */ |
| |
| bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, const QMetaObject *senderMetaObject) |
| { |
| if (sender == nullptr || (receiver == nullptr && slot != nullptr)) { |
| qWarning("QObject::disconnect: Unexpected nullptr parameter"); |
| return false; |
| } |
| |
| int signal_index = -1; |
| if (signal) { |
| void *args[] = { &signal_index, signal }; |
| for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) { |
| senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); |
| if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount) |
| break; |
| } |
| if (!senderMetaObject) { |
| qWarning("QObject::disconnect: signal not found in %s", sender->metaObject()->className()); |
| return false; |
| } |
| signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); |
| } |
| |
| return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1, slot); |
| } |
| |
| /*! |
| \internal |
| Used by QML to connect a signal by index to a slot implemented in JavaScript (wrapped in a custom QSlotOBjectBase subclass). |
| |
| The signal_index is an index relative to the number of methods. |
| */ |
| QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type) |
| { |
| if (!sender) { |
| qWarning("QObject::connect: invalid nullptr parameter"); |
| if (slotObj) |
| slotObj->destroyIfLastRef(); |
| return QMetaObject::Connection(); |
| } |
| const QMetaObject *senderMetaObject = sender->metaObject(); |
| signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); |
| |
| return QObjectPrivate::connectImpl(sender, signal_index, sender, /*slot*/0, slotObj, type, /*types*/0, senderMetaObject); |
| } |
| |
| /*! |
| \internal |
| Used by QML to disconnect a signal by index that's connected to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) |
| In the QML case the slot is not a pointer to a pointer to the function to disconnect, but instead it is a pointer to an array of internal values |
| required for the disconnect. |
| */ |
| bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot) |
| { |
| const QMetaObject *senderMetaObject = sender->metaObject(); |
| signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); |
| |
| return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot); |
| } |
| |
| /*! \class QMetaObject::Connection |
| \inmodule QtCore |
| Represents a handle to a signal-slot (or signal-functor) connection. |
| |
| It can be used to check if the connection is valid and to disconnect it using |
| QObject::disconnect(). For a signal-functor connection without a context object, |
| it is the only way to selectively disconnect that connection. |
| |
| As Connection is just a handle, the underlying signal-slot connection is unaffected |
| when Connection is destroyed or reassigned. |
| */ |
| |
| /*! |
| Create a copy of the handle to the \a other connection |
| */ |
| QMetaObject::Connection::Connection(const QMetaObject::Connection &other) : d_ptr(other.d_ptr) |
| { |
| if (d_ptr) |
| static_cast<QObjectPrivate::Connection *>(d_ptr)->ref(); |
| } |
| |
| /*! |
| Assigns \a other to this connection and returns a reference to this connection. |
| */ |
| QMetaObject::Connection& QMetaObject::Connection::operator=(const QMetaObject::Connection& other) |
| { |
| if (other.d_ptr != d_ptr) { |
| if (d_ptr) |
| static_cast<QObjectPrivate::Connection *>(d_ptr)->deref(); |
| d_ptr = other.d_ptr; |
| if (other.d_ptr) |
| static_cast<QObjectPrivate::Connection *>(other.d_ptr)->ref(); |
| } |
| return *this; |
| } |
| |
| /*! |
| Creates a Connection instance. |
| */ |
| |
| QMetaObject::Connection::Connection() : d_ptr(0) {} |
| |
| /*! |
| Destructor for QMetaObject::Connection. |
| */ |
| QMetaObject::Connection::~Connection() |
| { |
| if (d_ptr) |
| static_cast<QObjectPrivate::Connection *>(d_ptr)->deref(); |
| } |
| |
| /*! \internal Returns true if the object is still connected */ |
| bool QMetaObject::Connection::isConnected_helper() const |
| { |
| Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const |
| QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr); |
| |
| return c->receiver.loadRelaxed(); |
| } |
| |
| |
| /*! |
| \fn QMetaObject::Connection::operator bool() const |
| |
| Returns \c true if the connection is valid. |
| |
| The connection is valid if the call to QObject::connect succeeded. |
| The connection is invalid if QObject::connect was not able to find |
| the signal or the slot, or if the arguments do not match. |
| */ |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qnamespace.cpp" |
| #include "moc_qobject.cpp" |