TL;DR
I want to register a Qt Designer extension but don't want any widget plugin, so any of the following can solve my problem:
QDesignerCustomWidgetInterface
?I'm working on a set of plugins for Qt Designer. These plugins expose custom widgets to the designer. All widgets (some dozens) inherit from a common class (CCommonWidget
), such as:
CCommonWidget
|-> CLabel
|-> CPushButton
...
CCommonWidget
define some common properties for all widgets. I'd like to expose them to Qt Designer through extensions (for example, the QDesignerTaskMenuExtension
).
I started with a test in the CLabel
plugin. Here the two relevant methods:
// Register the extensions in Qt Designer
void CLabelPlugin::initialize(QDesignerFormEditorInterface *formEditor)
{
if (m_initialized) return;
auto extensionManager = formEditor->extensionManager();
Q_ASSERT(extensionManager);
extensionManager->registerExtensions(new CLabelPluginFactory(extensionManager), Q_TYPEID(QDesignerTaskMenuExtension));
m_initialized = true;
}
// The factory creates the menu extension if the widget is a CLabel
QObject* CLabelPluginFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const
{
if (iid != Q_TYPEID(QDesignerTaskMenuExtension)) return nullptr;
if (auto label = dynamic_cast<CLabel*>(object))
return new CLabelPluginMenu(label, parent);
return nullptr;
}
It worked flawlessly and I was about to extend the idea to the rest of the plugins. Instead of copy/pasting the code I started with a CCommonPlugin
and make every one to inherit from it. In order to make it as re-usable as possible I changed the createExtension
method to:
QObject* CCommonPluginFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const
{
if (iid != Q_TYPEID(QDesignerTaskMenuExtension)) return nullptr;
if (auto label = dynamic_cast<CCommonWidget*>(object))
return new CCommnPluginMenu(label, parent);
return nullptr;
}
Here I realized that even if only on plugin (CLabelPlugin
) was registering the extension factory, any other widget which inherits from CCommonWidget
will show the menu! (It was pretty obvious once I discovered it but before I just didn't think on it).
Now it was easier since I hadn't to change all the plugins (dozens) but just to create a new one, a dummy common plugin, that registers the extension factory.
The dummy plugin I've first created:
class CPluginCommon : public QObject, public QDesignerCustomWidgetInterface {
Q_OBJECT
Q_INTERFACES( QDesignerCustomWidgetInterface )
public:
explicit CPluginCommon( QObject* parent=0 );
public: // QDesignerCustomWidgetInterface
QWidget* createWidget( QWidget* parent ) { return nullptr; }
QString group() const { return QString(); }
QIcon icon() const { return QIcon(); }
QString includeFile() const { return QString(); }
bool isContainer() const { return false; }
QString name() const { return QString(); }
QString toolTip() const { return QString(); }
QString whatsThis() const { return QString(); }
virtual bool isInitialized() const override {
return m_initialized;
}
virtual void initialize(QDesignerFormEditorInterface *formEditor) override;
private:
bool m_initialized;
};
But a blank widget is displayed in the widget box of Qt Designer. I don't want an empty widget, I want no widget!
Another option is not to use these kind of plugins but I'm struggling to find a way to register the extension without a QDesignerCustomWidgetInterface
, but all that I can find is to get the extensions manager within QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface *formEditor)
(using formEditor->extensionManager()
).
Quick answer
To hide the widget from the widget box just return an empty XML (this is an undocumented feature):
class CPluginCommon : public QObject, public QDesignerCustomWidgetInterface {
// ...
public:
QString domXml() const { return QString(); }
};
Reviewing the code of the plugins manager of Qt Designer, I found in the QDesignerPluginManagerPrivate::addCustomWidget
method the following (c
is a pointer to QDesignerCustomWidgetInterface
)
const QString domXml = c->domXml();
if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box.
Given that, I saw that the default implementation of domXml
does return a tiny XML fragment for a generic widget:
virtual QString domXml() const
{
return QString::fromUtf8("<widget class=\"%1\" name=\"%2\"/>")
.arg(name()).arg(name().toLower());
}
For completeness, as far as I have seen in the source code of the plugins manager of the Qt Designer, there is no way to load a plugin that doesn't inherits from QDesignerCustomWidgetInterface
:
// Load plugins into widget database and factory.
void QDesignerIntegration::initializePlugins(QDesignerFormEditorInterface *formEditor)
{
// load the plugins
WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase*>(formEditor->widgetDataBase());
if (widgetDataBase) {
widgetDataBase->loadPlugins();
}
if (WidgetFactory *widgetFactory = qobject_cast<WidgetFactory*>(formEditor->widgetFactory())) {
widgetFactory->loadPlugins();
}
if (widgetDataBase) {
widgetDataBase->grabDefaultPropertyValues();
}
}
where
void WidgetDataBase::loadPlugins()
{
// ...
// 2) create a list plugins
ItemList pluginList;
const QDesignerPluginManager *pm = m_core->pluginManager();
foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets())
pluginList += createCustomWidgetItem(c, pm->customWidgetData(c));
// ...
}
void WidgetFactory::loadPlugins()
{
m_customFactory.clear();
QDesignerPluginManager *pluginManager = m_core->pluginManager();
QList<QDesignerCustomWidgetInterface*> lst = pluginManager->registeredCustomWidgets();
foreach (QDesignerCustomWidgetInterface *c, lst) {
m_customFactory.insert(c->name(), c);
}
}