This code fails on second Q_ASSERT.
class A : public QObject
{
Q_OBJECT
public:
void function(QObject *receiveOb, const char *slot)
{
Q_ASSERT((bool)connect(this, SIGNAL(mySignal(int)), receiveOb, SLOT(mySlot(int))));
Q_ASSERT(receiveOb->metaObject()->indexOfMethod(slot) != -1);
}
signals:
void mySignal(int param);
};
class MainClass : public QObject
{
Q_OBJECT
A a;
public slots:
void mySlot(int param)
{
param++;
}
public:
MainClass(QObject *papi = Q_NULLPTR) : QObject(papi)
{ }
void doIt()
{
a.function(this, SLOT(mySlot(int)));
}
};
As I can see, if connect is able to lookup the slot method, I could do the same.
What am I doing wrong?
What other checks can I do to find out my mistake?
Related
I'm trying to use static polymorphism instead of dynamics polymorphism with Qt signal/slot mechanism. But I get compile error. What is wrong in my code? What is workaround?
devices.h
#ifndef DEVICES_H
#define DEVICES_H
#include <QtCore>
#include <qdebug.h>
class DeviceController : public QObject
{
Q_OBJECT
public:
explicit DeviceController(QObject *parent = nullptr):QObject(parent){}
virtual ~DeviceController() {}
void doAllDevicesInit(){
emit deviceAInitSignal();
}
signals:
void deviceAInitSignal();
};
template<typename T> class BaseDevice {
public:
void init() {
static_cast<T*>(this)->doInit();
qDebug() << QString("BaseDevice initialized!");
}
};
class DeviceA : public BaseDevice<DeviceA> {
public:
void doInit() {
qDebug() << "DeviceA initialized!";
}
};
#endif // DEVICES_H
main.cpp
#include "devices.h"
int main(int argc, char *argv[])
{
Q_UNUSED(argc);Q_UNUSED(argv);
DeviceA deviceA;
DeviceController deviceController;
QObject::connect(&deviceController,&DeviceController::deviceAInitSignal,
&deviceA, &DeviceA::init);
deviceController.doAllDevicesInit();
return 0;
}
Compile output
Qt5.12.2/5.12.2/gcc_64/include/QtCore/qobjectdefs_impl.h:414:94:
error: invalid static_cast from type ‘QObject*’ to type
‘QtPrivate::FunctionPointer::*)()>::Object*’
{aka ‘BaseDevice*’}
FuncType::template call(static_cast(this_)->function, static_cast(r), a);
Thanks to drescherjm comment, a workaround is as follow
devices.h
...
template<typename T>
class BaseDevice: public QObject {
//Q_OBJECT, Error: Template classes not supported by Q_OBJECT
public:
explicit BaseDevice(QObject *parent = nullptr):QObject(parent){}
virtual ~BaseDevice() {}
void init() {
static_cast<T*>(this)->doInit();
qDebug() << QString("BaseDevice initialized!");
}
};
class DeviceA : public BaseDevice<DeviceA> {
Q_OBJECT
public:
explicit DeviceA(QObject *parent = nullptr):BaseDevice<DeviceA>(parent){}
virtual ~DeviceA() {}
void doInit() {
qDebug() << "DeviceA initialized!";
}
};
#endif // DEVICES_H
I have a very simple base class which defines a normal Q_PROPERTY with READ and NOTIFY (WRITE is not in all dereived implementations possible).
class BaseClass : public QObject {
Q_OBJECT
Q_PROPERTY(QStringList someEntries READ someEntries NOTIFY someEntriesChanged)
public:
explicit BaseClass(QObject *parent = Q_NULLPTR) : QObject(parent) {}
virtual ~BaseClass() {}
virtual QStringList someEntries() const = 0;
void processEntries() {
for(auto entry : someEntries()) {
//process entry
}
}
Q_SIGNALS:
void someEntriesChanged(const QStringList &someEntries);
};
Now I have 2 dereiving classes, of which one supports only one entry (SingleItem) and the other one supports multiple entries (MultiItem).
SingleItem converts the one QString-member to a QStringList when requested and allows setting it using the differently called property (theEntry):
class SingleItem : public BaseClass {
Q_OBJECT
Q_PROPERTY(QString theEntry READ theEntry WRITE setTheEntry NOTIFY theEntryChanged);
public:
explicit SingleItem(QObject *parent = Q_NULLPTR) : BaseClass(parent) {}
virtual ~SingleItem() {}
const QString &theEntry() const { return m_theEntry; }
void setTheEntry(const QString &theEntry) {
if(m_theEntry != theEntry)
Q_EMIT theEntryChanged(m_theEntry = theEntry);
}
// BaseClass interface
QStringList someEntries() const Q_DECL_OVERRIDE
{ return QStringList { m_theEntry }; }
Q_SIGNALS:
void theEntryChanged(const QString &theEntry);
private:
QString m_theEntry;
};
MutliItem stores a QStringList (for someEntries). I would like to write into that QStringList using the property defined in BaseClass. But I dont know how to add the WRITE option to the already defined Q_PROPERTY.
class MultiItem : public BaseClass {
Q_OBJECT
//Somehow add the setSomeEntries method to the property?
public:
explicit MultiItem(QObject *parent = Q_NULLPTR) : BaseClass(parent) {}
virtual ~MultiItem() {}
void setSomeEntries(const QStringList &someEntries) {
if(m_someEntries != someEntries)
Q_EMIT someEntriesChanged(m_someEntries = someEntries);
}
// BaseClass interface
QStringList someEntries() const Q_DECL_OVERRIDE{ return m_someEntries; }
private:
QStringList m_someEntries;
};
I cannot add the WRITE option in BaseClass because it would violate the meaning. Not every object of BaseClass allows setting that property but objects of MultiItem should be written only using that property.
Is this somehow possible without declaring a WRITE option to a virtual ... = 0 in BaseClass that just logs a warning when called in SingleItem?
I have a problem with the QServiceManager.
QServiceManager manager;
CFoo bar;
QList<QServiceInterfaceDescriptor> ServiceList = manager.findInterfaces(SERVICE_NAME);
for(int i = 0; i < ServiceList.length(); i++)
{
bool accessGranted = false;
QServiceInterfaceDescriptor descriptor = ServiceList[i];
if (descriptor.interfaceName() == INTERFACE)
{
bar = manager.loadLocalTypedInterface<IFoo>(descriptor, accessGranted);
if (NULL == bar && false == accessGranted)
{
connect(bar, SIGNAL(signal()),
this, SLOT(slot()));
}
}
}
I can do function calls specified in the interface IFoo on bar, like:
bar.function()
and I see that the remote object foo is receiving the function call, but when I send the signal remotely:
class IFoo : public QObject
{
Q_OBJECT
public:
virtual void function() = 0:
signals:
void signal();
};
class CFoo : public IFoo`
{
Q_OBJECT
void function()
{
emit signal();
}
};
it is not received. The function slot() is never called. I checked that the connect function gets called and returns TRUE. Can anybody pinpoint what I am doing wrong?
Cant use signals and slots if your class isnt qobject
#include<QObject>
class CFoo : public QObject, public IFoo
{
Q_OBJECT
signals:
void signal();
public:
void function()
{
emit signal();
}
};
I have two classes, MyClass and Widget. Below is the MyClass class and from my Widget class i want to get the str variable. How is that done?
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
void fetch();
public slots:
void replyFinished(QNetworkReply*);
private:
QNetworkAccessManager* m_manager;
};
MyClass::MyClass()
{
m_manager = new QNetworkAccessManager(this);
connect( m_manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
}
void MyClass::fetch()
{
m_manager->get(QNetworkRequest(QUrl("http://stackoverflow.com")));
}
void MyClass::replyFinished(QNetworkReply* pReply)
{
QByteArray data=pReply->readAll();
QString str(data);
//this str should be available in my widget class
}
EDIT: Here is a the important part of the widget
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//here str should be accessed
}
If you want the str variable from your function available to classes or other functions, here are two choices:
Return it from the function.
Declare a variable in MyClass to hold the string and set the
variable to the value.
Case 1: Returning from a function
QString MyClass::replyFinished(...)
{
QString str(data);
return data;
}
Case 2: Declare a class member variable
class MyClass
{
public:
QString m_replyStr;
};
//...
void MyClass::replyFinished(...)
{
QByteArray data = pReply->readAll();
m_replyStr = data;
}
Modifying your question with an example of what you want to do would be very helpful.
You can emit a signal with str as argument and connect it to a slot in your widget. Then you can do what you want with it.
This way you will preserve the event oriented design and you have not need to control if str exists. Simply when it's ready the slot will handle it.
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass();
void fetch();
public slots:
void replyFinished(QNetworkReply*);
signals:
void strReplyReady(QString str);
private:
QNetworkAccessManager* m_manager;
};
...
void MyClass::replyFinished(QNetworkReply* pReply)
{
QByteArray data=pReply->readAll();
QString str(data);
emit strReplyRead(str);
}
your Widget
class MyWidget : public QWidget
{
//public members
...
public slots:
void readReply(QString str);
}
void MyWidget::readReply(QString str){
//do what you want with str
}
in the main.cpp you do the connect with the static member of QObject
QObject::connect(myClassPointer,SIGNAL(strReplyReay(QString)) ,
myWidgetPointer,SLOT(readReply(QString)));
In my code I have:
template<class Ui_Dialog>
QDialog* Base_Dialog<Ui_Dialog>::set_caller(QDialog *new_caller)
{
QDialog* old_caller = caller_;
caller_ = new_caller;//Here I'm trying to set this to new caller
return old_caller;
}
but after setting the caller to new caller and exiting from this fnc, when I call caller I'm still getting the old caller instead of new one, as if no changes were made. Why?
EDIT:
//caller is defined in a following way:
class Main_Dialog : public Base_Dialog<Ui::Main_Dialog> {};
EDIT 2:
The interesting thing is that if instead of public Base_Dialog I alter Base_Dialog to non-template and define Main_Dialog as:
class Main_Dialog : public Base_Dialog, private Ui::Main_Dialog {};
Then it works as intended. Why?!
EDIT 3:
class Main_Dialog;
template<class Ui_Dialog>
class Base_Dialog : public QDialog, protected Ui_Dialog
{
// Q_OBJECT //no signals/slots
protected:
Main_Dialog* main_dlg_;
QDialog* caller_;
public:
Base_Dialog(Main_Dialog *main_dlg, QDialog *caller, QWidget *parent = nullptr);
QDialog* set_caller(QDialog *);
QDialog* clear_caller();
Main_Dialog* clear_main_dlg();
};
/*----------------*/
#include "Main_Dialog.hpp"
template<class Ui_Dialog>
Base_Dialog<Ui_Dialog>::Base_Dialog(Main_Dialog *main_dlg,QDialog *caller, QWidget *parent):
QDialog(parent),
main_dlg_(main_dlg),
caller_(caller)
{
//setupUi(this);
}
#include <QtDebug>
template<class Ui_Dialog>
QDialog* Base_Dialog<Ui_Dialog>::set_caller(QDialog *new_caller)
{
QDialog* old_caller = caller_;
caller_ = new_caller;
return old_caller;
}
#include "_1Dialog.hpp"
#include "_2Dialog.hpp"
class Main_Dialog : public Base_Dialog<Ui::Main_Dialog>
{
Q_OBJECT
QSet<QDialog*>* dialogs_;
private:
template<class Dialog,class Caller>
bool already_created_(Caller*&, QDialog*& already_exists)const;
template<class Dialog,class Caller, class Parent>
QDialog* create_(Caller*,Parent*);
/*template<class Dialog>
void show_();*/
/* template<class Dialog,class Caller>
QDialog* find_(Caller*)const;*/
public:
explicit Main_Dialog(QWidget *parent = 0);
template<class Dialog,class Caller>
QDialog* get_dialog(Caller*& caller);
public slots:
void _1clicked();
void _2clicked();
};
template<class Dialog,class Caller>
bool Main_Dialog::already_created_(Caller*& caller,QDialog*& already_exists)const
{/*the already_exists is introduced here in order to remove repetions of code and
searching*/
auto beg = dialogs_->begin();
auto end = dialogs_->end();
while(beg != end)
{
if(dynamic_cast<Dialog*>(*beg))
{
already_exists = *beg;
static_cast<Base_Dialog*>(already_exists)->set_caller(caller);
return true;
}
++beg;
}
return false;
}
template<class Dialog,class Caller, class Parent>
QDialog* Main_Dialog::create_(Caller *caller, Parent *parent)
{
return (*dialogs_->insert(new Dialog(this,caller,parent)));
}
template<class Dialog,class Caller>
QDialog* Main_Dialog::get_dialog(Caller *&caller)
{
QDialog* already_exists = nullptr;
if (already_created_<Dialog>(caller,already_exists))
{
return already_exists;
}
else
{
return create_<Dialog>(caller,this);
}
#include "Base_Dialog.hpp"
#include "ui__1Dialog.h"
class Main_Dialog;
class _1Dialog : public Base_Dialog<Ui::_1Dialog>
{
Q_OBJECT
public:
explicit _1Dialog(Main_Dialog* main_dlg, QDialog*caller, QWidget *parent = 0);
private slots:
void _2clicked();
void caller_clicked();
void main_clicked();
};
#endif // _1DIALOG_HPP
#include "_1Dialog.hpp"
#include "_2Dialog.hpp"
#include "Main_Dialog.hpp"
_1Dialog::_1Dialog(Main_Dialog* main_dlg, QDialog*caller, QWidget *parent) :
Base_Dialog(main_dlg,caller,parent)
{
setupUi(this);
}
void _1Dialog::_2clicked()
{
this->hide();
main_dlg_->get_dialog<_2Dialog>(this)->show();
}
void _1Dialog::caller_clicked()
{
this->hide();
caller_->show();
}
void _1Dialog::main_clicked()
{
this->hide();
main_dlg_->show();
}
}
#endif // MAIN_DIALOG_HPP
I think your problem not in set_caller. Next works for me:
#include <iostream>
using namespace std;
class QDialog {
int i;
public:
QDialog() : i(0) {}
QDialog(int i) : i(i) {}
void me() { cout << i << endl; }
};
namespace UI {
class Main_Dialog {}; // EDITED
}
template<class Ui_Dialog>
class Base_Dialog : public QDialog, protected Ui_Dialog
{
QDialog* caller_;
public:
QDialog* set_caller(QDialog *new_caller);
QDialog* get_caller() {return caller_; } ;
};
template<class Ui_Dialog>
QDialog* Base_Dialog< Ui_Dialog>::set_caller(QDialog *new_caller)
{
QDialog* old_caller = caller_;
caller_ = new_caller;//Here I'm trying to set this to new caller
return old_caller;
}
class Main_Dialog : public Base_Dialog<UI::Main_Dialog> {
public:
};
int main()
{
QDialog q1(1);
QDialog q2(2);
Main_Dialog md;
md.set_caller(&q1);
md.get_caller()->me();
md.set_caller(&q2);
md.get_caller()->me();
return 0;
}