How to use SIGNAL and SLOT without deriving from QObject?
Asked Answered
R

6

19

OR other way to formulate my question (though it didn't solve my problem): 'QObject::QObject' cannot access private member declared in class 'QObject'

I need SIGNALs and SLOTS functionality in my class, but I assume it is not possible without to derive from QObject?

class MyClass
{
signals:
   importantSignal();
public slots:
   importantSlot();
};

The Problem seems to be that I need to derive from QObject to use signals and slots ... but I need the default contructor of MyClass. But I can't construct them because of the following feature of QObject: No Copy Constructor or Assignment Operator.

I tried a lot ...

So my shoul Class look like that:

#include <QObject>
class MyClass: public QObject
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = 0); //autogenerated by qtcreator for QObject derived class
    MyClass(const MyClass * other);

signals:
    importantSignal();
public slots:
    importantSlot();
};

I need the default contructor of MyClass.

So is there any possibility do avoid the "'QObject::QObject' cannot access private member declared in class 'QObject'" error?

Or as an alternative is there any possibility to use signals and slots without QObject?

I'm glad for any advice.

Rizo answered 21/9, 2011 at 15:38 Comment(4)
If you look at the Qt documentation for QObject they talk about why you shouldn't try to use QObjects as a "value" (no copy constructors), but rather as unique objects - in other words reference them always with a pointer. Perhaps there is a way that we can change your design a bit so that you can make use of signals/slots. For instance, if you want to store instances of your class in a container/list, you can store the pointer instead. Why exactly do you need the default constructor? Or, how are you using this class?Teasley
i use this class as dataholder, instead of a struc... - each entity as one tupel. and the point why i try to derivate QObject is that i want to use signals and slots for loading webcontent (pictures)...Rizo
and i also want to store instances in self-written countainer/cluster. ? i need the default constructor for geting empty instances.Rizo
mh... i exactly use my class for storing values and for example a picture loaded from the web. the picture for example is drawn on a map (using marblewidget).Rizo
A
12

If you want a copyable object with QObject features you need membership (by pointer) rather than inheritence.

You can derive a class Handler from QObject where Handler's slots call SomeInterface virtual functions on its parent.

struct NonQObjectHandler {
    virtual ~ NonQObjectHandler () {}
    virtual void receive (int, float) = 0;
};

class Handler : public NonQObjectHandler {
    struct Receiver;
    std :: unique_ptr <Receiver> m_receiver;
    void receive (int, float); // NonQObjectHandler
public:
    Handler ();
    Handler (const Handler &); // This is what you're really after
};

class Handler :: Receiver : public QObject {
Q_OBJECT
private:
    NonQObjectHandler * m_handler;
private slots:
    void receive (int, float); // Calls m_handler's receive
public:
    Receiver (NonQObjectHandler *);
};

Handler :: Handler ()
: m_receiver (new Receiver (this))
{
}

Handler :: Handler (const Handler & old)
: m_receiver (new Receiver (this))
{
    // Copy over any extra state variables also, but
    // Receiver is created anew.
}

Handler :: Receiver :: Receiver (NonQObjectHandler * h)
: m_handler (h)
{
    connect (foo, SIGNAL (bar (int, float)), this, SLOT (receive (int, float)));
}

void Handler :: Receiver :: receive (int i, float f)
{
    m_handler -> receive (i, f);
}
Antoinetteanton answered 21/9, 2011 at 16:8 Comment(1)
you may give an example/snippet?Rizo
S
7

If you want to implement event-driven functionality using a signals/slots pattern, but do not want to work inside the confines of Qt (i.e., you want to use your class inside of STL containers, etc. that require copy-constructors), I would suggest using Boost::signal.

Otherwise, no, you can't possibly do what you're wanting without deriving from QObject since that base class is what handles the Qt runtime signals/slots functionality.

Silicosis answered 21/9, 2011 at 15:44 Comment(3)
mh... nice advice, but i like to work on with Qt. To late to use another lib. but thx.Rizo
so is there a way to avoid that "'QObject::QObject' cannot access private member declared in class 'QObject'"-error?Rizo
Yes ... don't do something that requires a copy-constructor or assignment operator. This basically means that it's often a good choice to work with objects derived from QObject through pointers rather than instances that may need copying to move around. It's not that you can't use instances, but you can't copy them, and therefore you also can't stick them in STL containers, etc.Silicosis
F
2

You cannot use Qt's signal/slot mechanisms without using QObject/Q_OBJECT.

