I have got a super class Common, which inherits from QObject. Then I have got a class Item, which inherits from Common.
Common.h
class Common : public QObject {
Q_OBJECT
public:
// some methods
};
Item.h
class Item : public Common {
Q_OBJECT
public:
// some methods
void test(QString value);
};
Item.cpp
void Item::test(QString value) {
qDebug() << value;
}
I want to use QMetaObject::invokeMethod to dynamically call a function.
So I implemented a test function in the Item class, which takes exactly one string.
Item* item = new Item();
QMetaObject::invokeMethod(item, "test", Qt::DirectConnection, Q_ARG(QString, "1234"));
This does not work. I get the following error: QMetaObject::invokeMethod: No such method Common::test(QString), which is perfectly okay and fine, because the Common class has no test function.
How can I tell QMetaObject::invokeMethod, that it should call the method from the Item class?
QMetaObject::invokeMethod can only invoke methods known to the Qt meta object system. These are slots and "invokable" functions, the latter being functions with the keyword Q_INVOKABLE before them.
So either write:
class Item : public Common {
Q_OBJECT
public slots:
// ^^^^^
void test(QString value);
};
or:
class Item : public Common {
Q_OBJECT
public:
Q_INVOKABLE void test(QString value);
//^^^^^^^^^
};
Related
Is it possible to call a static method?
I am using:
QMetaObject::invokeMethod(this
,strThread.toLatin1()
,Qt::DirectionConnection
,Q_ARG(clsThread*, this));
This works, however I want to call a static method and that doesn't work, is it possible to invoke a static method?
I've tried assigning to strThread: "clsScriptHelper::threadFun", but this doesn't work.
I couldn't get the static method to work, so I've implemented an alternative solution. In my derived thread class I added a member which has the type:
QObject* mpobjClass;
I then added a method to set this:
void setClassPtr(QObject* pobjClass) { mpobjClass = pobjClass; }
My invoke now looks like this:
QMetaObject::invokeMethod(mpobjClass
,strThread.toLatin1()
,Qt::DirectConnection
,Q_ARG(clsThread*, this));
This works for me.
yes, you can, but the method must be annotates as invocable i.e Q_INVOKABLE see what qt documented about it...
Foo obj;
QMetaObject::invokeMethod(&obj, "amSomething", Qt::DirectConnection);
and Foo should look like:
class Foo : public QObject
{
Q_OBJECT
public:
explicit Foo(QObject *parent = nullptr);
Q_INVOKABLE static void amSomething(){ qDebug() << "am in static";}
signals:
public slots:
};
Why would you do that? invokeMethod is for when the object has a dynamic type and you got an instance and want to call a method on that instance in spite of not knowing anything about the type.
What you seem to want to do is to dispatch static methods based on a string name. That's not hard and doesn't require invokeMethod:
class Class {
public:
static void method1();
static void method2();
static void dispatchByName(const char *name) {
if (QByteArrayLiteral("method1") == name) method1();
else if (QByteArrayLiteral("method2") == name) method2();
}
};
I am not experienced in OOP. I am developing an application using C++ and Qt. I have implemented 2 classes, base one and the one that inherits from it. Then I have added virtual methods for both and everything worked. But then I realized that I don't think it should... Here is the example:
This is my base class :
namespace Ui {
class CGenericProject;
}
class CGenericProject : public QDialog
{
Q_OBJECT
public:
explicit CGenericProject(QWidget *parent = 0);
~CGenericProject();
EMeasures_t type();
private:
Ui::CGenericProject *ui;
virtual void initPlot();
protected:
QCustomPlot* customPlot;
QVector<double> m_x;
QVector<double> m_y;
EMeasures_t m_type;
};
It has a virtual method called initPlot and it looks like this:
void CGenericProject::initPlot()
{
customPlot = ui->workPlot;
customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes );
customPlot->setFocusPolicy(Qt::ClickFocus);
customPlot->xAxis->setAutoTickStep(false);
customPlot->yAxis->setAutoTickStep(false);
customPlot->xAxis->setTickStep(100);
customPlot->yAxis->setTickStep(100);
customPlot->xAxis->setRange(0, 1000);
customPlot->yAxis->setRange(0, 1000);
}
And then i have a class that derives it:
class CEisProject : public CGenericProject
{
public:
CEisProject();
~CEisProject();
private:
virtual void initPlot();
void exampleEisMethod();
};
its initPlot is here:
void CEisProject::initPlot()
{
// give the axes some labels:
customPlot->xAxis->setLabel("Re [Ohm]");
customPlot->yAxis->setLabel("- Im [Ohm]");
customPlot->replot();
}
This is how i create the object:
CGenericProject* test = new CEisProject();
Now, when the initPlot() method is called, first the initPlot() from base class CGenericProject is called and then initPlot() from CEisProject is called. I wanted this functionality, where I can predefine some stuff in generic class and then add specific stuff in the childs.
But when I think of it, shouldn't initPlot() be calles exclusevily? I mean, shouldn't the method be called from base class or child class, instead of both, one after another? I have noticed this after reading this answer.
Constructors:
CGenericProject::CGenericProject(QWidget *parent) :
QDialog(parent),
ui(new Ui::CGenericProject)
{
ui->setupUi(this);
initPlot();
m_x.clear();
m_y.clear();
}
CEisProject::CEisProject()
{
m_type = EMeasures_t::eEIS;
initPlot();
}
You did not show the definition of the constructors, just their declaration. But I'm pretty sure the constructor definitions contain the answer to your question.
You may not be aware that the derived class constructor calls the base class constructor before directing virtual functions to the derived class. So a virtual function called in the base class construction (of an object which will soon be derived class) gets the base class definition of that virtual function.
Also, your constructor should be like:
// File .h
CEisProject(QWidget *parent = 0);
// File .cpp
CEisProject::CEisProject(QWidget *parent) : CGenericProject(parent)
{
...
}
or you won't be able to parent your derived widgets.
I have a QMainWindow class.
class MainWindow: public QMainWindow
{
Q_OBJECT
...
public:
void insertVector();
...
};
and I have class SqlStorage to make operation with Data Base.
class SqlStorage : public QObject {
Q_OBJECT
...
public:
static void loadSQL();
...
};
In insertVector() method I try to asynchronously write in DB.
void MainWindow::insertVector()
{
SqlStorage* _sqlStorage = new SqlStorage;
QFuture<void> future = QtConcurrent::run(_sqlStorage, &SqlStorage::loadSQL);
}
But when I try to compile, I have error that: "term does not evaluate to a function taking 1 arguments".
Where is my problem?
When you want to call static member functions with QtConcurrent::run, you do it the same way you call a regular non-member function. Only difference is you include the class scope in it. Like this:
QFuture<void> future = QtConcurrent::run(SqlStorage::loadSQL);
This may be due to me not entirely understanding how interfaces in C++ work, but here we go:
I have base interface for a property class in QT5.
class IBaseProperty
{
public:
virtual ~IBaseProperty() {}
// Returns the property key as a string.
virtual QString getKey() = 0;
// Returns the property value as a raw string.
virtual QString getValueRaw() = 0;
// Sets the property key as a string.
virtual void setKey(QString key) = 0;
// Sets the property value as a raw string.
virtual void setValueRaw(QString value) = 0;
};
I also have a templated interface extension to make it easier to subclass properties that handle more specific data types.
template <class T>
class IProperty : public IBaseProperty
{
public:
virtual ~IProperty() {}
// Classifies a property with a Property_t identifier.
virtual Property_t getPropertyType() = 0;
// Returns the property value as the specified type.
// Bool is true if conversion was successful.
virtual T getValue(bool* success) = 0;
// Sets the property value as the specified type.
virtual void setValue(T value) = 0;
// Returns whether the current value can be converted correctly
// to the specified type.
virtual bool canConvert() = 0;
};
My base property (implementing only IBaseProperty) looks like this:
class BaseProperty : public QObject, public IBaseProperty
{
Q_OBJECT
public:
explicit BaseProperty(QObject *parent = 0, QString key = "", QString value = "");
virtual QString getKey();
virtual QString getValueRaw();
public slots:
virtual void setKey(QString key);
virtual void setValueRaw(QString value);
protected:
QPair<QString, QString> m_Property; // KV pair this property holds.
};
I subclass this to make a string property - obviously the base property can just return strings, but I wanted to keep the same function format between string/int/float/etc. properties by allowing getValue in all of them. GetValue in this case simply calls getValueRaw to return the value.
class StringProperty : public BaseProperty, public IProperty<QString>
{
Q_OBJECT
public:
explicit StringProperty(QObject *parent = 0, QString key = "", QString value = "");
virtual inline Property_t getPropertyType() { return Prop_String; }
virtual QString getValue(bool* success);
virtual bool canConvert();
public slots:
virtual void setValue(QString value);
};
The ambiguity occurs when I implement getValue and setValue:
inline QString StringProperty::getValue(bool* success)
{
*success = canConvert();
return getValueRaw(); // This line causes the ambiguity.
}
The compiler complains:
C2385: Ambiguous access of 'getValueRaw': could be the 'getValueRaw'
in base 'BaseProperty' or could be the 'getValueRaw' in base
'IBaseProperty'.
I'm not entirely sure what to do in this situation - I would have thought that IBaseProperty being a pure virtual class would mean that the function would not be able to be called from this point anyway, so would only be called from where it was implemented (BaseProperty). What would be the correct course of action to take to fix this? I'm not sure from which base class I should be calling the function.
For the first look, it seems to be classical diamond problem or diamond inheritance
String property inherits from BaseProperty and IProperty, and both of them have same base class IBaseProperty. That's why there is an ambiguity.
The problem is that a StringProperty contains two base class subobjects of type IBaseProperty. You probably want just one IBaseProperty, in which case you need to use virtual inheritance. (It's often a good idea for "interfaces" to be virtual base classes.)
template <class T>
class IProperty : public virtual IBaseProperty
{ /*...*/ };
class BaseProperty : public virtual IBaseProperty, public QObject
{ Q_OBJECT; /*...*/ };
class StringProperty : public virtual IProperty<QString>, public BaseProperty
{ Q_OBJECT; /*...*/ };
Recommended reading: C++ FAQ 25.8 through 25.15.
Is it possible to have a template class, which inherit from QObject (and has Q_OBJECT macro in it's declaration)?
I would like to create something like adapter for slots, which would do something, but the slot can take arbitrary number of arguments (number of arguments depends on the template argument).
I just tried doing it, and got linker errors. I guess gmake or moc is not getting called on this template class. Is there a way to do this? Maybe by explicitly instantiating templates?
It is not possible to mix template and Q_OBJECT but if you have a subset of types you can list the slots and signals like this:
class SignalsSlots : public QObject
{
Q_OBJECT
public:
explicit SignalsSlots(QObject *parent = 0) :
QObject(parent) {}
public slots:
virtual void writeAsync(int value) {}
virtual void writeAsync(float value) {}
virtual void writeAsync(double value) {}
virtual void writeAsync(bool state) {}
virtual void writeAsync(svga::SSlideSwitch::SwitchState state) {}
signals:
void readAsynkPolledChanged(int value);
void readAsynkPolledChanged(float value);
void readAsynkPolledChanged(double value);
void readAsynkPolledChanged(bool state);
void readAsynkPolledChanged(svga::SSlideSwitch::SwitchState state);
};
...
template <class T>
class Abstraction : public SignalsSlots
{...
Taking into account some restrictions: you can.
First please became familiar (if already not) https://doc.qt.io/archives/qq/qq16-dynamicqobject.html. - it will help to imlement it.
And about restrictions: you can have a template QObject class i.e. template class derived from QObject, but:
Do not tell the moc to compile it.
Q_OBJECT is just a macro and you have to replace it by it real
content which is virtual interface and something else :)
Implement QMetaObject activation (above mentioned virtual interface
and be caution with object info data, which is also come from
Q_OBJECT) and some else functionality and you will have template
QObject (even with template slots)
But as I managed to catch the one draw back - it is not possible to
simply use this class as a base for another class.
There are some other drawbacks - but I think the detail
investigation will show you them.
Hope this will helpful.
It is still not possible to mix templates and Q_OBJECT but depending on your use case you may use the new 'connect'-syntax. This allows at least the usage of template-slots.
Classical non-working approach:
class MySignalClass : public QObject {
Q_OBJECT
public:
signals:
void signal_valueChanged(int newValue);
};
template<class T>
class MySlotClass : public QObject {
Q_OBJECT
public slots:
void slot_setValue(const T& newValue){ /* Do sth. */}
};
Desired usage but not compilable:
MySignalClass a;
MySlotClass<int> b;
QObject::connect(&a, SIGNAL(signal_valueChanged(int)),
&b, SLOT(slot_setValue(int)));
Error: Template classes not supported by Q_OBJECT (For
MySlotClass).
Solution using new the 'connect'-syntax:
// Nothing changed here
class MySignalClass : public QObject {
Q_OBJECT
public:
signals:
void signal_valueChanged(int newValue);
};
// Removed Q_OBJECT and slots-keyword
template<class T>
class MySlotClass : public QObject { // Inheritance is still required
public:
void slot_setValue(const T& newValue){ /* Do sth. */}
};
Now we can instantiate desired 'MySlotClass'-objects and connect them to appropriate signal emitters.
MySignalClass a;
MySlotClass<int> b;
connect(&a, &MySignalClass::signal_valueChanged,
&b, &MySlotClass<int>::slot_setValue);
Conclusion: Using template-slots is possible. Emitting template signals is not working since a compiler error will occur due to missing Q_OBJECT.
I tried explicitly instantiating templates, and got this :
core_qta_qt_publisheradapter.hpp:96: Error: Template classes not supported by Q_OBJECT
I guess that answers my question.
EDIT
Actually, if I place whole template class definition in the header, then the qt preprocessor doesn't process it, and then I get linker errors. Therefore it must be possible to do it, if I add missing methods.
EDIT #2
This library did exactly what I wanted - to use a custom signal/slot mechanism, where the slot has not-defined signature.