I am working on making my own designer widget that looks and functions like qt. But now I needed to know how the property is created. I knew we can get the properties of an widget using QMetaObject and QMetaProperty but my question is will I be able to get the class name of each property. Like object name property comes from QObject and geomentry property comes from QWidget. Is it so that i should hard code myself to model or is there a way to get the classInfo from property. I have attached the image which im trying to achieve any response or related post is appreciated.
thanks,
In order to create mapping between classes and their properties you need to traverse through your object's class hierarchy. Here is the sample code that crates such mapping:
// Create properties per class mapping for the initialObject.
const QMetaObject *mo = initialObject->metaObject();
QMap<QString, QStringList> propertyMap;
do {
QStringList properties;
for(int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) {
properties << QString::fromLatin1(mo->property(i).name());
}
propertyMap[mo->className()] = properties;
} while (mo = mo->superClass());
Now the propertyMap contains all properties sorted by the class names they belong to.
Related
In my project , i use C++ , QScxmlCppDataModel , there is always occur a error , "No data-model instantiated" when i start the state machine,
I follow the Qt document says
1、Add data model in scxml file
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" datamodel="cplusplus:DataModel:DataModel.h" name="PowerStateMachine" qt:editorversion="4.6.1" initial="nomal">
2、Create a new data model subclass
#include "qscxmlcppdatamodel.h"
#include <QScxmlEvent>
class DataModel :public QScxmlCppDataModel
{
Q_OBJECT
Q_SCXML_DATAMODEL
public:
// DataModel();
bool isMoreThan50() const;
bool isLessThan50() const ;
int m_power;
QString m_Descript;
QVariant m_var;
};
3、load and start state machine
m_stateMachine = QScxmlStateMachine::fromFile(":/powerStateMachine.scxml");
for(QScxmlError& error:m_stateMachine->parseErrors())
{
qDebug()<<error.description();
}
m_stateMachine->connectToEvent("powerLoss", this, &MainWindow::onPowerLossEvent);
m_stateMachine->connectToEvent("pwoerUp", this, &MainWindow::onPowerUpEvent);
m_stateMachine->connectToState("low", this, &MainWindow::onLowState);
m_stateMachine->connectToState("nomal", this, &MainWindow::onNomalState);
m_stateMachine->connectToState("danger", this, &MainWindow::onDangerState);
m_stateMachine->connectToState("full", this, &MainWindow::onFullState);
DataModel *dataModel = new DataModel;
m_stateMachine->setDataModel(dataModel);
m_stateMachine->init();
m_stateMachine->start();
error image
but still have a error :"No data-model instantiated",when i start the
state machine , anybody know how to fix it ?? thank you
Qt's documentation on QScxmlCppDataModel specifically says:
The C++ data model for SCXML ... cannot be used when loading an SCXML
file at runtime.
And that is exactly what you are doing, so instead of loading the scxml file at runtime:
m_stateMachine = QScxmlStateMachine::fromFile(":/powerStateMachine.scxml");
Use the compiled statemachine directly:
MyStateMachine statemachine;
MyDataModel datamodel;
statemachine.setDataModel(&datamodel);
In the example above:
1- MyStateMachine is the value you have assigned to the <name> attribute of the scxml element (i.e. scxml file/model).
2- MyDataModel is the name you have given to your c++ data model class (i.e. your class derived from QScxmlCppDataModel)
class MyDataModel : public QScxmlCppDataModel
{
Q_OBJECT
Q_SCXML_DATAMODEL
public:
MyDataModel();
};
Too late an answer, but hope it helps others.
I have a list of application specific items uniquely identified by an id. Their names are displayed in a QTreeWidget (one item corresponds to a single QTreeWidgetItem). I would like to somehow attach the corresponding ids to these QTreeWidgetItems so that upon selection changed I can access the id of the corresponding item and do some processing.
QTreeWidgetItem does not inherit from QObject so I cannot use its setProperty function. How could I do this?
Just create some user defined roles for the properties...
typedef enum {
id_1_role = Qt::UserRole,
id_2_role,
id_N_role,
} property_id_role;
Then you can use the normal means of getting/setting the data associated with a QTreeWidgetItem.
QTreeWidgetItem *item = ...
/*
* Set the property value.
*/
item->setData(column, property_id_role::id_2_role, id_2_value);
/*
* Get the property value.
*/
auto id_2_value = item->data(column, property_id_role::id_2_role).value<id_2_type>();
Do you know that QTreeWidgetItem has a setData method?
setData(int column, int role, const QVariant &value)
You can use it with your roles. For example:
int your_id = 123;
ui->treeWidget->currentItem()->setData(0,Qt::UserRole,your_id);
qDebug() << ui->treeWidget->currentItem()->data(0,Qt::UserRole);
I am quite new to Qt, so I am probably asking a pretty obvious question.
I would like to create a super type for all of my custom QML GUI elements that I want to create in C++.
This super type is supposed to add predefined states to a QML Item. Something alike to this:
import StatedGuiElement 1.0
import QtQuick 2.0
Item {
width: 300; height: 200
StatedGuiElement {
id: aStatedGuiElement
anchors.centerIn: parent
width: 100; height: 100
//some visible Custom Gui Elements
states:[
State {
name: "A_STATE"
},
State {
name: "ANOTHER_STATE"
}]
}
I know how to create a simple custom Item from this tutorial (http://doc.qt.io/qt-5/qtqml-tutorials-extending-qml-example.html). I guess the states could be defined by using an enum in the C++ class which inherits from QQuickItem. However, this tutorial does not show how to create more complex Qt Quick elements like the state list.
class StatedGuiElement : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
//pass States via Q_PROPERTY?
public:
//define Enum for fixed States here?
//ENUM STATES {A_STATE, ANOTHER_STATE}
StatedGuiElement( QQuickItem *parent = 0);
QString name() const;
void setName(const QString &name);
private:
QString m_name;
//Some List of States?
signals:
public slots:
};
So the questions I am wondering about are as follows:
Is it even possible to predefine QML State types and use them in multiple elements?
How do I add complex QML types like State Lists in a C++ class such as StatedGuiElement?
First you create your StatedGuiElement as a QQuickItem subclass.
Then you crate a StatedGuiElement.qml, import the package that contains the C++ element, make a StatedGuiElement {} inside, add your states in QML to it, then you can use StatedGuiElement in your project. It will be the one with the QML extra stuff predefined in it.
This assumes the element actually has things you need to implement in C++. If not then it would not make sense to have a C++ element at all. I am not sure whether old C++ state classes will work with QML, probably not, and using the QML states from C++ will be anything but convenient, so you really should do the states in QML, on top of whatever C++ stuff you may have.
It is possible to define your properties once and use them im multiple elements, if you nest your QML elements inside a super-type QML element where you have all your states defined. Child elements can access the parent parameters.
Alternatively, you can also just simply set the context property for each QML which should use the data like this in C++:
QQuickView view;
QStringList data;
// fill the list with data via append()
view.rootContext()->setContextProperty("dataList", QVariant::fromValue(data));
// now the QML can freely use and access the list with the variable name "dataList"
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
In the QML-side, you can also declare a custom property which holds the names of the states.
Item {
property variant state_list: ["element1", "element2", "element3"]
// or if you defined a list in the C++ part as a context property
// you can use this instead:
// property variant state_list: dataList
states: [
State {
name: state_list[0]
},
State {
name: state_list[1]
},
// and so on
]
}
If you need a property that is a list of elements, i..e. states being a list of State objects, then you can do this in C++ using the QQmlListProperty type.
You need a QObject derived type for the list element type.
Example
class Entry : public QObject
{
// the list entry element's API
};
class MyItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Entry> entries READ entries)
public:
QQmlListProperty<Entry> entries() const {
return QQmlListProperty<Entry>(this, m_entries);
}
private:
QList<Entry*> m_entries;
};
Register both with qmlRegisterType()
In QML
MyItem {
entries: [
Entry {
},
Entry {
}
]
}
I would like to know if there is any macro or way how to register Qt model as property of QObject.
For example, I have AnimalModel (http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel).
I Know I can pass it to root context of QuickView
QuickView view;
view.rootContext()->setContextProperty("myModel", &model);
In case I have QObject registered via Qml macros, I can pass this object to view too:
view.rootContext()->setContextProperty("obj", pDataObject);
But what If I want to have QObject which holds model of any data?
For example:
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
...
AnimalModel m_modelAnimals;
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
};
Every example I found until now shows how to pass QAbstractListModel to root context. But none how to use it as QObject property.
(I know there is QQmlListProperty but QQmlListProperty doesn't support partial refresh. It's always necessary to rebuild all Qml objects)
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
Yes it is, didn't you try? Of course, it will not be a AnimalModel but a AnimalModel *, but as long as the model inherits QAbstractListModel, that's all you need. You don't even need the NOTIFY part, as changes, internal to the model will be automatically reflected anyway. modelAnimalsChanged only makes sense when you replace the entire model with a different model, and naturally, to shut up QML's warnings about using a property without a notify signal. A cleaner way to do the latter when the model object doesn't change is to just return a AnimalModel * from a slot or a Q_INVOKABLE.
If you want a truly flexible model, you can make one that stores QObject *, then from QML you can create arbitrary objects with arbitrary properties, and add to the model. Then from the model you have a single object role which returns the object, and you can query and use the object to retrieve the properties it holds. Whereas a "classical" list model implementation will define a model with a static, fixed schema, using this approach allows to have "amorphous" objects in the model with different properties.
Naturally, this requires some type safety, for example have a property int type for each object in such a model, and based on it you can determine the available properties for the object. My usual approach is to have a Loader for a delegate, and have it pass the object as a data source to different QML UI implementations visualizing that object type that it instantiates. This way you have both different objects in the model, and different QML items as view delegates.
The last step to making the ultimate "jack of all trades" list/model object is to implement QQmlListProperty and Q_CLASSINFO("DefaultProperty", "container") for it, allowing you to both compose the list/model dynamically, or using QML's declarative syntax. Also note that with this solution, you can add to or remove from such a model, even remove declaratively instantiated objects.
Also, depending on your usage scenario, you may have to either qmlRegisterType() or qmlRegisterUncreatableType() for the model.
OK, on a second glance, it looks like by "model of any data" you didn't mean schema-less models but simply different schema models. In that case, instead of returning an AnimalModel *, you can use a QAbstractListModel * or even a QObject * - it will work in QML anyway, as it employs dynamism through the meta system. But at any rate, schema-less models are that much more powerful and flexible, and they don't need C++ code to be defined, it can all work from QML alone.
class List : public QAbstractListModel {
Q_OBJECT
QList<QObject *> _data;
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(QQmlListProperty<QObject> content READ content)
Q_PROPERTY(QObject * parent READ parent WRITE setParent)
Q_CLASSINFO("DefaultProperty", "content")
public:
List(QObject *parent = 0) : QAbstractListModel(parent) { }
int rowCount(const QModelIndex &p) const { Q_UNUSED(p) return _data.size(); }
QVariant data(const QModelIndex &index, int role) const {
Q_UNUSED(role)
return QVariant::fromValue(_data[index.row()]);
}
QHash<int, QByteArray> roleNames() const {
static QHash<int, QByteArray> roles = { { Qt::UserRole + 1, "object" } };
return roles;
}
int size() const { return _data.size(); }
QQmlListProperty<QObject> content() { return QQmlListProperty<QObject>(this, _data); }
public slots:
void add(QObject * o) { insert(o, _data.size()); }
void insert(QObject * o, int i) {
if (i < 0 || i > _data.size()) i = _data.size();
beginInsertRows(QModelIndex(), i, i);
_data.insert(i, o);
o->setParent(this);
sizeChanged();
endInsertRows();
}
QObject * take(int i) {
if ((i > -1) && (i < _data.size())) {
beginRemoveRows(QModelIndex(), i, i);
QObject * o = _data.takeAt(i);
o->setParent(0);
sizeChanged();
endRemoveRows();
return o;
} else qDebug() << "ERROR: take() failed - object out of bounds!";
return 0;
}
QObject * get(int i) {
if ((i > -1) && (i < _data.size())) return _data[i];
else qDebug() << "ERROR: get() failed - object out of bounds!";
return 0;
}
void internalChange(QObject * o) { // added to force sort/filter reevaluation
int i = _data.indexOf(o);
if (i == -1) {
qDebug() << "internal change failed, obj not found";
return;
} else {
dataChanged(index(i), index(i));
}
}
signals:
void sizeChanged();
};
Then, after you qmlRegisterType<List>("Core", 1, 0, "List"); you can use it pretty much any way you want to - it will hold any QObject or derived, naturally including QMLs QtObject It can directly be used as a model to drive a ListView. You can populate it dynamically using the slots or declarative, like this:
List {
QtObject { ... }
QtObject { ... }
List {
QtObject { ... }
QtObject { ... }
}
}
It will also handle object ownership, and you can easily nest it, producing in essence a compartmentalized tree model - note that you can't declaratively do that with QML's ListModel. You may want to add a parentChanged signal and implement a setter that emits it if you want to bind against a changing parent, it was not necessary in my case.
As of how to use it with a view, you can either use the objectName property or an int type property or basically any means to discern between different object types, and use a Loader for the delegate:
Loader {
// using component in order to capture context props and present to the variable delegate
sourceComponent: Qt.createComponent(obj.objectName + ".qml")
// if that is not needed simply use
// source: obj.objectName + ".qml"
// or setSource to pass specific properties to delegate properties
// Component.onCompleted: setSource(obj.objectName + ".qml", {/*prop list*/})
}
Update:
Here is also the gist of the implementation for a simple and just as dynamic and generic sorting and filtering proxy to go with this model for enhanced usability.
I'd like to store custom objects (let's say instances of MyDataClass) in a tree structure, and linked with a view. So I used QStandardItemModel. I think that MyDataClass should inherit from QStandardItem :
class MyDataClass : public QStandardItem
{
public:
MyDataClass(QString name)
private:
vector<float> someData;
}
But I cannot figure out how to store instances of this class in a QStandardItemModel.
I tried QStandardItem.setChild and then appendRow but it does not work and I think I don't really get the QStandardItemModel thing.
I think that the solution deals woth QStandardItem.setData but I cannot figure out how it works for custom objects.
I have finally make it work using QVariant.
To fill the model with custom data :
MyDataClass *data;
... // adding some data
QVariant variant;
variant.setValue(data);
QStandardItemModel model; // here is your model
QStandardItem *parentItem = model.invisibleRootItem();
QStandardItem *item = new QStandardItem();
item->setData(variant);
parentItem->setChild(0, 0, item); // adding the item to the root
Later, when you want to retrieve your data :
MyDataClass *retrievedData = model.invisibleRootItem()->
child(0, 0)->data().value<MyDataClass*>();
Note that I had to add a line in the class declaration :
class MyDataClass : public QStandardItem
{
public:
MyDataClass(QString name)
private:
vector<float> someData;
}
Q_DECLARE_METATYPE(MyDataClass *) // add this line
Thank you for your help.
You can use QStandardItemModel::setItemPrototype.
http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html#setItemPrototype
Inherit from QStandardItem and reimplement method clone.
Create a new instance of your item and pass it to setItemPrototype.