Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?
-
Hi,
as the title already says, I'm working on a widget, which I'm building from scratch.
I followed Qt's pImpl design, therefore I have my ownWidgetPrivate
class (+d_ptr
) and make use of theQ_xxxxx
macros as much as I can (Q_D
,Q_Q
, etc.).The way my widget should work requires it to have a
QTimer
.
This timer and the totimeout()
connected function I want to hide so that they are not visible/accessible from the "public" API (Widget.h
). They are both part of myWidgetPrivate
class.So now my questions:
How to make this work?
I've read aboutQ_PRIVATE_SLOT
but at the same time everywhere is written that it is only there because of compatibility to Qt4 and C++ Standard below C++11. In Qt5 and above it's allegedly not needed...I also found this pretty old guide:
which works, when built exactly like it's written there.
I seebob!
bob!
bob!
bob!
bob!every second on timeout in my output pane.
However, adapting it to my project does not seem to work.
I don't use Qt4 (obviously) and I don't have inline functions.
My structure looks like:- widget.h
- widget_p.h
- widget.cpp (
Widget
andWidgetPrivate
both share this code file)
In addition I don't use the String-based connections...
This altogether results in a bunch of errors, which make my code not compile :(
Any help is appreciated :)
H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h::215::-1">H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: note: template argument deduction/substitution failed: H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h: In substitution of 'template<class Func1, class Func2> static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer<Prototype>::Object*, Func1, const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType*, Func2&&, Qt::ConnectionType) [with Func1 = void (QTimer::*)(QTimer::QPrivateSignal); Func2 = void (WidgetPrivate::*)()]': H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:215:9: error: no type named 'ContextType' in 'struct QtPrivate::ContextTypeForFunctor<void (WidgetPrivate::*)(), void>' H:/dev/Qt/6.7.0/mingw_64/include/QtCore/qobject.h</a>:264:9: note: candidate: 'template<class Func1, class Func2> static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer<Prototype>::Object*, Func1, Func2&&)' 264 | connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot) | ^~~~~~~
-
You can not use the pmf connect syntax with the pImpl idiom. One way is to derive the private class from QObject or use a lambda.
Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)
-
Hi,
Can you share a minimal reproducer ?
That would make things easier to spot the issue. -
You can not use the pmf connect syntax with the pImpl idiom. One way is to derive the private class from QObject or use a lambda.
Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)
-
Here is a stripped-down example to show what I'm doing:
widget.h
(Public API)#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QScopedPointer> class WidgetPrivate; class Widget: public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr); ~Widget(); Q_SIGNALS: // public signal which should fire when background logic // and timer timeout occur void timespanPassed(); protected: void mousePressEvent(QMouseEvent *ev); void mouseReleaseEvent(QMouseEvent *ev); protected: Widget(WidgetPrivate &dd, QWidget *parent = nullptr); protected: /*const*/ QScopedPointer<WidgetPrivate> /*const*/ d_ptr; private: Q_DECLARE_PRIVATE(Widget) Q_DISABLE_COPY(Widget) // Is this needed?! If not, how else to do this Q_PRIVATE_SLOT(d_func(), void foo()) }; #endif // WIDGET_H
widget_p.h
#ifndef WIDGET_P_H #define WIDGET_P_H #include <QDebug> class Widget; class QTimer; class WidgetPrivate { Q_DECLARE_PUBLIC(Widget) Q_DISABLE_COPY(WidgetPrivate) public: WidgetPrivate(Widget *q); virtual ~WidgetPrivate(); void init(); Widget *const q_ptr; // this is the timer I want to use internally QTimer *timer; // internal function which causes a signal in public API to fire // (after some processing) void emitTimespanPassed(); private: void foo(); // private internal slot?! }; #endif // WIDGET_P_H
widget.cpp
#include "widget.h" #include "widget_p.h" #include <QPainter> #include <QPointer> #include <QMouseEvent> #include <QTimer> // needed?! #include "moc_widget.cpp" WidgetPrivate::WidgetPrivate(Widget* q) : q_ptr(q) , timer(nullptr) { } WidgetPrivate::~WidgetPrivate() { } void WidgetPrivate::init() { Q_Q(Widget); // init public API settings here timer = new QTimer(q); timer->setSingleShot(true); // interval is changable in API timer->setInterval(3000); // 3s // connect here?! // if so, how? // QObject::connect(timer, &QTimer::timeout, this, [&](){ foo(); }); q->setMouseTracking(true); } void WidgetPrivate::emitTimespanPassed() { Q_Q(Widget); QPointer<Widget> guard(q); // some conditions and logic to emit public signal... // [ ... ] // emit widget signal if (guard) emit q->timespanPassed(); } // function connected to QTimer::timeout void WidgetPrivate::foo() { // if (xyz) { // some logic // cause emit of public signal in Widget API emitTimespanPassed(); // } } // END INTERNAL PRIVATE // ####################################################################################### // ####################################################################################### // ####################################################################################### // BEGIN PUBLIC API Widget::Widget(QWidget *parent) : QWidget(parent) , d_ptr(new WidgetPrivate(this)) { Q_D(Widget); d->init(); // OR connect here?!?!? // better here or in WidgetPrivate::init()? // no error but signal not emitted QObject::connect(d->timer, SIGNAL(timeout()), this, SLOT(foo())); // does NOT work, crashes with mentioned error QObject::connect(d->timer, &QTimer::timeout, d_func(), &WidgetPrivate::foo); } Widget::Widget(WidgetPrivate &dd, QWidget *parent) : Widget(parent) , d_ptr(&dd) { Q_D(Widget); d->init(); } void Widget::mousePressEvent(QMouseEvent *ev) { Q_D(Widget); d->timer->start(); QWidget::mousePressEvent(ev); } void Widget::mouseReleaseEvent(QMouseEvent *ev) { Q_D(Widget); d->timer->stop(); QWidget::mouseReleaseEvent(ev); } // END PUBLIC API // ####################################################################################### // ####################################################################################### // #######################################################################################
main.cpp
#include <QApplication> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QObject::connect(&w, &Widget::timespanPassed, &w, [&](){ qDebug() << "Timespan passed"; }); w.show(); return a.exec(); }
This is my code... I'm unsure about many things there.
Where to connect my internal function?
Is the#include "moc_widget.cpp"
needed? Some sources state that it's needed to tellmoc
to "moc" theWidgetPrivate
part?!Very much appreciated if any of you can shed a light on this procedure for me :))
I hope you get what I'm trying to do... but maybe there are even some structural flaws?! Can't tell actually :( -
As I said the pmf syntax needs a QObject as sender and receiver.
-
@Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:
As I said the pmf syntax needs a QObject as sender and receiver.
Ah sorry, while I was editing my example code I missed your reply :)
Oh wow didn't know that :0
So the( SIGNAL(), SLOT() )
connection stuff is not 100% identical in terms of use-case and compatibility with the Functor-based connections?
Good to know.@Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:
Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)
Yeah, I checked the Qt Private sources here and there while I was building my widget to see how it's done. Haven't seen this exact part, but will visit it again ;-)
Since I don't want to include the Qt Private source completely (which would make my widget highly dependent on a single Qt version), I made my ownWidgetPrivate
based onQWidgetPrivate
"style" but without inheriting it... so noQObjectData
,QObjectPrivate
and whatnot... for me ;-)Edit:
@Christian-Ehrlicher said in Widget from scratch using pImpl, how to Q_PRIVATE_SLOT?:
Maybe you can also take a look on QObjectPrivate::connect() in qobject_p.h (when someone asks - I don't know this function... 🙂)
This function?
What is there to see? Other thanQObjectPrivate::connect
.
You mean the use of twoQObject
Functors?when someone asks - I don't know this function
Nobody will know 🤐
One way is to derive the private class from QObject or use a lambda.
If one way to make it work is using the
SIGNAL
/SLOT
keywords again, I will probably try it.
Tried lambda, did not work either :/ Or maybe I was doing something wrong.Edit_II:
I made my actual code work, using String-based connections while I moved the connect statement to the
WidgetPrivate::init()
function and hid theQTimer
also behind theWidgetPrivate
data.
Not 100% pleased, tho :D
Is it a good idea to make the Impl class aQObject
?!
What's your recommendation?@SGaist oh lol even missed your comment too :D Was about to add the code anyway :)
-
-
-