QMetaMethods for regular methods missing? - c++

I'm new in QT, and I'm just testing out the MOC.
For a given class:
class Counter : public QObject
{
Q_OBJECT
int m_value;
public:
Counter() {m_value = 0;}
~Counter() {}
int value() {return m_value;}
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
I want to get a list of all methods in a class, but seem to only be getting a list of signals and slots, although the documentation says it should be all methods? Here's my code:
#include <QCoreApplication>
#include <QObject>
#include <QMetaMethod>
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
const QMetaObject cntmo = Counter::staticMetaObject;
for(int i = 0; i != cntmo.methodCount(); ++i)
{
QMetaMethod qmm(cntmo.method(i));
cout << qmm.signature() << endl;
}
return app.exec();
}
Please beware this is my best c/p, perhaps I forgot to include some headers.
My output:
destroyed(QObject*)
destroyed()
deleteLater()
_q_reregisterTimers(void*)
valueChanged(int)
setValue(int)
Does anyone know why this is happening? Does qt not recognise
int value() {return m_value;}
as a valid method? If so, is there a macro I've forgotten or something like that?
P.S. I'm using 4.6.2
UPDATE
I forgot the implementation of the setValue method, not that it makes too much a difference to my actual question.
void Counter::setValue(int value)
{
if(value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}

As far as I remember you can't access all methods of a QObject subclass through the QMetaObject it provides access only for the signals, slots and INVOCABLE methods:
class MyClass: public QObject {
Q_OBJECT
public:
Q_INVOCABLE int someMethod(const QString &someParam);
};
Maybe it's also provide access to Q_PROPERTY getters and setters. Read articles about Qt object model and meta object system more carefully.
Quotation from QMetaObject class description (http://doc.trolltech.com/4.6/qmetaobject.html#details):
"method() and methodCount() provide information about a class's meta-methods (signals, slots and other invokable member functions)."
There is no information about normal C++ methods access. And actually it's good since reflective techniques are slow.

Related

QT/QML c++ Program crash on access a QList from QML

I have 2 classes for datahandling (CGameList and CGame).
I define one GameList (_gamelist) object in qml to work with it.
I have a Listviews with show the the games from this GameList (editGames_open()).
If I click on one entry of this list, it open a new list with a detailed view of this game (editGame_open(index)).
This work works like expected.
Now my problem:
If I go back to the list and try open it again, my program crash (not every time, sometimes its work 20x times).
The crash appear after the call of getGame.
If I use the debugger I can see my CGameList-object looks fine(data a correct + item in my QList are correct), but after this the program crash with a Segmentation Fault.
The callstack show only QQMlData::wasDeleted as last entry.
I think the problem is, that my object is delete, but I cant find this.
I had try to change my QList from QList _games to QList* _games but without success.
One other thing(I think its the same problem):
Sometimes getGame give back a NULL-pointer(although the game is in the list, but the data are wrong).
cgamelist.h
#ifndef CGAMELIST_H
#define CGAMELIST_H
#include <QObject>
#include <QList>
#include <qfile.h>
#include <QTextStream>
#include <QDir>
#include <QStandardPaths>
#include <QDateTime>
#include <cgame.h>
class CGameList : public QObject
{
Q_OBJECT
Q_PROPERTY(int itemCount READ getItemCount)
public:
CGameList(QObject *parent = 0);
Q_INVOKABLE bool addGame(QString name,int layout);
Q_INVOKABLE int getItemCount() const;
Q_INVOKABLE void saveGame(int index);
Q_INVOKABLE void loadGames(bool force=true);
Q_INVOKABLE CGame* getGame(int i) const;
Q_INVOKABLE QString getGamename(int i) const;
Q_INVOKABLE QString getGamedate(int i) const;
Q_INVOKABLE void delGame(int i);
private:
QList<CGame*>* _games;
};
#endif // CGAMELIST_H
cgamelist.cpp
CGameList::CGameList(QObject *parent) : QObject(parent)
{
_games=new QList<CGame*>();
_games->clear();
}
...
CGame* CGameList::getGame(int i) const
{
/* CGame*g=new CGame();
g->setGamename("test");
return g;*/
try
{
return _games->at(i);
}
catch(...)
{
return NULL;
}
}
...
cgame.h*
#ifndef CGAME_H
#define CGAME_H
#include <QObject>
#include <QString>
#include <QDateTime>
class CGame : public QObject
{
Q_OBJECT
Q_PROPERTY(QString gamename READ getGamename WRITE setGamename)
Q_PROPERTY(int itemCount READ getItemCount)
Q_PROPERTY(int layout READ getLayout WRITE setLayout)
Q_PROPERTY(int duration READ getDuration WRITE setDuration)
Q_PROPERTY(QDateTime date READ getDate WRITE setDate)
public:
CGame(QObject *parent = 0);
Q_INVOKABLE QString getGamename() const;
Q_INVOKABLE void setGamename(QString name);
Q_INVOKABLE int getItemCount() const;
Q_INVOKABLE int getLayout() const;
Q_INVOKABLE void setLayout(int layout);
Q_INVOKABLE int getDuration() const;
Q_INVOKABLE void setDuration(int duration);
Q_INVOKABLE QDateTime getDate() const;
Q_INVOKABLE void setDate(QDateTime date);
Q_INVOKABLE QString getEvent(int i) const;
Q_INVOKABLE void addEvent(QString ename,int time,int sec,int duration);
private:
QString _name;
int _layout;
int _duration;
QList<QString>* _events;
QDateTime _date;
};
#endif // CGAME_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "clayoutlist.h"
#include "clayout.h"
#include "clayoutitem.h"
#include "cgame.h"
#include "cgamelist.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//QApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<CLayoutList>("STSP.Tag",1,0,"TagLayoutList");
qmlRegisterType<CLayout>("STSP.Tag",1,0,"TagLayout");
qmlRegisterType<CLayoutItem>("STSP.Tag",1,0,"TagLayoutItem");
qmlRegisterType<CGame>("STSP.Tag",1,0,"Game");
qmlRegisterType<CGameList>("STSP.Tag",1,0,"GameList");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2
import STSP.Tag 1.0
ApplicationWindow {
TagLayoutList {
id: _layoutlist
}
GameList
{
id:_gamelist
}
visible: true
MainForm {
id: mainform
anchors.fill: parent
}
MessageDialog{
id:info
}
...
function editGames_open()
{
_gamelist.loadGames()
mainform.p_gameslistmodel.clear()
var i
for (i = 0; i < _gamelist.getItemCount(); i++) {
mainform.p_gameslistmodel.append({
name: _gamelist.getGamename(i),
date: _gamelist.getGamedate(i),
gameindex: i
})
}
mainform.p_editgames.delmode=false
mainform.p_editgames.visible = true
mainform.p_startmenu.visible=false
}
function editGame_open(index)
{
mainform.p_eventlistmodel.clear()
var game={}
try
{
game=_gamelist.getGame(index)
}
catch(exc)
{
console.log("Serious Error 2 "+exc)
return
}
if(game==null)
{
console.log("Reload Games")
_gamelist.loadGames()
game=_gamelist.getGame(index)
}
if(game==null)
{
console.log("Error Game not found")
return
}
var i
var event
var events
var t,s,m,h
for(i=0;i<game.getItemCount();i++)
{
event=game.getEvent(i).split('#')[1]
//console.log(event)
events=event.split(',')
t=events[1]
s=t%60
t=(t-s)/60
m=t%60
h=(t-m)/60
mainform.p_eventlistmodel.append({
name: events[0],
time: h+":"+(m<10?"0":"")+m+":"+(s<10?"0":"")+s,
eventindex: i
})
}
mainform.p_editgame.gname=game.getGamename()
t=game.getDuration()
s=t%60
t=(t-s)/60
m=t%60
h=(t-m)/60
mainform.p_editgame.gtime=h+":"+(m<10?"0":"")+m+":"+(s<10?"0":"")+s
mainform.p_editgames.delmode=false
mainform.p_editgames.visible=false
mainform.p_editgame.visible=true
}
function editGame_back()
{
mainform.p_editgame.visible=false
mainform.p_editgames.visible=true
}
It's hard to say without being able to debug your program, but it sounds like QML is taking ownership of your CGame object:
When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.
Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will not ever take ownership of a QObject instance which already has a parent.
The simplest solution would be to assign a parent to each CGame object before returning it to QML. Alternatively, you can do the following for each object:
QQmlEngine::setObjectOwnership(game, QQmlEngine::CppOwnership);
i think you will create your QList from C++, and put in qml like a contextProperty (with that you dont need declare in qml the class, but you still need register type), but the problem is you need refresh (executing again the sentence of declaration of contextPropertie) for every change in the model, and yes, is better user QObject*.
Maybe is better try use a QmlListProperty (here my case Use QQmlListProperty to show and modify QList in Qml), in youtube exist other tutorial for use that class), or QAbstractListModel (i don't worked before with that class, but tell me is lightly better in some cases).

Qt abstract class as interface

I am working on an IDE written in QT. I need to use the QMainWindow AND the QSyntaxHighLighter classes. However, when compiling it spits out the following error.
cannot declare variable 'w' to be of abstract type 'SquareSDK'
That refers to my main.cpp file.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SquareSDK w;
w.show();
return a.exec();
}
That class is defined in ediot.h
#ifndef EDIOT_H
#define EDIOT_H
#include <QMainWindow>
#include <QFileDialog>
#include <QtCore>
#include <QtGui>
#include <QObject>
#include <QSyntaxHighlighter>
namespace Ui {
class SquareSDK;
}
class SquareSDK : public QMainWindow, private QSyntaxHighlighter
{
Q_OBJECT
public:
explicit SquareSDK(QWidget *parent = 0);
~SquareSDK();
private slots:
void on_actionUndo_triggered();
void on_actionRedo_triggered();
void on_actionCut_triggered();
void on_actionCopy_triggered();
void on_actionPaste_triggered();
void on_actionNew_triggered();
void on_actionOpen_triggered();
void on_actionSave_triggered();
void on_actionSave_As_triggered();
void on_actionRun_in_Default_Web_Browser_triggered();
void on_actionReload_triggered();
void test();
void syntax();
private:
Ui::SquareSDK *ui;
QString mFilename;
QString urlname;
QString urldebug;
QString OS;
QString a;
QString b;
QString error;
QString ext;
QString text;
};
#endif // EDIOT_H
As one or both of base classes have 1 or more pure virtual methods, until you implement them in SquareSDK, SquareSDK is an abstract class (because it literally inherits those pure virtual methods). You can't instantiate an abstract class.
(Although you can use it as a type for pointer or reference. But it can only point to its non-abstract sub-classes, despite pointer-type being of abstract class. But that's not important right now)
I checked out the documentation for QSyntaxHighlighter, and it has 1 pure virtual method.
//this is how it looks like inside QSyntaxHighlighter class
virtual void highlightBlock (const QString& text) = 0;
So implement it in your SquareSDK:
//in .h
void highlightBlock (const QString& text);
//in .cpp
void SquareSDK::highlightBlock (const QString& text)
{
//...yourcode...
}
It's a function for highlighting blocks, and because it's pure virtual, it has absolutely no original behavior defined so you must program it completely on your own. So implement it in a way it highlights a block the way you want it highlighted.
QMainWindow, luckily, contains no pure virtual methods, which cuts you some slack.
EDIT:
Inherit QSyntaxHighlighter as public. highlightBlock is protected, and if base class is inherited as private, it becomes unreachable.
Double QObject inheritance is forbidden in Qt! So you can't do it with QMainWindow and QSyntaxHighlighter. In this case double inheritance is bad in many many other ways!
Use composition not aggregation here!

Access UI elements from another class c++

I have a problem with accessing ui elements from another class(with instance). I have a second QMainWindow in my application, I can access in secondWindow.cxx class all ui elements but not in read.cxx class. My code looks like following. Where is my mistake? Thank you for your help.
-------------------------------secondWindow.h------------------------------------
#ifndef __secondWindow_h
#define __secondWindow_h
#include "ui_secondwindow.h"
class secondWindow : public QMainWindow
{
friend class read;
igstkStandardClassBasicTraitsMacro(secondWindow, QMainWindow);
Q_OBJECT
public:
igstkStateMachineMacro();
secondWindow();
virtual ~secondWindow();
void createSignalAndSlots();
public slots:
void secondWindowTest();
protected:
private:
Ui::secondMainWindow m_secondWindowUI;
};
#endif
-------------------------------secondWindow.cxx------------------------------------
#include "secondWindow.moc"
#include "secondWindow.h"
#include "read.h"
secondWindow::secondWindow() :m_StateMachine(this)
{
m_secondWindowUI.setupUi(this);
createSignalAndSlots();
}
void secondWindow::createSignalAndSlots()
{
connect(m_secondWindowUI.pushButton1, SIGNAL(clicked()),this, SLOT(secondWindowTest()));
connect(m_secondWindowUI.pushButton2, SIGNAL(clicked()), read::instance(), SLOT(readTest()));
}
void secondWindow::secondWindowTest()
{
m_secondWindowUI.pushButton1->setEnabled(true); //OK
}
secondWindow::~secondWindow(){}
---------------------------------read.h--------------------------------------
#pragma once
#include "secondWindow.h"
class read : public QObject
{
Q_OBJECT
public:
static read *instance();
read();
virtual ~read() {}
public slots:
void readTest();
protected:
secondWindow *m_readUI;
static read *m_read;
private:
};
---------------------------------read.cxx--------------------------------------
#include <read.moc>
#include "secondWindow.h"
#include "read.h"
read *read::m_read= NULL;
read::read()
{
m_readUI = dynamic_cast<secondWindow*>( QApplication::instance() );
}
read *read::instance()
{
if(m_read == NULL)
m_read = new read();
return m_read;
}
void read::readTest()
{
m_readUI->m_secondWindowUI.qlabelTest->setText("test"); //segmentation fault
}
You are casting a QApplication::instance(), which is a QApplication * deriving from QCoreApplication * deriving from QObject *. That won't work, it's not a secondWindow *, not even a QMainWindow *, not even a QWidget *.
Apart from that, your coding style is rather strange -- in Qt, it's customary to use CamelCase for classes, not thisStuff which usually applies to functions and methods. Including <read.moc> is just wrong. Why is read::m_read static? Finally, the coupling between the two window classes is set up in a strange way (accessing global stuff like QApplication just to get a reference to another window smells ugly code). A much better and more obvious approach is to either wrap all of your windows in a parent object or setting up the dependencies explicitly, perhaps like this:
MainWindow *mainWindow = new MainWindow();
SecondWindow *second = new SecondWindow(mainWindow);
UtilityWindow *utilityWin = new UtilityWindow(second);

Design Issue with Qt

using Qt 5.0.0
The following is roughly an Observer pattern (the code is stripped to bare minimum to explain only the problem):
class A : public QObject
{
Q_OBJECT
public:
void registerListner(Observer *pObs);
static A* getInstance();
signals:
void sig();
};
void A::registerListner(Observer *pObs)
{
connect(this, SIGNAL(sig()), pObs, SLOT(slo));
}
////////////////////////////////////////////////////////////////
class Observer : public QObject
{
Q_OBJECT
public slots:
virtual void slo() = 0;
};
class ConcreteObserver : public Observer , public QWidget
{
Q_OBJECT
public: //re-mentioning "slots" is not necessary
virtual void slo();
};
ConcreteObserver *pCObs = new ConcreteObserver;
A::getInstance()->registerListner(pCObs);
/////////////////////////////////////////////////////////////
problem (apart from dreaded-diamond):
Can't inherit multiple times from QObject - moc does not allow it.
One possible solution is derive Observer from QWidget and then ConcreteObserver from Observer alone. However this is putting a constraint on ConcreteObserver. Maybe ConcreteObserver_2 needs to derive from QDialog instead etc.
How do i solve this design problem? Is there anything specific to Qt 5.0.0 Signal-Slot (in addition to earlier versions) that can solve this, or what would you suggest?
If runtime warnings are not enough for you, you can add a bit of compile-time type checking by making registerListener a function template and avoid multiple inheritance of QObject by not defining an Observer class per-se.
Here's what this could look like: (Note: my SFINAE skills are non-existent, this could probably be made nicer.)
#include <QObject>
#include <QDebug>
#include <type_traits>
class A : public QObject
{
Q_OBJECT
public:
template <typename T>
void registerListener(T *pObs)
{
static_assert(std::is_base_of<QObject, T>::value,
"Listener must be a QObject");
static_assert(std::is_same<void,
decltype(std::declval<T>().slo())
>::value,
"Slot slo must have signature void slo();");
connect(this, SIGNAL(sig()), pObs, SLOT(slo()));
}
static A* getInstance() { return instance; }
static void init() { instance = new A; }
void doStuff() { emit sig(); }
signals:
void sig();
private:
static A *instance;
};
A few test cases:
class BadObject1 : public QObject
{
Q_OBJECT
public:
BadObject1() {}
public slots:
void slo(int){}
};
class BadObject2 : public QObject
{
Q_OBJECT
public:
BadObject2() {}
public slots:
int slo(){return 0;}
};
struct BadObject3 {
void slo();
};
class ObservedObject : public QObject
{
Q_OBJECT
public:
ObservedObject(QString const& name): QObject() {
setObjectName(name);
}
public slots:
virtual void slo(){
qDebug() << objectName();
}
};
class ObservedObject2 : public ObservedObject
{
Q_OBJECT
public:
ObservedObject2(QString const& name)
: ObservedObject(name + " (derived)") {}
};
And a main file:
#include "A.h"
A* A::instance = 0;
int main(int , char **)
{
A::init();
A::getInstance()->registerListener(new BadObject1);
A::getInstance()->registerListener(new BadObject2);
A::getInstance()->registerListener(new BadObject3);
A::getInstance()->registerListener(new ObservedObject("foo"));
A::getInstance()->registerListener(new ObservedObject2("bar"));
A::getInstance()->doStuff();
}
You'll get compiler errors for all the BadObjectN cases. If you comment them out, the output will look like:
"foo"
"bar (derived)"
A warning though: this will not check if the void slo(); member is indeed a slot. You can check that at runtime with something like:
if (pObs->metaObject()->indexOfSlot("slo()") == -1) {
qDebug() << "Class" << pObs->metaObject()->className()
<< "doesn't have a slo slot.";
::exit(1);
}
This will work and do what is expected (unless you've got a class hierarchy where the slot wasn't declared virtual - then strange things will happen in derived classes that omit the slots "specifier". So I advocate that your docs not have the comment you have above about that specifier: it is always a good idea to have it when overloading a slot).
I don't believe this last check is achievable at compile-time, "slot resolution" is done with a runtime walk of the QObject meta-data and involves parsing moc-generated strings. Even if it was with some recursive template magic, I don't think it's work the effort. You'll get a runtime error message at registration type in which you can include the actual class name of the faulty object. That's a very accurate error message, and should be caught by the simplest testcases.

How to pass QList from QML to C++/Qt?

I'm trying to pass QList of integer from QML to C++ code, but somehow my approach is not working. With below approach am getting following error:
left of '->setParentItem' must point to class/struct/union/generic type
type is 'int *'
Any inputs to trouble shoot the issue is highly appreciated
Below is my code snippet
Header file
Q_PROPERTY(QDeclarativeListProperty<int> enableKey READ enableKey)
QDeclarativeListProperty<int> enableKey(); //function declaration
QList<int> m_enableKeys;
cpp file
QDeclarativeListProperty<int> KeyboardContainer::enableKey()
{
return QDeclarativeListProperty<int>(this, 0, &KeyboardContainer::append_list);
}
void KeyboardContainer::append_list(QDeclarativeListProperty<int> *list, int *key)
{
int *ptrKey = qobject_cast<int *>(list->object);
if (ptrKey) {
key->setParentItem(ptrKey);
ptrKey->m_enableKeys.append(key);
}
}
You CAN'T use QDeclarativeListProperty (or QQmlListProperty in Qt5) with any other type than QObject derived ones. So int or QString will NEVER work.
If you need to exchange a QStringList or a QList or anything that is an array of one of the basic types supported by QML, the easiest way to do it is to use QVariant on the C++ side, like this :
#include <QObject>
#include <QList>
#include <QVariant>
class KeyboardContainer : public QObject {
Q_OBJECT
Q_PROPERTY(QVariant enableKey READ enableKey
WRITE setEnableKey
NOTIFY enableKeyChanged)
public:
// Your getter method must match the same return type :
QVariant enableKey() const {
return QVariant::fromValue(m_enableKey);
}
public slots:
// Your setter must put back the data from the QVariant to the QList<int>
void setEnableKey (QVariant arg) {
m_enableKey.clear();
foreach (QVariant item, arg.toList()) {
bool ok = false;
int key = item.toInt(&ok);
if (ok) {
m_enableKey.append(key);
}
}
emit enableKeyChanged ();
}
signals:
// you must have a signal named <property>Changed
void enableKeyChanged();
private:
// the private member can be QList<int> for convenience
QList<int> m_enableKey;
};
On the QML side, simply affect a JS array of Number, the QML engine will automatically convert it to QVariant to make it comprehensible to Qt :
KeyboardContainer.enableKeys = [12,48,26,49,10,3];
That's all !