QItemEditorCreatorBase with custom enum
-
Can someone give me a working example how to build a custom editor factory for a QComboBox with a custom enum as data? I tried following this example, but It won't work.
My Code Looks like this://hpp Q_DECLARE_METATYPE(EnumType) class EnumTypeComboBox : public QComboBox { Q_OBJECT Q_ENUM(EnumType) Q_PROPERTY(EnumType enumType READ enumType WRITE setEnumType USER true) public: EnumTypeComboBox (QWidget *widget = nullptr); EnumType enumType() const; void setEnumType (const EnumType &enumType); private: void populateList(); }; //cpp EnumTypeComboBox::EnumTypeComboBox (QWidget *widget) : QComboBox(widget) { populateList(); } EnumType EnumTypeComboBox::enumType() const { return qvariant_cast<EnumType>(itemData(currentIndex(), qMetaTypeId<EnumType>())); } void EnumTypeComboBox::setEnumType(const EnumType &enumType) { setCurrentIndex(findData(enumType, qMetaTypeId<EnumType>())); } void EnumTypeComboBox::populateList() { addItem("First Type",EnumType::FirstType); } //registration QItemEditorFactory * factory = new QItemEditorFactory; QItemEditorCreatorBase * enumTypeCreator= new QStandardItemEditorCreator<EnumTypeComboBox>(); factory->registerEditor(qMetaTypeId<EnumType>(), enumTypeCreator); QItemEditorFactory::setDefaultFactory(factory);
It gives me 4 of these Linker Errors:
editorwidget.obj:-1: Fehler: LNK2001: Nicht aufgelöstes externes Symbol ""public: static struct QMetaObject const EnumTypeComboBox::staticMetaObject" (?staticMetaObject@EnumTypeComboBox@@2UQMetaObject@@B)".Things that are not clear to me:
The enum is included from a seperate header file. Can I even use it as Q_ENUM in my own class? Doesn't it say it adds the enum to a qmetatype automatically? Doing so results in the C:\Qt\5.15.2\msvc2019\include\QtCore\qmetatype.h:1916: Fehler: C2338: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system Error. -
Ok it works now. It just needed some changes.
I had to change the property function to save the data with the Qt::UserRole. I guess because the QComboBox::addItem(QString text, QVariant userData) function used to populate the box uses the Qt::UserRole by default.cv::ThresholdTypes CVThresholdTypeComboBox::threshType() const { return qvariant_cast<cv::ThresholdTypes>(itemData(currentIndex(), Qt::UserRole)); } void CVThresholdTypeComboBox::setThreshType(const cv::ThresholdTypes &threshType) { setCurrentIndex(findData(threshType, Qt::UserRole)); }
The conversion function to get a string representation of my custom metatype looks as follows.
QString CVThresholdTypeComboBox::thresholdToString(const cv::ThresholdTypes &threshType) { switch(threshType) { case cv::THRESH_BINARY: return "Binary"; case cv::THRESH_BINARY_INV: return "Binary Inverted"; case cv::THRESH_TRUNC: return "Truncated"; case cv::THRESH_TOZERO: return "To Zero"; case cv::THRESH_TOZERO_INV: return "To Zero Inverted"; case cv::THRESH_MASK: return "Mask"; case cv::THRESH_OTSU: return "Otsu"; case cv::THRESH_TRIANGLE: return "Triangle"; default: return "Default"; } }
The conversion function is registered with this function.
QMetaType::registerConverter<cv::ThresholdTypes, QString>(&CVThresholdTypeComboBox::thresholdToString);
-
Can someone give me a working example how to build a custom editor factory for a QComboBox with a custom enum as data? I tried following this example, but It won't work.
My Code Looks like this://hpp Q_DECLARE_METATYPE(EnumType) class EnumTypeComboBox : public QComboBox { Q_OBJECT Q_ENUM(EnumType) Q_PROPERTY(EnumType enumType READ enumType WRITE setEnumType USER true) public: EnumTypeComboBox (QWidget *widget = nullptr); EnumType enumType() const; void setEnumType (const EnumType &enumType); private: void populateList(); }; //cpp EnumTypeComboBox::EnumTypeComboBox (QWidget *widget) : QComboBox(widget) { populateList(); } EnumType EnumTypeComboBox::enumType() const { return qvariant_cast<EnumType>(itemData(currentIndex(), qMetaTypeId<EnumType>())); } void EnumTypeComboBox::setEnumType(const EnumType &enumType) { setCurrentIndex(findData(enumType, qMetaTypeId<EnumType>())); } void EnumTypeComboBox::populateList() { addItem("First Type",EnumType::FirstType); } //registration QItemEditorFactory * factory = new QItemEditorFactory; QItemEditorCreatorBase * enumTypeCreator= new QStandardItemEditorCreator<EnumTypeComboBox>(); factory->registerEditor(qMetaTypeId<EnumType>(), enumTypeCreator); QItemEditorFactory::setDefaultFactory(factory);
It gives me 4 of these Linker Errors:
editorwidget.obj:-1: Fehler: LNK2001: Nicht aufgelöstes externes Symbol ""public: static struct QMetaObject const EnumTypeComboBox::staticMetaObject" (?staticMetaObject@EnumTypeComboBox@@2UQMetaObject@@B)".Things that are not clear to me:
The enum is included from a seperate header file. Can I even use it as Q_ENUM in my own class? Doesn't it say it adds the enum to a qmetatype automatically? Doing so results in the C:\Qt\5.15.2\msvc2019\include\QtCore\qmetatype.h:1916: Fehler: C2338: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system Error.@InTheBeninging said in QItemEditorCreatorBase with custom enum:
CVThresholdTypeComboBox
This can not come from the class you are showing above...
-
@InTheBeninging said in QItemEditorCreatorBase with custom enum:
CVThresholdTypeComboBox
This can not come from the class you are showing above...
@Christian-Ehrlicher It is. I just forgot to change it's name :D
-
@Christian-Ehrlicher It is. I just forgot to change it's name :D
@InTheBeninging please post actual code. How should we know that you did not forgot or add something?
Is the moc file beeing generated? I would guess not. -
@InTheBeninging please post actual code. How should we know that you did not forgot or add something?
Is the moc file beeing generated? I would guess not.imgproc.hpp the enum from opencv
//! type of the threshold operation //!  enum ThresholdTypes { THRESH_BINARY = 0, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{maxval}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f] THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] THRESH_MASK = 7, THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value };
cvthreshold.hpp
#include <QComboBox> #include "opencv2/opencv.hpp" Q_DECLARE_METATYPE(cv::ThresholdTypes) class CVThresholdTypeComboBox : public QComboBox { Q_OBJECT Q_ENUM(cv::ThresholdTypes) Q_PROPERTY(cv::ThresholdTypes threshType READ threshType WRITE setThreshType USER true) public: CVThresholdTypeComboBox(QWidget *widget = nullptr); cv::ThresholdTypes threshType() const; void setThreshType(const cv::ThresholdTypes &threshType); private: void populateList(); };
cvthreshold.cpp
#include "cvthreshold.h" CVThresholdTypeComboBox::CVThresholdTypeComboBox(QWidget *widget) : QComboBox(widget) { populateList(); } cv::ThresholdTypes CVThresholdTypeComboBox::threshType() const { return qvariant_cast<cv::ThresholdTypes>(itemData(currentIndex(), qMetaTypeId<cv::ThresholdTypes>())); } void CVThresholdTypeComboBox::setThreshType(const cv::ThresholdTypes &threshType) { setCurrentIndex(findData(threshType, qMetaTypeId<cv::ThresholdTypes>())); } void CVThresholdTypeComboBox::populateList() { addItem("Binary",cv::THRESH_BINARY); addItem("Binary Inverted",cv::THRESH_BINARY_INV); addItem("Mask",cv::THRESH_MASK); addItem("Otsu",cv::THRESH_OTSU); }
cvpipelineeditorwidget.cpp
#include "cvpipelineeditorwidget.h" #include "ui_cvpipelineeditorwidget.h" #include <QDebug> #include <QComboBox> #include <QItemEditorFactory> #include "cvthreshold.h" CVPipelineEditorWidget::CVPipelineEditorWidget(QWidget *parent) : QWidget(parent), ui(new Ui::CVPipelineEditorWidget) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui->treeWidget->setHeaderLabels({"Enable","Eigenschaft","Wert"}); ui->treeWidget->setColumnWidth(0, 50); //TEST QItemEditorFactory * factory = new QItemEditorFactory; QItemEditorCreatorBase * threshTypeCreator = new QStandardItemEditorCreator<CVThresholdTypeComboBox>(); factory->registerEditor(qMetaTypeId<cv::ThresholdTypes>(), threshTypeCreator); QItemEditorFactory::setDefaultFactory(factory); //ENDTEST QTreeWidgetItem * item = new QTreeWidgetItem(ui->treeWidget); item->setCheckState(0, Qt::Checked); item->setText(1, "Threshold"); QTreeWidgetItem * threshItem = new QTreeWidgetItem(item); threshItem->setText(1, "Threshold"); threshItem->setData(2, Qt::DisplayRole, QStringList{"smtsmt","sad"}); item->addChild(threshItem); QTreeWidgetItem * threshMaxItem = new QTreeWidgetItem(item); threshMaxItem->setText(1, "Maximum"); item->addChild(threshMaxItem); QTreeWidgetItem * threshTypeItem = new QTreeWidgetItem(item); threshTypeItem->setText(1, "Algorithm"); threshTypeItem->setData(2, Qt::DisplayRole, cv::THRESH_BINARY); QComboBox * threshTypeCombobox = new QComboBox; threshTypeCombobox->addItem("Binary",cv::THRESH_BINARY); threshTypeCombobox->addItem("Binary Inverted",cv::THRESH_BINARY_INV); threshTypeCombobox->addItem("Mask",cv::THRESH_MASK); threshTypeCombobox->addItem("Otsu",cv::THRESH_OTSU); // ui->treeWidget->setItemWidget(threshTypeItem, 2, threshTypeCombobox); QObject::connect(ui->treeWidget, &QTreeWidget::itemChanged, this, [this]{ qDebug() << "Item data changed"; }); }
No moc file being generated.
-
imgproc.hpp the enum from opencv
//! type of the threshold operation //!  enum ThresholdTypes { THRESH_BINARY = 0, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{maxval}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f] THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f] THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f] THRESH_MASK = 7, THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value };
cvthreshold.hpp
#include <QComboBox> #include "opencv2/opencv.hpp" Q_DECLARE_METATYPE(cv::ThresholdTypes) class CVThresholdTypeComboBox : public QComboBox { Q_OBJECT Q_ENUM(cv::ThresholdTypes) Q_PROPERTY(cv::ThresholdTypes threshType READ threshType WRITE setThreshType USER true) public: CVThresholdTypeComboBox(QWidget *widget = nullptr); cv::ThresholdTypes threshType() const; void setThreshType(const cv::ThresholdTypes &threshType); private: void populateList(); };
cvthreshold.cpp
#include "cvthreshold.h" CVThresholdTypeComboBox::CVThresholdTypeComboBox(QWidget *widget) : QComboBox(widget) { populateList(); } cv::ThresholdTypes CVThresholdTypeComboBox::threshType() const { return qvariant_cast<cv::ThresholdTypes>(itemData(currentIndex(), qMetaTypeId<cv::ThresholdTypes>())); } void CVThresholdTypeComboBox::setThreshType(const cv::ThresholdTypes &threshType) { setCurrentIndex(findData(threshType, qMetaTypeId<cv::ThresholdTypes>())); } void CVThresholdTypeComboBox::populateList() { addItem("Binary",cv::THRESH_BINARY); addItem("Binary Inverted",cv::THRESH_BINARY_INV); addItem("Mask",cv::THRESH_MASK); addItem("Otsu",cv::THRESH_OTSU); }
cvpipelineeditorwidget.cpp
#include "cvpipelineeditorwidget.h" #include "ui_cvpipelineeditorwidget.h" #include <QDebug> #include <QComboBox> #include <QItemEditorFactory> #include "cvthreshold.h" CVPipelineEditorWidget::CVPipelineEditorWidget(QWidget *parent) : QWidget(parent), ui(new Ui::CVPipelineEditorWidget) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui->treeWidget->setHeaderLabels({"Enable","Eigenschaft","Wert"}); ui->treeWidget->setColumnWidth(0, 50); //TEST QItemEditorFactory * factory = new QItemEditorFactory; QItemEditorCreatorBase * threshTypeCreator = new QStandardItemEditorCreator<CVThresholdTypeComboBox>(); factory->registerEditor(qMetaTypeId<cv::ThresholdTypes>(), threshTypeCreator); QItemEditorFactory::setDefaultFactory(factory); //ENDTEST QTreeWidgetItem * item = new QTreeWidgetItem(ui->treeWidget); item->setCheckState(0, Qt::Checked); item->setText(1, "Threshold"); QTreeWidgetItem * threshItem = new QTreeWidgetItem(item); threshItem->setText(1, "Threshold"); threshItem->setData(2, Qt::DisplayRole, QStringList{"smtsmt","sad"}); item->addChild(threshItem); QTreeWidgetItem * threshMaxItem = new QTreeWidgetItem(item); threshMaxItem->setText(1, "Maximum"); item->addChild(threshMaxItem); QTreeWidgetItem * threshTypeItem = new QTreeWidgetItem(item); threshTypeItem->setText(1, "Algorithm"); threshTypeItem->setData(2, Qt::DisplayRole, cv::THRESH_BINARY); QComboBox * threshTypeCombobox = new QComboBox; threshTypeCombobox->addItem("Binary",cv::THRESH_BINARY); threshTypeCombobox->addItem("Binary Inverted",cv::THRESH_BINARY_INV); threshTypeCombobox->addItem("Mask",cv::THRESH_MASK); threshTypeCombobox->addItem("Otsu",cv::THRESH_OTSU); // ui->treeWidget->setItemWidget(threshTypeItem, 2, threshTypeCombobox); QObject::connect(ui->treeWidget, &QTreeWidget::itemChanged, this, [this]{ qDebug() << "Item data changed"; }); }
No moc file being generated.
@InTheBeninging said in QItemEditorCreatorBase with custom enum:
No moc file being generated
So what build system do you use? Without the moc file you will get this linker error
-
Can someone give me a working example how to build a custom editor factory for a QComboBox with a custom enum as data? I tried following this example, but It won't work.
My Code Looks like this://hpp Q_DECLARE_METATYPE(EnumType) class EnumTypeComboBox : public QComboBox { Q_OBJECT Q_ENUM(EnumType) Q_PROPERTY(EnumType enumType READ enumType WRITE setEnumType USER true) public: EnumTypeComboBox (QWidget *widget = nullptr); EnumType enumType() const; void setEnumType (const EnumType &enumType); private: void populateList(); }; //cpp EnumTypeComboBox::EnumTypeComboBox (QWidget *widget) : QComboBox(widget) { populateList(); } EnumType EnumTypeComboBox::enumType() const { return qvariant_cast<EnumType>(itemData(currentIndex(), qMetaTypeId<EnumType>())); } void EnumTypeComboBox::setEnumType(const EnumType &enumType) { setCurrentIndex(findData(enumType, qMetaTypeId<EnumType>())); } void EnumTypeComboBox::populateList() { addItem("First Type",EnumType::FirstType); } //registration QItemEditorFactory * factory = new QItemEditorFactory; QItemEditorCreatorBase * enumTypeCreator= new QStandardItemEditorCreator<EnumTypeComboBox>(); factory->registerEditor(qMetaTypeId<EnumType>(), enumTypeCreator); QItemEditorFactory::setDefaultFactory(factory);
It gives me 4 of these Linker Errors:
editorwidget.obj:-1: Fehler: LNK2001: Nicht aufgelöstes externes Symbol ""public: static struct QMetaObject const EnumTypeComboBox::staticMetaObject" (?staticMetaObject@EnumTypeComboBox@@2UQMetaObject@@B)".Things that are not clear to me:
The enum is included from a seperate header file. Can I even use it as Q_ENUM in my own class? Doesn't it say it adds the enum to a qmetatype automatically? Doing so results in the C:\Qt\5.15.2\msvc2019\include\QtCore\qmetatype.h:1916: Fehler: C2338: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system Error.@InTheBeninging said in QItemEditorCreatorBase with custom enum:
The enum is included from a seperate header file. Can I even use it as Q_ENUM in my own class?
That's not possible.
The Q_ENUM declaration has to be in the class where the enum is defined.
This class has to also have the Q_OBJECT macro, or (if the Q_OBJECT overhead is not needed, Q_GADGET). -
Thanks for the hints @Christian-Ehrlicher and @Axel-Spoerl. I cleared my build folder before rebuilding and removed the Q_ENUM and it works now. The only problem is, that instead of showing the enums name the treewidgets cell remains empty after a selection as shown in the lower half of the image.
This is likely because Q_ENUM can't be used here. Is there any other way to add QString representations to custom MetaTypes? -
Thanks for the hints @Christian-Ehrlicher and @Axel-Spoerl. I cleared my build folder before rebuilding and removed the Q_ENUM and it works now. The only problem is, that instead of showing the enums name the treewidgets cell remains empty after a selection as shown in the lower half of the image.
This is likely because Q_ENUM can't be used here. Is there any other way to add QString representations to custom MetaTypes?@InTheBeninging
As said already: Use theQ_OBJECT
orQ_GADGET
macro. Or implement a manual conversion function. -
Ok it works now. It just needed some changes.
I had to change the property function to save the data with the Qt::UserRole. I guess because the QComboBox::addItem(QString text, QVariant userData) function used to populate the box uses the Qt::UserRole by default.cv::ThresholdTypes CVThresholdTypeComboBox::threshType() const { return qvariant_cast<cv::ThresholdTypes>(itemData(currentIndex(), Qt::UserRole)); } void CVThresholdTypeComboBox::setThreshType(const cv::ThresholdTypes &threshType) { setCurrentIndex(findData(threshType, Qt::UserRole)); }
The conversion function to get a string representation of my custom metatype looks as follows.
QString CVThresholdTypeComboBox::thresholdToString(const cv::ThresholdTypes &threshType) { switch(threshType) { case cv::THRESH_BINARY: return "Binary"; case cv::THRESH_BINARY_INV: return "Binary Inverted"; case cv::THRESH_TRUNC: return "Truncated"; case cv::THRESH_TOZERO: return "To Zero"; case cv::THRESH_TOZERO_INV: return "To Zero Inverted"; case cv::THRESH_MASK: return "Mask"; case cv::THRESH_OTSU: return "Otsu"; case cv::THRESH_TRIANGLE: return "Triangle"; default: return "Default"; } }
The conversion function is registered with this function.
QMetaType::registerConverter<cv::ThresholdTypes, QString>(&CVThresholdTypeComboBox::thresholdToString);
-
Ok it works now. It just needed some changes.
I had to change the property function to save the data with the Qt::UserRole. I guess because the QComboBox::addItem(QString text, QVariant userData) function used to populate the box uses the Qt::UserRole by default.cv::ThresholdTypes CVThresholdTypeComboBox::threshType() const { return qvariant_cast<cv::ThresholdTypes>(itemData(currentIndex(), Qt::UserRole)); } void CVThresholdTypeComboBox::setThreshType(const cv::ThresholdTypes &threshType) { setCurrentIndex(findData(threshType, Qt::UserRole)); }
The conversion function to get a string representation of my custom metatype looks as follows.
QString CVThresholdTypeComboBox::thresholdToString(const cv::ThresholdTypes &threshType) { switch(threshType) { case cv::THRESH_BINARY: return "Binary"; case cv::THRESH_BINARY_INV: return "Binary Inverted"; case cv::THRESH_TRUNC: return "Truncated"; case cv::THRESH_TOZERO: return "To Zero"; case cv::THRESH_TOZERO_INV: return "To Zero Inverted"; case cv::THRESH_MASK: return "Mask"; case cv::THRESH_OTSU: return "Otsu"; case cv::THRESH_TRIANGLE: return "Triangle"; default: return "Default"; } }
The conversion function is registered with this function.
QMetaType::registerConverter<cv::ThresholdTypes, QString>(&CVThresholdTypeComboBox::thresholdToString);
@InTheBeninging
Nice, good that you got it working.Some minor beautification nits:
- text values could be stored in an array and the enum value be used as the index.
- the function could be
static constexpr
. - in switches over enums, I don't use
default
. That way, the compiler rants at me, if I have (again!!) forgotten to implement new enum values.
Q_UNREACHABLE()
can be added after the switch, but it doesn't likestatic constexpr
on some platforms.
-
I InTheBeninging has marked this topic as solved on