aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpyside/globalreceiverv2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpyside/globalreceiverv2.cpp')
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp199
1 files changed, 95 insertions, 104 deletions
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
index c843a40ac..51070b4ad 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.cpp
+++ b/sources/pyside6/libpyside/globalreceiverv2.cpp
@@ -9,35 +9,23 @@
#include <autodecref.h>
#include <gilstate.h>
+#include <pep384ext.h>
-#include <QtCore/qhashfunctions.h>
#include <QtCore/QMetaMethod>
#include <QtCore/QSet>
+#include <QtCore/QDebug>
#include <cstring>
#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
-namespace
-{
- static int DESTROY_SIGNAL_ID = 0;
- static int DESTROY_SLOT_ID = 0;
-}
namespace PySide
{
-size_t qHash(const GlobalReceiverKey &k, size_t seed)
-{
- QtPrivate::QHashCombine hash;
- seed = hash(seed, k.object);
- seed = hash(seed, k.method);
- return seed;
-}
-
class DynamicSlotDataV2
{
- Q_DISABLE_COPY(DynamicSlotDataV2)
+ Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
public:
DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent);
~DynamicSlotDataV2();
@@ -51,6 +39,8 @@ class DynamicSlotDataV2
static void onCallbackDestroyed(void *data);
static GlobalReceiverKey key(PyObject *callback);
+ void formatDebug(QDebug &debug) const;
+
private:
bool m_isMethod;
PyObject *m_callback;
@@ -61,6 +51,32 @@ class DynamicSlotDataV2
GlobalReceiverV2 *m_parent;
};
+void DynamicSlotDataV2::formatDebug(QDebug &debug) const
+{
+ debug << "method=" << m_isMethod << ", m_callback=" << m_callback;
+ if (m_callback != nullptr)
+ debug << '/' << Py_TYPE(m_callback)->tp_name;
+ debug << ", self=" << m_pythonSelf;
+ if (m_pythonSelf != nullptr)
+ debug << '/' << Py_TYPE(m_pythonSelf)->tp_name;
+ debug << ", m_pyClass=" << m_pyClass;
+ if (m_pyClass != nullptr)
+ debug << '/' << Py_TYPE(m_pyClass)->tp_name;
+ debug << ", signatures=" << m_signatures.keys();
+}
+
+QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "DynamicSlotDataV2(";
+ if (d)
+ d->formatDebug(debug);
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
}
using namespace PySide;
@@ -83,10 +99,10 @@ DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *paren
// PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
m_isMethod = true;
- m_callback = PyObject_GetAttr(callback, PySide::PyName::im_func());
+ m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func());
Py_DECREF(m_callback);
- m_pythonSelf = PyObject_GetAttr(callback, PySide::PyName::im_self());
+ m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
Py_DECREF(m_pythonSelf);
//monitor class from method lifetime
@@ -107,8 +123,8 @@ GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback)
return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
} else if (PySide::isCompiledMethod(callback)) {
// PYSIDE-1589: Fix for slots in compiled functions
- Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self()));
- Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func()));
+ Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
+ Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
return {self, func};
}
return {nullptr, callback};
@@ -120,7 +136,7 @@ PyObject *DynamicSlotDataV2::callback()
//create a callback based on method data
if (m_isMethod)
- callback = Py_TYPE(m_callback)->tp_descr_get(m_callback, m_pythonSelf, nullptr);
+ callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
else
Py_INCREF(callback);
@@ -146,7 +162,7 @@ void DynamicSlotDataV2::onCallbackDestroyed(void *data)
auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
self->m_weakRef = nullptr;
Py_BEGIN_ALLOW_THREADS
- delete self->m_parent;
+ SignalManager::instance().deleteGlobalReceiver(self->m_parent);
Py_END_ALLOW_THREADS
}
@@ -160,31 +176,20 @@ DynamicSlotDataV2::~DynamicSlotDataV2()
Py_DECREF(m_callback);
}
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) :
+const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
+
+GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
QObject(nullptr),
m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
- m_sharedMap(std::move(map))
+ m_receiver(receiver)
{
m_data = new DynamicSlotDataV2(callback, this);
- m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME);
- m_metaObject.update();
- m_refs.append(nullptr);
-
-
- if (DESTROY_SIGNAL_ID == 0)
- DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
-
- if (DESTROY_SLOT_ID == 0)
- DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME);
-
-
}
GlobalReceiverV2::~GlobalReceiverV2()
{
m_refs.clear();
// Remove itself from map.
- m_sharedMap->remove(m_data->key());
// Suppress handling of destroyed() for objects whose last reference is contained inside
// the callback object that will now be deleted. The reference could be a default argument,
// a callback local variable, etc.
@@ -193,7 +198,7 @@ GlobalReceiverV2::~GlobalReceiverV2()
// leading to the object being deleted, which emits destroyed(), which would try to invoke
// the already deleted callback, and also try to delete the object again.
DynamicSlotDataV2 *data = m_data;
- m_data = Q_NULLPTR;
+ m_data = nullptr;
delete data;
}
@@ -204,69 +209,34 @@ int GlobalReceiverV2::addSlot(const char *signature)
void GlobalReceiverV2::incRef(const QObject *link)
{
- if (link) {
- if (!m_refs.contains(link)) {
- bool connected{};
- Py_BEGIN_ALLOW_THREADS
- connected = QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- Py_END_ALLOW_THREADS
- if (connected)
- m_refs.append(link);
- else
- Q_ASSERT(false);
- } else {
- m_refs.append(link);
- }
- } else {
- m_refs.append(nullptr);
- }
+ Q_ASSERT(link);
+ m_refs.append(link);
}
void GlobalReceiverV2::decRef(const QObject *link)
{
- if (m_refs.isEmpty())
- return;
-
-
+ Q_ASSERT(link);
m_refs.removeOne(link);
- if (link) {
- if (!m_refs.contains(link)) {
- bool result{};
- Py_BEGIN_ALLOW_THREADS
- result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- Py_END_ALLOW_THREADS
- Q_ASSERT(result);
- if (!result)
- return;
- }
- }
-
- if (m_refs.isEmpty())
- Py_BEGIN_ALLOW_THREADS
- delete this;
- Py_END_ALLOW_THREADS
+}
+void GlobalReceiverV2::notify()
+{
+ purgeDeletedSenders();
}
-int GlobalReceiverV2::refCount(const QObject *link) const
+static bool isNull(const QPointer<const QObject> &p)
{
- if (link)
- return m_refs.count(link);
+ return p.isNull();
+}
- return m_refs.size();
+void GlobalReceiverV2::purgeDeletedSenders()
+{
+ m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
}
-void GlobalReceiverV2::notify()
+bool GlobalReceiverV2::isEmpty() const
{
- const QSet<const QObject *> objSet(m_refs.cbegin(), m_refs.cend());
- Py_BEGIN_ALLOW_THREADS
- for (const QObject *o : objSet) {
- if (o) {
- QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
- }
- }
- Py_END_ALLOW_THREADS
+ return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
}
GlobalReceiverKey GlobalReceiverV2::key() const
@@ -294,26 +264,22 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
if (!m_data) {
- if (id != DESTROY_SLOT_ID) {
- const QByteArray message = "PySide6 Warning: Skipping callback call "
- + slot.methodSignature() + " because the callback object is being destructed.";
- PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
- }
+ const QByteArray message = "PySide6 Warning: Skipping callback call "
+ + slot.methodSignature() + " because the callback object is being destructed.";
+ PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
return -1;
}
- if (id == DESTROY_SLOT_ID) {
- if (m_refs.isEmpty())
- return -1;
- auto obj = *reinterpret_cast<QObject **>(args[1]);
- incRef(); //keep the object live (safe ref)
- m_refs.removeAll(obj); // remove all refs to this object
- decRef(); //remove the safe ref
- } else {
- const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
- Shiboken::AutoDecRef callback(m_data->callback());
- SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
- }
+ const bool setSenderDynamicProperty = !m_receiver.isNull();
+ if (setSenderDynamicProperty)
+ m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
+
+ const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
+ Shiboken::AutoDecRef callback(m_data->callback());
+ SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
+
+ if (setSenderDynamicProperty)
+ m_receiver->setProperty(senderDynamicProperty, QVariant{});
// SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
// error so it considered "handled".
@@ -330,3 +296,28 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
return -1;
}
+
+void GlobalReceiverV2::formatDebug(QDebug &debug) const
+{
+ debug << "receiver=" << m_receiver << ", slot=" << m_data;
+ if (isEmpty())
+ debug << ", empty";
+ else
+ debug << ", refs=" << m_refs;
+};
+
+QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "GlobalReceiverV2(";
+ if (g)
+ g->formatDebug(debug);
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
+}
+
+} // namespace PySide