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.
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);
}
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.
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.
Since Qt5 you can just connect to any function
connect(&timer, &QTimer::finished,
&instanceOfMyClass, &MyClass::fancyMemberFunction);
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:
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.
Related
i have 2 class : Class MaFentre and Code
code.h :
class Code : public QObject {
public :
explicit Code(Q3DScatter *scatter);
public slots:
std::vector<point> readingData(std::string inputFileName);
}
MaFenetre.h :
class MaFenetre : public QWidget
{ Q_OBJECT
public:
MaFenetre();
private:
QLineEdit *entry1;
}
Code.cpp :
std::vector<point> Code::readingData(std::string inputFileName){
// i read a file here
}
i created the Code class object in the constructor of the class MaFenetre
Code *modifier = new Code(graph);
for making connection between slot and signal
QObject::connect(entry1, SIGNAL(textChanged(QString)),modifier, SLOT(readingDara(std::string inputFileName)))
i know the parameters must be of the same type , for that i try to code :
QObject::connect(entry, SIGNAL(textChanged(QString.toStdString)),modifier, SLOT(readingDara(std::string inputFileName)))
but it doesnt work
Your signal and slot arguments are not compatible.
You can do this workaround with the lambda function
Code *modifier = new Code();
MaFenetre * poMaFenetre = new MaFenetre();
connect(poMaFenetre->Entry(), &QLineEdit::textChanged,
[modifier](const QString & oText)
{
std::vector<int> data = modifier->readingData(oText.toStdString());
// Handle data here...
});
In the MaFenetre
class MaFenetre : public QWidget
{
Q_OBJECT
public:
MaFenetre() {entry1.reset(new QLineEdit());}
QLineEdit *Entry() {return entry1.data();}
private:
QScopedPointer<QLineEdit> entry1;
};
Using signals and slots it's not the same as calling function and pass parameters.
At first signal and slot must have same parameters type, means they must be defined with same parameters. In your case you have to change your slot to fit possible signals. Also note that returned value is useless in case of slot invoking, so better way is to keep you reading function as is, move it to private area, and create wrapper slot:
void Code::readingDataSlot(QString inputFileName)
{
std::vector<point> result = readingData( inputFileName.toStdString() );
// Do what ever you need with result vector
}
and connect it to signal.
connect(entry1, SIGNAL(textChanged(QString)),modifier, SLOT(readingDataSlot(QString)));
I'm new to programming. I can not understand how to make a reference to the methods of another class.
I have several files and classes:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtCore/QtGlobal>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class Valve;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
class A {
...
private:
void start();
}
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowFlags(Qt::CustomizeWindowHint);
this->setFixedSize(this->geometry().width(),this->geometry().height());
//класс для 7 клапанов
valve = new Valve(7);
}
MainWindow::~MainWindow()
{
delete settings;
delete ui;
}
void MainWindow::valveSwitch(int id)
{
if (valve->getState(id))
closeValve(id);
else
openValve(id);
}
void MainWindow::openValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->open(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(QPalette(Qt::green));
//valveButton[id-1]->setStyleSheet(VALVE_OPEN_COLOR);
QString style = QString(DEFAULT_STYLE_BUTTON) + QString(DEFAULT_BACKGROUND_BUTTON);
valveButton[id-1]->setStyleSheet(style);
ui->mainLabel->setText(str + " open! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant open " + str);
remoteDisconnect();
}
}
void MainWindow::closeValve(int id)
{
QString str = "Valve №" + QString::number(id);
valveButton[id-1]->setEnabled(false);
if (valve->close(id)) {
valveButton[id-1]->setEnabled(true);
//valveButton[id-1]->setPalette(style()->standardPalette());
valveButton[id-1]->setStyleSheet("");
ui->mainLabel->setText(str + " close! :)");
}
else {
valveButton[id-1]->setEnabled(true);
ui->mainLabel->setText("Cant close " + str);
remoteDisconnect();
}
}
A::A
{
}
A::~A
{
}
void A::start()
{
//MainWindow::openValve(2);
//valve.open(3);
}
How do I access MainWindow class methods openValve/closeValve from class A?
Or how can I access an instance valve of a class Valve of MainWindow's constructor from class A?
//MainWindow::openValve(2);
//valve.open(3);
At very first:
openValve is not static, so you need an instance of MainWindow to be able to call it:
MainWindow* mw_ex0;
// alternatively, if more appropriate:
MainWindow& mw_ex1;
mw_ex0->openValve(2);
mw_ex1.openValve(2);
The MainWindow instance could be a parameter of your function start or a member variable of class A – depending on your concrete needs.
Same applies if you want to access the valve member (valve is a pointer, so you need operator->): mw_ex0->valve->open(3); or mw_ex1.valve->open(3);).
However, you need to grant class A access to those currently private members; three options:
Make A a friend class of MainWindow - this allows A to access MainWindow's private members (might apply for Valve class, too, if open is not public).
Make the appropriate functions public (MainWindow::openValve and Valve::open); to access the valve member of MainWindow, too, you could make it public, too, but it is in general not recommendable to make the internals of a class publicly available to the outside world - someone might simply change your valve member to something else - and your program is broken... So rather provide a simple getter for.
Make A an inner class of MainWindow. Then it gets access to its outer class members implicitly (depending on the requirements for class A, this might not be suitable – up to you to decide...).
Sidenotes:
In your constructor, you do not initialise the settings member.
You do not clean up the valve member in your destructor (potential memory leak).
To avoid having to clean up, you could to incorporate valve directly in your class - this is not always suitable, but might be a good option here (up to you to decide, just showing the alternative):
class MainWindow
{
Valve valve;
};
MainWindow::MainWindow()
: valve(7) // calls constructor directly
{ }
Be aware that you now do use operator. to access the valve's members (mw_ex0->valve.open(3);). Advantage is that Valve will no be automatically cleaned up together with MainWindow. Alternatively, a std::unique_ptr could be used to hold the pointer to your Valve instance, then you get automatic cleanup, too.
You should pass a MainWindow object into the A::start method:
class A {
...
private:
void start(MainWindow & w);
}
void A::start(MainWindow & w) {
w._MainWindow_method_name_here_();
}
Or you should declare a static method in MainWindow class:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static void aStaticMethod();
};
void MainWindow::aStaticMethod() {
...
}
void A::start() {
MainWindow::aStaticMethod();
}
To access protected/private methods of MainWindow you should declare the A class as a friend of MainWindow:
class MainWindow : public QMainWindow
{
friend class A;
...
};
Update
I create a new class for it to work in a separate thread, and call its methods from the main class (by clicking on the button). Accordingly, I need class A to open / close valves, etc.
The "true Qt way" is to use signals & slots mechanism.
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// Transform open/closeValve methods into slots
// (a method that can be assigned as an event handler)
//
public slots:
void openValve(int id);
void closeValve(int id);
private:
Ui::MainWindow *ui;
Settings *settings;
Valve *valve;
};
// This class should be a descendant of QObject
//
class A : public QObject
{
Q_OBJECT
// Transform this method to a slot, so it can be called
// as regular method, or can be assigned as an event handler,
// for instance, as QPushButton::click handler.
//
public slots:
void start();
// Add signals
//
signals:
void openValveSignal(int id);
void closeValveSignal(int id);
}
void A::start()
{
// do something and then emit the signal to open valve,
// MainWindow::openValve(2) will be called
emit openValveSignal(2);
...
// do something and then emit the signal to close valve,
// MainWindow::closeValve(3) will be called
emit closeValveSignal(3);
}
// connects A signals with MainWindow slots,
// so when you `emit A::***Signal()` then corresponding
// `MainWindow::***` method will be called
//
void initialize(MainWindow * pWnd, A * pA)
{
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
You can call a->start() method from MainWindow methods as usual. Or you can connect button clicked signal with A::start method, for instance:
void initialize(MainWindow * pWnd, QAbstractButton * pBtn, A * pA)
{
// C++11 lambda function is used here because A::start has no arguments
QObject::connect(pBtn, &QAbstractButton::clicked, [pA](){ pA->start(); });
QObject::connect(pA, &A::openValveSignal, pWnd, &MainWindow::openValve);
QObject::connect(pA, &A::closeValveSignal, pWnd, &MainWindow::closeValve);
}
so when you click a button then A::start method will be called automatically. And then MainWindow::open/closeValve methods will be called from A::start method.
Declare openValve as public method and valve as public object (open must be public too)
Then use as:
MainWindow mainWindow;
mainWindow.openValve(2);
mainWindow.valve.open(3);
I'm trying to connect the signal currentIndexChanged(int) from QComboBox to a slot from my class that receive an enum, like Foo::mySlot(EnumFoo).
I know that all these ways DON'T work:
connect(cb, &QComboBox::currentIndexChanged, foo, &Foo::mySlot);
connect(cb, static_cast<void (QComboBox::*)(EnumFoo)>(&QComboBox::currentIndexChanged), foo, &Foo::mySlot);
connect(cb, &QComboBox::currentIndexChanged, foo, static_cast<void (Foo::*)(int)>(&Foo::mySlot));
because in C/C++, int never cast implicitly to an enum type. The opposite is true, I think if my signal has an enum parameter, I can connect it to a slot with int parameter without problem.
I know how to solve this question using lambda function:
connect(cb, &QComboBox::currentIndexChanged, [=](int i){ foo.mySlot(static_cast<EnumFoo>(i)); });
Is there any way to solve this problem WITHOUT lambda function?
EDIT: Using the solution I proposed, I'm connecting Foo::mySlot this way.
LambdaWrapper *lw = new LambdaWrapper;
lw->connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int i){ foo.mySlot(static_cast<EnumFoo>(i)); }, foo);
and I don't have to worry about disconnection stuff anymore. Just have to manage lw lifespan.
Use an intermediate slot that takes an int and calls the other slot function directly.
class Foo : public QObject
{
Q_OBJECT:
public slots:
void IndexChanged(int intParam);
void mySlot(EnumFoo fooType);
};
void Foo::IndexChanged(int index);
{
EnumFoo fooType = <static_cast<EnumFoo>(index);
mySlot(fooType);
}
connect(cb, &QComboBox::currentIndexChanged, foo, &Foo::IndexChanged);
I've created a small class to automate this disconnect stuff handling.
lambdawrapper.h
class LambdaWrapper : public QObject {
Q_OBJECT
public:
explicit LambdaWrapper(QObject* parent = 0);
virtual ~LambdaWrapper();
template<typename Func1, typename Func2>
void connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
Func1 signal,
Func2 slot,
QObject* receiver = 0) {
connections << QObject::connect(sender, signal, slot);
helperConnection(receiver);
}
private slots:
void disconnectReceiver(QObject* obj);
private:
void helperConnection(QObject* obj);
QList<QMetaObject::Connection> connections;
QObjectList receivers;
};
lambdawrapper.cpp
LambdaWrapper::LambdaWrapper(QObject *parent) : QObject(parent) { }
LambdaWrapper::~LambdaWrapper() {
for (const QMetaObject::Connection& c : connections)
QObject::disconnect(c);
}
void LambdaWrapper::disconnectReceiver(QObject *obj) {
if (receivers.contains(obj)) {
QList<const QMetaObject::Connection*> toRemove;
const int n = receivers.size();
for (int i=0; i<n ; ++i) {
if (receivers.at(i) == obj) {
disconnect(connections.at(i));
toRemove << & connections.at(i);
}
}
receivers.removeAll(obj);
for (const QMetaObject::Connection* c : toRemove)
connections.removeAll(*c);
}
}
void LambdaWrapper::helperConnection(QObject* receiver) {
receivers << receiver;
if (receiver) QObject::connect(receiver, &QObject::destroyed, this, &LambdaWrapper::disconnectReceiver);
}
So, all you have to do is to instantiate this class into your main QWidget, QMainWindow or QDialog derivative.
foo.h
....
class Foo : public QMainWindow
....
private:
LambdaWrapper* lw;
....
foo.cpp
....
lw = new LambdaWrapper(this);
QCheckBox *ck = new QCheckBox("check");
lw->connect(ck, &QAbstraceButton::toggled, [=](bool b){ /* do stuff */}, 0);
....
The 4th argument of connect member is optional and represents some QObject pointer which is called inside the lambda (and that can crash the program if the lambda is called with this object already deleted). If you pass this argument, its destroyed signal will be connected to a slot in LambdaWrapper which ensures that all connections to this receiver will be disconnected when this receiver is destroyed.
It's pretty dirty but solves my problem. Now I can connect to lambdas straightforward and don't have to worry about disconnecting them.
I have a base class, which defines a Qt slot
class Base
{
public:
Base()
{
connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot));
}
public slots:
virtual void mySlot()
{}
}
Subclass A just implements some other stuff. Subclass B overrides the slot
class SubB : Base
{
public:
SubB() : Base()
{
// Necessary?
connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot));
}
public slots:
virtual void mySlot() override
{}
}
Does the override of the slot also replace the connection, which was done before in the Bass constructor (I.e. The connect in SubB would be unnecessary)?
It gets better: you don't need any special treatment for the slot in the derived class. There's no need to make it virtual (it already is per C++ semantics), and there's no need to make it a slot again (it already is per Qt semantics). It is incorrect to add the second connection in Derived, it'll simply result in the slot being activated twice per every activation of the signal.
Remember that signals and slots are regular C++ methods, and that slots are invoked from machine-generated code that looks exactly as if you called the slot without specifying the particular class it should be in. Thus a virtual slot acts as you think it should, given the semantics of C++.
The below is sufficient:
class Base : public QObject
{
Q_OBJECT
public:
Base(QObject * src, QObject * parent = 0) : QObject(parent)
{ connect(src, SIGNAL(mySignal), SLOT(mySlot)); }
Q_SLOT virtual void mySlot() {}
};
class Derived : public Base
{
Q_OBJECT
public:
Derived(QObject * src, QObject * parent = 0) : Base(src, parent) {}
void mySlot() Q_DECL_OVERRIDE { ... }
};
You don't need to put the same connect in the subclass constructor. When a SubB object is created, the connect in the Base constructor will connect the right slot (the one overridden in SubB).
I would like to derive all of my widgets from a base class widget that automatically establishes a signal/slot connection between a slot for the class and a (rarely called) signal.
The slot is a virtual function, so that any widgets for which I wish to implement custom functionality can derive from the virtual slot function. In the desired scenario, all my widgets would derive from this base class with the virtual slot, so that by default all of my widget instances would be connected to the desired signal with a slot defined for the object (with default behavior from the base class).
I know that virtual slots are allowed in Qt. However, deriving from two QObject classes is not supported, so that, for example, the following code is disallowed:
class MySignaler : public QObject
{
Q_OBJECT
public:
MySignaler : QObject(null_ptr) {}
signals:
void MySignal();
}
MySignaler signaler;
class MyBaseWidget: public QObject
{
Q_OBJECT
public:
MyBaseWidget() : QObject(null_ptr)
{
connect(&signaler, SIGNAL(MySignal()), this, SLOT(MySlot()));
}
public slots:
virtual void MySlot()
{
// Default behavior here
}
}
// Not allowed!
// Cannot derive from two different QObject-derived base classes.
// How to gain functionality of both QTabWidget and the MyBaseWidget base class?
class MyTabWidget : public QTabWidget, public MyBaseWidget
{
Q_OBJECT
public slots:
void MySlot()
{
// Decide to handle the signal for custom behavior
}
}
As the sample code demonstrates, it seems impossible to gain both the benefits of (in this example) the QTabWidget, and also the automatic connection from the desired signal function to the virtual slot function.
Is there some way, in Qt, to have all my application's widget classes share common base-class slot and connect() functionality while allowing my widgets to nonetheless derive from Qt widget classes such as QTabWidget, QMainWindow, etc.?
Sometimes when inheritance is problematic, one can replace it, or a part of it, with composition.
That's the approach needed in Qt 4: instead of deriving from a QObject, derive from a non-QObject class (MyObjectShared) that carries a helper QObject that is used as a proxy to connect the signal to its slot; the helper forwards that call to the non-QObject class.
In Qt 5, it is not necessary to derive from a QObject at all: signals can be connected to arbitrary functors. The MyObjectShared class remains the same.
Should Qt 4 compatibility be generally useful in other areas of the code, one can use a generic connect function that connects signals to functors in both Qt 4 and Qt 5 (in Qt 4, it would use an implicit helper QObject).
// https://github.com/KubaO/stackoverflown/tree/master/questions/main.cpp
#include <QtCore>
#include <functional>
#include <type_traits>
class MySignaler : public QObject {
Q_OBJECT
public:
Q_SIGNAL void mySignal();
} signaler;
#if QT_VERSION < 0x050000
class MyObjectShared;
class MyObjectHelper : public QObject {
Q_OBJECT
MyObjectShared *m_object;
void (MyObjectShared::*m_slot)();
public:
MyObjectHelper(MyObjectShared *object, void (MyObjectShared::*slot)())
: m_object(object), m_slot(slot) {
QObject::connect(&signaler, SIGNAL(mySignal()), this, SLOT(slot()));
}
Q_SLOT void slot() { (m_object->*m_slot)(); }
};
#endif
class MyObjectShared {
Q_DISABLE_COPY(MyObjectShared)
#if QT_VERSION < 0x050000
MyObjectHelper helper;
public:
template <typename Derived>
MyObjectShared(Derived *derived) : helper(derived, &MyObjectShared::mySlot) {}
#else
public:
template <typename Derived, typename = typename std::enable_if<
std::is_base_of<MyObjectShared, Derived>::value>::type>
MyObjectShared(Derived *derived) {
QObject::connect(&signaler, &MySignaler::mySignal,
std::bind(&MyObjectShared::mySlot, derived));
}
#endif
bool baseSlotCalled = false;
virtual void mySlot() { baseSlotCalled = true; }
};
class MyObject : public QObject, public MyObjectShared {
Q_OBJECT
public:
MyObject(QObject *parent = nullptr) : QObject(parent), MyObjectShared(this) {}
// optional, needed only in this immediately derived class if you want the slot to be a
// real slot instrumented by Qt
#ifdef Q_MOC_RUN
void mySlot();
#endif
};
class MyDerived : public MyObject {
public:
bool derivedSlotCalled = false;
void mySlot() override { derivedSlotCalled = true; }
};
void test1() {
MyObject base;
MyDerived derived;
Q_ASSERT(!base.baseSlotCalled);
Q_ASSERT(!derived.baseSlotCalled && !derived.derivedSlotCalled);
signaler.mySignal();
Q_ASSERT(base.baseSlotCalled);
Q_ASSERT(!derived.baseSlotCalled && derived.derivedSlotCalled);
}
int main(int argc, char *argv[]) {
test1();
QCoreApplication app(argc, argv);
test1();
return 0;
}
#include "main.moc"
To share some code between two QObjects, you could have the QObject as a member of the class,an interposing non-object class that uses generic class that's parametrized only on the base type. The generic class can have slots and signals. They must be made visible to moc only in the immediately derived class - and not in any further derived ones.
Alas, you generally cannot connect any of the generic class's signals or slots in the constructor of the class, since at that point the derived class isn't constructed yet, and its metadata isn't available - from Qt's perspective, the signals and slots don't exist as such. So the Qt 4-style runtime-checked connect will fail.
The compile-time-checked connect will not even compile, because the this pointer it works on has an incorrect compile-time type, and you know nothing about the type of the derived class.
A workaround for Qt-4 style connect only is to have a doConnections method that the derived constructor has to call, where the connections are made.
Thus, let's make the generic class parametric on the base and the derived class as well - the latter is known as the Curiously Recurring Template Pattern, or CRTP for short.
Now you have access to the derived class's type, and can use a helper function to convert this to a pointer to the derived class, and use it in the Qt 5-style compile-time-checked connects.
The Qt 4-style runtime checked connect still needs to be invoked from doConnections. So,if you use Qt 5, that's not an issue. You shouldn't be using Qt 4-style connect in Qt 5 code anyway.
The slots require slightly different treatment depending on whether the class immediately derived from the generic class overrides them or not.
If a slot is virtual and has an implementation in the immediately derived class, you should expose it to moc in the normal fashion - using a slots section or the Q_SLOT macro.
If a slot doesn't have an implementation in the immediately derived class (whether virtual or not), its implementation in the generic class should be made visible to moc only, but not to the compiler - you don't wish to override it, after all. Thus the slot declarations are wrapped in #ifdef Q_MOC_RUN block that is only active when moc is reading the code. The generated code will refer to the generic implementations of the slots.
As we wish to make sure this indeed works, we'll add some booleans to track whether the slots were invoked.
// main.cpp
#include <QtWidgets>
template <class Base, class Derived> class MyGenericView : public Base {
inline Derived* dthis() { return static_cast<Derived*>(this); }
public:
bool slot1Invoked, slot2Invoked, baseSlot3Invoked;
MyGenericView(QWidget * parent = 0) : Base(parent),
slot1Invoked(false), slot2Invoked(false), baseSlot3Invoked(false)
{
QObject::connect(dthis(), &Derived::mySignal, dthis(), &Derived::mySlot2); // Qt 5 style
QObject::connect(dthis(), &Derived::mySignal, dthis(), &Derived::mySlot3);
}
void doConnections() {
Q_ASSERT(qobject_cast<Derived*>(this)); // we must be of correct type at this point
QObject::connect(this, SIGNAL(mySignal()), SLOT(mySlot1())); // Qt 4 style
}
void mySlot1() { slot1Invoked = true; }
void mySlot2() { slot2Invoked = true; }
virtual void mySlot3() { baseSlot3Invoked = true; }
void emitMySignal() {
emit dthis()->mySignal();
}
};
The generic class is very simple to use. Remember to wrap any non-virtual overridden slots in a moc-only guard!
Also recall the general rule that applies to all Qt code: if you have a slot, it should be declared to moc only once. So, if you had a class that further derives from MyTreeWidget or MyTableWidget, you don't want a Q_SLOT or slots macro in front of any necessarily virtual slot overrides. If present, it'll subtly break things. But you definitely want Q_DECL_OVERRIDE.
If you're on Qt 4, remember to call doConnections, otherwise the method is unnecessary.
The particular choice of QTreeWidget and QTableWidget is completely arbitrary, meaningless, and shouldn't be taken to mean that such use makes any sense (it likely doesn't).
class MyTreeWidget : public MyGenericView<QTreeWidget, MyTreeWidget> {
Q_OBJECT
public:
bool slot3Invoked;
MyTreeWidget(QWidget * parent = 0) : MyGenericView(parent), slot3Invoked(false) { doConnections(); }
Q_SIGNAL void mySignal();
#ifdef Q_MOC_RUN // for slots not overridden here
Q_SLOT void mySlot1();
Q_SLOT void mySlot2();
#endif
// visible to the C++ compiler since we override it
Q_SLOT void mySlot3() Q_DECL_OVERRIDE { slot3Invoked = true; }
};
class LaterTreeWidget : public MyTreeWidget {
Q_OBJECT
public:
void mySlot3() Q_DECL_OVERRIDE { } // no Q_SLOT macro - it's already a slot!
};
class MyTableWidget : public MyGenericView<QTreeWidget, MyTableWidget> {
Q_OBJECT
public:
MyTableWidget(QWidget * parent = 0) : MyGenericView(parent) { doConnections(); }
Q_SIGNAL void mySignal();
#ifdef Q_MOC_RUN
Q_SLOT void mySlot1();
Q_SLOT void mySlot2();
Q_SLOT void mySlot3(); // for MOC only since we don't override it
#endif
};
Finally, this little test case shows that it indeed works as desired.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyTreeWidget tree;
MyTableWidget table;
Q_ASSERT(!tree.slot1Invoked && !tree.slot2Invoked && !tree.slot3Invoked);
emit tree.mySignal();
Q_ASSERT(tree.slot1Invoked && tree.slot2Invoked && tree.slot3Invoked);
Q_ASSERT(!table.slot1Invoked && !table.slot2Invoked && !table.baseSlot3Invoked);
emit table.mySignal();
Q_ASSERT(table.slot1Invoked && table.slot2Invoked && table.baseSlot3Invoked);
return 0;
}
#include "main.moc"
This approach gives you the following:
The common code class derives from the base class, and can thus easily invoke or override the behavior of the base class. In this particular example, you can reimplement the QAbstractItemView methods etc.
There is full support for signals and slots. Even though the signals and slots are declared as such in the metadata of the derived class, you can still use them in the generic class.
In this situation you may make use of composition rather than multiple inheritance. Something like this:
class MySignaler : public QObject
{
Q_OBJECT
public:
MySignaler : QObject(NULL) {}
signals:
void MySignal();
}
MySignaler signaler;
class MyBaseWidgetContainer: public QWidget
{
Q_OBJECT
public:
MyBaseWidgetContainer() : QObject(NULL), widget(NULL)
{
connect(&signaler, SIGNAL(MySignal()), this, SLOT(MySlot()));
}
public slots:
virtual void MySlot()
{
// Default behavior here
}
private:
QWidget *widget;
}
class MyTabWidgetContainer : public MyBaseWidgetContainer
{
Q_OBJECT
public:
MyTabWidgetContainer() {
widget = new QTabWidget(this);
QLayout *layout = new QBoxLayout(this);
layout->addWidget(widget);
}
public slots:
void MySlot()
{
// Decide to handle the signal for custom behavior
}
}