#include "qgpropertywidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydebug.h" class QgPropertyWidget::Implementation { public: QWidget* w; QLabel* lbl; QVBoxLayout* ly; QTreeView* t; QStandardItemModel* m; QObject* obj; QDialogButtonBox* bb; }; enum DelegateType { DT_Enum=QVariant::UserType+1, DT_Checkbox, DT_Combo }; const int MyTypeRole = Qt::UserRole + 1; class PropertyItem: public QStandardItem { public: QObject& obj; const QMetaObject& mt; QMetaProperty mp; int index; QStandardItem& value; QStandardItem& label; virtual ~PropertyItem() { //MyDebug()<<"PropertyItem::~PropertyItem()"; } PropertyItem(QObject& o, int i): obj(o), mt(*o.metaObject()), mp(mt.property(i)), index(i), value(*this), label(*new QStandardItem) { label.setData(mp.name(), Qt::DisplayRole); label.setEditable(false); QString description; //=mt.classInfo(mt.indexOfClassInfo("description")).value(); description+=QString(" (")+QString(mp.typeName())+QString(")"); label.setToolTip(description); label.setData(MyTypeRole, reinterpret_cast(&obj)); value.setData(reinterpret_cast(this), MyTypeRole); value.setEnabled(mp.isValid() && mp.isReadable()); value.setData("(invalid)", Qt::DisplayRole); value.setEditable(false); propertyInit(); } virtual void propertyInit() { value.setEditable(mp.isWritable()); value.setData(mp.read(&obj), Qt::DisplayRole); } virtual QString displayText() const { return mp.read(&obj).toString(); } virtual QWidget* createEditor(QWidget */*parent*/, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const { return 0; } virtual bool updateEditorGeometry(QWidget */*editor*/, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const { return false; } virtual bool onSetModelData(QWidget* /*editor*/, QAbstractItemModel */*model*/, const QModelIndex& /*index*/) const { obj.setProperty(mp.name(), value.data(Qt::DisplayRole)); return false; } virtual bool setModelData(QWidget* editor, QAbstractItemModel *model, const QModelIndex& index) { label.setCheckable(true); label.setCheckState(Qt::Checked); return onSetModelData(editor, model, index); } virtual bool setEditorData(QWidget */*editor*/, const QModelIndex &/*index*/) const { return false; } }; class EnumPropertyItem: public PropertyItem { public: EnumPropertyItem(QObject& o, int i): PropertyItem(o, i) { } virtual void propertyInit() override { QMetaEnum e=mp.enumerator(); auto v=mp.read(&obj).toInt(); auto key=e.valueToKey(v); //value.setData(v); if (!key) value.setData(QString("%1 (invalid)").arg(v), Qt::DisplayRole); else { value.setData(key, Qt::DisplayRole); value.setToolTip(QString("%1 (%2)").arg(key).arg(v)); } } virtual QString displayText() const { return mp.read(&obj).toString(); } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const override { QMetaEnum e=mp.enumerator(); auto v=mp.read(&obj).toInt(); auto* cb=new QComboBox(parent); int selected=0; for(int i=0; iaddItem(QString("%1 (%2)").arg(e.key(i)).arg(e.value(i)), e.value(i)); if (e.value(i)==v) selected=i; } cb->setCurrentIndex(selected); return cb; } virtual bool onSetModelData(QWidget* editor, QAbstractItemModel */*model*/, const QModelIndex& /*index*/) const override { QComboBox* cb=qobject_cast(editor); int v=cb->currentData().toInt(); mp.write(&obj, v); value.setData(cb->currentText(), Qt::DisplayRole); MyDebug<<"property="<(mt_opaque); auto editor=p->createEditor(parent, option, index); if (editor) return editor; } return QStyledItemDelegate::createEditor(parent, option, index); } virtual QString displayText(const QVariant &value, const QLocale &locale) const override { return QStyledItemDelegate::displayText(value, locale); } virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { auto mt_opaque = index.data(MyTypeRole).toULongLong(); //MyTypeRole).toInt(); if (mt_opaque) { auto p=reinterpret_cast(mt_opaque); if (p->updateEditorGeometry(editor, option, index)) return; } QStyledItemDelegate::updateEditorGeometry(editor, option, index); } virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QStyledItemDelegate::setModelData(editor, model, index); auto mt_opaque = index.data(MyTypeRole).toULongLong(); //MyTypeRole).toInt(); if (mt_opaque) { auto p=reinterpret_cast(mt_opaque); if (p->setModelData(editor, model, index)) return; } //QStyledItemDelegate::setModelData(editor, model, index); } virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override { auto mt_opaque = index.data(MyTypeRole).toULongLong(); //MyTypeRole).toInt(); if (mt_opaque) { auto p=reinterpret_cast(mt_opaque); if (p->setEditorData(editor, index)) return; } QStyledItemDelegate::setEditorData(editor, index); } }; static void populateTreeR(QObject* obj, const QMetaObject* mt, int start, int stop, QStandardItem* root) { if (mt==0) mt=obj->metaObject(); if (start<0) start=mt->propertyOffset(); if (stop<0) stop=mt->propertyCount(); for(int i=start; imetaObject(); auto mp=mt->property(i); if (mp.isEnumType() && !mp.isFlagType()) pi=new EnumPropertyItem(*obj, i); else if (mp.type()==(QVariant::Type)QMetaType::QObjectStar) { auto item=new QStandardItem(mp.name()); item->setEditable(false); //auto i2=new QStandardItem("FAKE"); //item->appendRow(i2); //i2->setEditable(false); //QObject* o=((QObject*)mp.read(obj).toULongLong()); QObject* o=reinterpret_cast(mp.read(obj).toLongLong()); if (o) { populateTreeR(o, 0, -1, -1, item); root->appendRow(item); } //pi=new PropertyItem(*obj, i); } else pi=new PropertyItem(*obj, i); pi->propertyInit(); root->appendRow(QList()<<&pi->label<superClass(); if (sc) { auto item=new QStandardItem(sc->className()); item->setEditable(false); //auto i2=new QStandardItem("FAKE"); //item->appendRow(i2); //i2->setEditable(false); populateTreeR(obj, sc, sc->propertyOffset(), -1, item); root->appendRow(item); } } static void populateTree(QObject* obj, QStandardItem* root) { populateTreeR(obj, 0, -1, -1, root); } QgPropertyWidget::QgPropertyWidget(QObject *obj, QWidget *parent): QWidget(parent), //QScrollArea(parent), p_(*new QgPropertyWidget::Implementation) { p_.obj=obj; p_.w=this; p_.ly=new QVBoxLayout; p_.lbl=new QLabel(obj->objectName()); p_.ly->addWidget(p_.lbl); p_.t=new QTreeView(parent); p_.t->setUniformRowHeights(true); p_.t->setItemDelegate(new MyDelegate); p_.m=new QStandardItemModel; auto root=p_.m->invisibleRootItem(); p_.m->setHeaderData(0, Qt::Horizontal, "Property", Qt::DisplayRole); p_.m->setHeaderData(1, Qt::Horizontal, "Value", Qt::DisplayRole); populateTree(obj, root); connect(p_.t, &QTreeView::expanded, [=](const QModelIndex&) { MyDebug<<"expanded!"; }); /* connect(p_.m, &QStandardItemModel::itemChanged, [=](QStandardItem* item) { item->setCheckState(Qt::Checked); } ); */ p_.t->setModel(p_.m); p_.t->resizeColumnToContents(1); p_.t->setAlternatingRowColors(true); p_.ly->addWidget(p_.t); //p_.bb=new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); //p_.ly->addWidget(p_.bb); //p_.w->setLayout(p_.ly); //this->setWidget(p_.w); setLayout(p_.ly); //connect(p_.bb, &QDialogButtonBox::accepted, this, QDialog::accept); //connect(p_.bb, &QDialogButtonBox::rejected, this, QDialog::reject); } QgPropertyWidget::~QgPropertyWidget() { p_.obj=0; p_.t->setModel(0); delete p_.m; delete &p_; } class QgPropertyDialog::Implementation { public: QgPropertyWidget* w; QVBoxLayout* ly; QDialogButtonBox* bb; }; QgPropertyDialog::QgPropertyDialog(QObject* obj, QWidget* parent): QDialog(parent), p_(*new QgPropertyDialog::Implementation) { p_.w=new QgPropertyWidget(obj, this); p_.ly=new QVBoxLayout; p_.ly->addWidget(p_.w); p_.bb=new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); p_.ly->addWidget(p_.bb); setLayout(p_.ly); connect(p_.bb, &QDialogButtonBox::accepted, this, QDialog::accept); connect(p_.bb, &QDialogButtonBox::rejected, this, QDialog::reject); } QgPropertyDialog::~QgPropertyDialog() { //setLayout(0); delete p_.ly; delete &p_; }