You theoretically could create a dummy QObject and compose it into your class. The dummy would then forward the slot calls to your class. You will probably run in to issues with lifetime management, for the reasons that Liz has described in her comment.

Fakir answered 21/9, 2011 at 16:5 Comment(1)
You can use connect() without Q_OBJECT. But you won't be able to use emit without Q_OBJECT.Shearer
N
2

Since Qt5 you can just connect to any function

connect(&timer, &QTimer::finished,
        &instanceOfMyClass, &MyClass::fancyMemberFunction);
Nils answered 1/1, 2017 at 20:0 Comment(2)
Does this work when MyClass does not derive from QObject?Bayly
No, but then you can omit the receiver (3rd arg) and use static funcions or std::bind/lambdas to bind the object and member function to a callable.Nils
S
1

In Qt5, you use QObject::connect to connect signal with slot:

/*
   QMetaObject::Connection QObject::connect(
    const QObject *sender,
    const char *signal,
    const char *method,
    Qt::ConnectionType type = Qt::AutoConnection) const;
 */

#include <QApplication>
#include <QDebug>
#include <QLineEdit>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QLineEdit lnedit;

    // connect signal `QLineEdit::textChanged` with slot `lambda function`
    QObject::connect(&lnedit, &QLineEdit::textChanged, [&](){qDebug()<<lnedit.text()<<endl;});

    lnedit.show();
    return app.exec();
}

The result:

enter image description here

Sybilla answered 2/12, 2017 at 13:2 Comment(0)
B
0

Just because QObject is not copyable doesn't mean that you have to copy it when your class is copied, or assigned to, etc. Specifically, all you need to do is to insulate your class from QObject's copy and assignment operators (because they are deleted).

Typically, you'd factor out the "copyable non-copyable QObject" into a separate class:

// main.cpp
#include <QObject>
#include <QVariant>

class CopyableQObject : public QObject
{
protected:
   explicit CopyableQObject(QObject* parent = nullptr) : QObject(parent) {}
   CopyableQObject(const CopyableQObject& other) { initFrom(other); }
   CopyableQObject(CopyableQObject&& other)      { initFrom(other); }
   CopyableQObject& operator=(const CopyableQObject& other)
   {
      return initFrom(other), *this;
   }
   CopyableQObject& operator=(CopyableQObject&& other)
   {
      return initFrom(other), *this;
   }
private:
   void initFrom(const CopyableQObject& other)
   {
      setParent(other.parent());
      setObjectName(other.objectName());
   }
   void initFrom(CopyableQObject& other)
   {
      initFrom(const_cast<const CopyableQObject&>(other));
      for (QObject* child : other.children())
         child->setParent( this );
      for (auto& name : other.dynamicPropertyNames())
         setProperty(name, other.property(name));
   }
};

You can copy whatever attributes you desire between the QObject instances. Above, the parent and object name are copied. Furthermore, when moving from another object, its children are reparented, and dynamic property names are transferred as well.

Now the CopyableQObject adapter implements all the constructors that will allow derived classes to be copy-constructible, copy-assignable, move-constructible and move-assignable.

All your class needs to do is derive from the adapter class above:

class MyClass : public CopyableQObject
{
   Q_OBJECT
public:
   Q_SIGNAL void signal1();
   Q_SIGNAL void signal2();
};

We can test that it works:

int main()
{
   int counter = 0;

   MyClass obj1;
   MyClass obj2;
   Q_SET_OBJECT_NAME(obj1);

   QObject::connect(&obj1, &MyClass::signal1, [&]{ counter += 0x1; });
   QObject::connect(&obj1, &MyClass::signal2, [&]{ counter += 0x10; });
   QObject::connect(&obj2, &MyClass::signal1, [&]{ counter += 0x100; });
   QObject::connect(&obj2, &MyClass::signal2, [&]{ counter += 0x1000; });

   Q_ASSERT(counter == 0);
   emit obj1.signal1();
   emit obj1.signal2();
   Q_ASSERT(counter == 0x11);

   QObject obj3(&obj1);
   Q_ASSERT(obj3.parent() == &obj1);
   const auto name1 = obj1.objectName();

   obj2 = std::move(obj1);

   Q_ASSERT(obj3.parent() == &obj2);
   Q_ASSERT(obj2.objectName() == name1);

   emit obj2.signal1();
   emit obj2.signal2();
   Q_ASSERT(counter == 0x1111);
}

#include "main.moc"

This concludes the complete, compileable example.

Babby answered 24/9, 2021 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.