Yesterday I was asked to recreate a regular QT form using QML (which was my first attempt ever using QLM). Everything was going well until I tried using c++ methods in the QML. This is obviously not the original code, but the scenario looks something like this:
I have a super class deriving from QObject, with some properties, methods and even virtual methods:
class SuperClass : public QObject {
Q_OBJECT
Q_PROPERTY(QString someProperty READ someProperty WRITE setSomeProperty)
protected:
QString m_someProperty;
public:
QString someProperty(void){return m_someProperty;} //get method
void setSomeProperty(QString newValue){m_someProperty = newValue;} //set method
Q_INVOKABLE virtual QString printSomething(void) = 0;
}
And then I have a class deriving from the SuperClass (like a specialization) with some more specific properties and methods and of course the virtual methods implementations and stuff:
class DerivedClass : public SuperClass {
Q_PROPERTY(QString someSpecificProperty READ someSpecificProperty WRITE setSomeSpecificProperty)
private:
QString m_someSpecificProperty;
public:
QString specificProperty(void){return m_someSpecificProperty;} //get method
void someSpecificProperty(QString newValue){m_someSpecificProperty = newValue;} //set method
QString printSomething(void){return QString("Something!");} //SuperClass virtual method
Q_INVOKABLE QString printSomethingSpecific(void){return QString("Something Specific!");}
}
OK, this is it! Now assuming that DerivedClass is instantiated and added to the QML context properly under the name of "DrvClass" for example and that I have some QML control like a TextField which has a 'text:' property:
text: DrvClass.someProperty
using MasterClass' properties, it works just fine.
text: DrvClass.printSomething()
even using virtual methods from MasterClass' which are implemented in the derived class works fine. but...
text: DrvClass.someSpecificProperty
doesn't work and I get something like "Unable to assign [undefined] to QString"
text: DrvClass.printSomethingSpecific()
also doesn't work! "TypeError: Property 'printSomethingSpecific' of object SuperClass() is not a function" And the weird part is that it says that it's not a function from the SuperClass, being the instantiated class the Derived one!
I've looked for similar errors, but most of the time is from people who just forgot to include the Q_OBJECT macro... Mine's there for sure!
It seems that QML doesn't like much classes deriving from other classes that derive from QObjects :-/ Probably something to do with the meta-object compiler who only looks for invokable methods where it finds the Q_OBJECT macro and not on it's subclasses!
So what you guys think the solution for this might be?
I could just add the Q_OBJECT macro to the DerivedClasses instead of the SuperClass, but I really need the SuperClass to be a QObject because of signals and stuff! So is there some other macro I have to add to the DerivedClass for the moc to 'see' it?
Or is this just the fruit of inexperience and I'm doing a dumb mistake somewhere?
DerivedClass is missing Q_OBJECT macro (it is not inherited!).
Then simply run qmake again on your project & compile: it should work.
Related
I am reading other people's code and see this:
class UAVItem:public QObject,public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
...
But I didn't see they are using any sort of plug-in in this program.
Therefore, can I just remove the line:
Q_INTERFACES(QGraphicsItem)
?
If you have a class Derived which inherits from a class Base, which in turn inherits from QObject, and both Derived and Base contain the Q_OBJECT macro, then qobject_cast can be used to safely cast from a pointer (or reference) to Base, to a pointer (or reference) to Derived, similar to dynamic_cast in standard C++ but without RTTI.
If Base does not inherit from QObject, then qobject_cast can still be used in this way, but only if Base has a corresponding Q_DECLARE_INTERFACE macro and Derived contains Q_INTERFACES(Base).
In your case, Q_INTERFACES(QGraphicsItem) being present in UAVItem means that qobject_cast can be used to cast from a pointer (or reference) to QGraphicsItem to a pointer (or reference) to UAVItem, despite QGraphicsItem not inheriting from QObject.
From reference doc,
class ToolInterface
{
public:
virtual QString toolName() const = 0;
};
Q_DECLARE_INTERFACE(ToolInterface, "in.forwardbias.tool/1.0");
// Hammer in hammer.h (our Hammer plugin)
#include "toolinterface.h"
class Hammer : public QObject, public ToolInterface
{
Q_OBJECT
Q_INTERFACES(ToolInterface)
public:
QString toolName() const { return "hammer"; }
};
Q_EXPORT_PLUGIN2(hammer, Hammer);
When moc runs on the hammer.h code, it inspects Q_INTERFACES. It
generates code for a function called qt_metacall - void
*Hammer::qt_metacast(const char *iname). This goal of this 'casting' function is to return a pointer to an interface depending on iname.
moc also verifies whether the interface names that you have put inside
Q_INTERFACES have indeed been declared. It can do this by inspecting
the header files and looking for a Q_DECLARE_INTERFACE. In our case,
there is a Q_DECLARE_INTERFACE inside toolinterface.h.
I a project of mine, written in Qt, I have a QWidget Widget that should display either a MyTreeWidget (inheriting from QTreeWidget) or a MyTableWidget (inheriting from QTableWidget)
Constraints
Widget shouldn't know who it is talking to. Therefore it must (??) own a class inherited by the My(Tree|Table)Widget
MyTreeWidget and MyTableWidget share a lot of code and I don't want to copy paste this code. So I thought of making them inherit from a MyGenericView which inherit from QAbstractItemView
The Interfaces
#include <QAbstractItemView>
#include <QTreeWidget>
#include <QTableWidget>
class MyGenericView : public QAbstractItemView
{
Q_OBJECT
public:
MyGenericView();
};
class MyTreeWidget : virtual public QTreeWidget,
virtual public MyGenericView
{
Q_OBJECT
public:
explicit MyTreeWidget(QWidget *parent = 0);
};
class MyTableWidget : public MyGenericView, public QTableWidget { ... };
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0) :
QWidget(parent)
{
m_genericView = new MyTreeWidget();
}
private:
MyGenericView *m_genericView;
};
The Error
erreur : invalid new-expression of abstract class type 'MyTableWidget'
m_genericView = new MyTableWidget();
note: because the following virtual functions are pure within 'MyTableWidget':
class MyTableWidget : public QTableWidget, public MyGenericView
And the same for MyTreeWidget.
So how would you correct this?
It seems that what you're trying to do is ill-advised. Both views that you derive from are convenience views. They hopelessly mix up the view with the model. It's OK to use them if the needs are simple and convenience is all you're after, but in your case I presume most of the shared code is related to the model side of things, not to the view. You could probably achieve what you wish by simply showing a QStandardItemModel on either a stock QTableView or a stock QTreeView, and having a class that uses the QStandardItemModel to build up your data structure.
For more details of how you could do it, if it turned out to be the right thing to do, see this answer.
Edit : As suggested below in comments, this answer is based on faulty assumptions. Please see the better answer below.
First, you have an issue of diamond inheritance. Your error is because MyTableWidget has an undefined pure virtual member function.
Frankly though, I'm not sure why you want to use multiple inheritance at all here. If it's to save on code duplication, why can't MyTreeWidget and MyTableWidget share behavioural elements via composition instead of inheritence? Is this definitely a case of is-a vs has-a? If it's code specific to widgets that is shared but don't overlap in any way with the QTableWidget/QTreeWidget approach, just write an adaptor class that will be filled with either a Tree or Table widget.
I have the following case:
class A: public QObject
class B: public A, public QThread
Then the inheritance ambiguous happens because the QObject is inherited twice...
Is there a solution to this?
QThread inherits non-virtually from QObject. Therefore, there is no way of inheriting down an hierarchy from both QThread and QObject without creating an ambiguity. Virtual inheritance won't help here, since you are not dealing with any diamond inheritance pattern.
A fix is to alter your design, as #Gabor Angyal mentioned.
Related question: how can i inherit from both QWidget and QThread?
Multiple inheritance of QObject base classes does not work.
You can do something like this to solve this:
class A: public QObject
class B: public A
{
Q_OBJECT
public:
//...
QThread *threadController() { return &mThreadController; }
private:
//...
QThread mThreadController;
}
You could also write delegates for the signals and slots you need instead of exposing the whole QThread object, or just write higher level API for class B and leave its internal QThread completely hidden. Depends on what you are trying to do, really.
If you really need to subclass QThread, then just use that as member variable type instead.
A little tricky, but might work for you:
template<class T>
class A: public T
class B: public A<QThread>
And if you need to use A just by itself then:
A<QObject> *a = new A<QObject>;
This pattern is called Mixin.
UPDATE
Ok, I realised that the moc system would obviously not work with this solution. I support hyde's answer instead.
I'm fighting with Qt. Cannot find out reliable solution for my specific problem.
We have custom class MyWidget that must:
be derived from QWidget to override closeEvent method
have fields that must be initialzed in constructor
Problems:
QWidget's guts initialized with QUiLoader from .ui file. So I have only QWidget* pointer
QWidget is non-copyable.
QWidget has no move constructor
The code (error checking and memory management are omitted for simplicity):
class MyWidget : public QWidget
{
bool m_Closed;
public:
MyWidget(QWidget* qw) :
QWidget(*qw), // error: copy constructor is private
m_Closed(false)
{}
bool IsClosed() const { return m_Closed; }
virtual void closeEvent(QCloseEvent *) override { m_Closed = true; }
};
QFile file("main.ui");
QUiLoader uiLoader;
MyWidget* uiMain = new MyWidget(uiLoader.load(&file));
uiMain->show();
Questions:
How can I workaround this? I feel that solution is very simple.
Can I use move semantics here somehow?
Note that:
I cannot make QWidget member, as I need to override its method.
Probably, I can make some MyWidget::Init() method, to init those bool flag, which must be called after each instantiation. But I find this solution unreliable.
In the end, I must just have QWidget, that I can check if it was closed or not (maybe you know another, simple way)
I use MSVC 2013 RC and GCC 4.8.1, so C++11 solution would be great
Do not hesitate, I appreciate any suggestions and criticism.
.ui files can use custom classes that derive from QWidget, so you can use your class in the Designer - even without writing any Designer plugins (it won't be shown). Right-click on a widget and select "Promote".
You need to create your own derived version of QUiLoader, and provide an implementation of the factory method QUiLoader::createWidget that can create your widgets. See this answer for a complete example.
Then you put your initialization code in the derived widget.
i have a class that inherits from QObject and have the Q_OBJECT macro:
class SomeClass: public QObject
{
Q_OBJECT
public:
SomeClass(QObject *parent = 0);
void method1();
void method2();
...
};
in another class in the same header i create an instance of that class, and then i try to get all Methods from 'SomeClass' and store it in a QMap:
this->someclass = new SomeClass(); // in constructor.
...
cout<<"init some class methods"<<endl;
const QMetaObject *metaobj = dynamic_cast<QObject*>(this->someclass)->metaObject();
cout<<"offset "<<metaobj->methodOffset()<<endl;
for(int i = metaobj->methodOffset();i < metaobj->methodCount();i++){
QMetaMethod metamethod = metaobj->method(i);
//if(metamethod.methodType() == QMetaMethod::Method){
QString methodname = QString(metamethod.signature());
methodname = methodname.replace(QRegExp("\\(.*\\)"),"");
controlmethods.insert(methodname,metamethod);
cout<<"added method: "<<metamethod.signature()<<" as "<<methodname.toAscii().data()<<endl;
//}
}
But this do not showed me any method added because the methods offset is equals to the methods count, why can be? i dont get with the reason, Thanks any help.
You need to use the Q_INVOKABLE macro for each method you want to see in the QMetaObject.
From the documentation:
Q_INVOKABLE
Apply this macro to declarations of member functions to allow them to be invoked via the meta-object system. The macro is written before the return type, as shown in the following example:
class Window : public QWidget {
Q_OBJECT
public:
Window();
void normalMethod();
Q_INVOKABLE void invokableMethod(); };
The invokableMethod() function is marked up using Q_INVOKABLE, causing it to be registered with the meta-object system and enabling it to be invoked using QMetaObject::invokeMethod(). Since normalMethod() function is not registered in this way, it cannot be invoked using QMetaObject::invokeMethod().
You can also use the slots macro. I think Q_INVOKABLE may be more minimal, though.
QMetaObject is only aware of signals, slots, properties and other invokable member functions, occasionally referred to as "meta-methods" as a group.
Also, for the first line of your example, you should (probably) just call
const QMetaObject *metaobj = someClass->metaObject();
This isn't just cosmetic. The dynamic_cast would move type-checking to runtime, which isn't necessary if you know at compile time that someClass is a pointer to a QObject-derived class. (dynamic_casting to QObject* will work, and will get you the correct QMetaObject because of virtual inheritance, but it's unnecessary, less safe, and unclear.)
You don't actually need an instance of the class to get the meta-object:
const QMetaObject *metaobj = SomeClass::staticMetaObject();
This is possible because there is one QMetaObject per class, not per object.
For anyone who wants to know more about the meta-object system, I recommend coffee and the documentation. Usually you don't need to deal with QMetaObject instances directly, unless you're writing a scripting engine or something equally 'meta'. It's easy to unintentionally duplicate functionality Qt already provides.
Also, Q_DECLARE_METATYPE is not what you want.
There is some kind of ambiguity in the official docs.
First we see:
method() and methodCount() provide information about a class's
meta-methods (signals, slots and other invokable member functions).
But later:
int QMetaObject::methodCount() const Returns the number of methods in
this class, including the number of properties provided by each base
class. These include signals and slots as well as normal member
functions.
But actually we can't have access to 'normal' methods through Qt MetaObject System.
So, to get access to your methods, you should declare them with the Q_INVOKABLE macro:
Q_INVOKABLE void method1();
Q_INVOKABLE void method2();