PDA

View Full Version : Custom Property On Custom Widget



Ashutosh2k1
23rd June 2011, 13:30
Hi All,
I need a very urget clarification .Can we show the Custom Property (Like of Struct where each type of struct may be used Defined here the Struct is user defined ) in the Qt Designer by any means ?.
As i Know there are two ways Either Q_PROPERTY Which works for the Qt defined type and the other is QDesignerPropertySheetExtension
Currently i am using the QDesinerPropertySheetExtension but the Property class is not coming
So plz help me

Santosh Reddy
25th June 2011, 12:56
How did you use QDesinerPropertySheetExtension?

Ashutosh2k1
27th June 2011, 06:54
I have used the qDesignerPropertySheet Extension as it was mentioned in the doc .But i dont know how to display the custom Property because anyway i have not found any method for showing the custom Property only get set or Property group methods are available

Santosh Reddy
27th June 2011, 07:35
I hope you trying to create a custom widget plugin for Qt designer, is it so?

Ashutosh2k1
27th June 2011, 07:51
ya and i want to give the custom(compound ) property to my custom widget . I have read somewhere that this could be possible only from QdesignerPropertySheet extension but i am not able to do this .

Santosh Reddy
27th June 2011, 08:22
The QDesignerPropertySheetExtension class allows you to manipulate a widget's properties which is displayed in Qt Designer's property editor. You should be proving the properties information in your QDesignerCustomWidgetInterface

Ashutosh2k1
27th June 2011, 08:32
Hi
Thanks For reply. I can manipulate widget property through QDesignerPropertySheetExtension But where i have to provide my property in QDesignerCustomWidgetInterface . Can u plz elaborate what u r saying

Santosh Reddy
27th June 2011, 08:33
Did you read this example (http://doc.qt.nokia.com/latest/designer-customwidgetplugin.html) ?

Ashutosh2k1
27th June 2011, 08:52
Hi
I have just read the example .I think The only place where i can provide the compound Property is domxml function .But my Compound Property is not coming on the widget property editor so what i am missing .

I am giving u some design what i am doing
I have created a CustomProp Class Its Plugin CustomPropPlugin
Exteansion Class Subclassed from QDesignerPropertySheetExtension and the factory class derived from QExtensionFactory Ok
Now In the initialize function of plugin i am getting the extension class of Qt designer and throught that i register the manager class
I have also implemented just the method of QDesginerPropertyExtension in my Extension Class
So am i missing something or doing something wrong

Santosh Reddy
27th June 2011, 09:10
How does your domXml() look?

Ashutosh2k1
27th June 2011, 09:26
return "<widget class=\"CustomWidgetPropertyShow\" name=\"customWidgetPropertyShow\">\n"
" <property name=\"geometry\">\n"
" <rect>\n"
" <x>0</x>\n"
" <y>0</y>\n"
" <width>100</width>\n"
" <height>100</height>\n"
" </rect>\n"
" </property>\n"
"</widget>\n";

Also if add something it will come as dynamic property .I have just checked with adding something but it gives the flavour of dynamic Property which i don't want .I wnat the custom struct to appear as static Property

Santosh Reddy
27th June 2011, 09:31
May be I asked a wrong question, we should be looking at Extension class, did you properly implement these

virtual QVariant property ( int index ) const = 0
virtual QString propertyGroup ( int index ) const = 0
virtual QString propertyName ( int index ) const = 0

Ashutosh2k1
27th June 2011, 10:27
Hi
I have not found very clear explanation of implementation of these function in the documenatation .so can provide me some stuff on this

Santosh Reddy
27th June 2011, 10:42
Most of the functions should be clear enough, provided you understand how Qt Designer works. What did you implement in the virtual functions of Extension class?

All these virtual functions should be implemented properly for the widget custom properties to be displayed in Qt Designer.

for example if your widget has 2 QString properties "PRO1", "PRO2" then implementation would like this,


int count() const { return 2; }

QVariant property(int index) const
{
if(index == 0) return pro1;
else if(index == 1) return pro2;
else return QVariant;
}

QString propertyName(int index) const
{
if(index == 0) return QString("PRO1");
else if(index == 1) return QString("PRO2");
else return QString;
}


note you need to implement all the functions properly..

Ashutosh2k1
27th June 2011, 12:58
Hi
very very Thanks. I think that now i can implement the Custom (Compound Property ) On the Widget .

Ashutosh2k1
28th June 2011, 07:01
Hi,
If I implement my own property sheet extension so in this case i will miss the default property editor given by the widget or the qt designer . But i want to use the both property editor the one provided by the QT designer and other which is written by me So is there any way

Santosh Reddy
28th June 2011, 10:05
I not very sure, but this should work, as per documentation


"When implementing a custom widget plugin, a pointer to Qt Designer's current QDesignerFormEditorInterface object is provided by the QDesignerCustomWidgetInterface::initialize() function's parameter."

From the QDesignerFormEditorInterface object, get the previous property editor interface instance, and map the properties in your class, like map 0 to n properties from previous property editor, and n to m are your custom properties, as you know you can get and set the 0 to n properties using previous property editor interface instance

Ashutosh2k1
28th June 2011, 11:37
Hi
The Problem is that how can i map those property becoz In the QDesignerPropertyEditorInterface No method is there ,also if i will go for meta object and get the Property through meta object i am not sure it will work .However i am doing work on this
By the way Thanks For Reply Sir

ViRuSTriNiTy
29th June 2012, 13:13
If I implement my own property sheet extension so in this case i will miss the default property editor given by the widget or the qt designer . But i want to use the both property editor the one provided by the QT designer and other which is written by me So is there any way

@Ashutosh2k1

I had to solve the same issue. Here is what i came up with after several experiments.

First declare a custom property sheet factory:


class CCustomWidgetPropertySheetFactory : public QExtensionFactory
{
typedef QExtensionFactory inherited;

public:
CCustomWidgetPropertySheetFactory(QExtensionManage r * apParent);

protected:
virtual QObject * createExtension(QObject * apObject, const QString & arIId, QObject * apParent) const;

private:
QExtensionManager * mpExtensionManager;
mutable bool mCreateExtensionCalled;
};

...then add the implementation of the factory...


CCustomWidgetPropertySheetFactory::CCustomWidgetPr opertySheetFactory(QExtensionManager * apParent)
: inherited(apParent)
, mpExtensionManager(apParent)
, mCreateExtensionCalled(false)
{}

QObject * CCustomWidgetPropertySheetFactory::createExtension (QObject * apObject, const QString & arIId, QObject /* QExtensionFactory */ * apParent) const
{
QObject * lResult(0);

if (!mCreateExtensionCalled)
{
// note: setting flag to avoid infinite loop caused by later usage of qt_extension<>
mCreateExtensionCalled = true;

if (arIId == Q_TYPEID(QDesignerPropertySheetExtension))
{
CCustomWidget * lCustomWidget(qobject_cast<CCustomWidget *>(apObject));
if (lCustomWidget)
{
QDesignerPropertySheetExtension * lDefaultPropertySheetExtensionImplementor(qt_exten sion<QDesignerPropertySheetExtension *>(mpExtensionManager, apObject));
if (lDefaultPropertySheetExtensionImplementor)
{
lResult = new CCustomWidgetPropertySheetExtension(apParent, *lDefaultPropertySheetExtensionImplementor);
}
}
}

mCreateExtensionCalled = false;
}

return lResult;
}

So far we created a custom property sheet and pass the default property sheet to it. Now declare the custom property sheet (i simplified the code for better understanding here):


class CCustomWidgetPropertySheetExtension : public QObject, public QDesignerPropertySheetExtension
{
Q_OBJECT
Q_INTERFACES(QDesignerPropertySheetExtension)

public:
CCustomWidgetPropertySheetExtension(QObject * apParent,
QDesignerPropertySheetExtension & apDefaultPropertySheetExtension);
virtual ~CCustomWidgetPropertySheetExtension();

virtual int count() const;

virtual int indexOf(const QString &name) const;

virtual QString propertyName(int index) const;

virtual QString propertyGroup(int index) const;

virtual bool isVisible(int index) const;

virtual QVariant property(int index) const;

...

private:
QDesignerPropertySheetExtension & mpDefaultPropertySheetExtension;
int mPropertyCount;
};

...and add the implementation of the custom property sheet...


CCustomWidgetPropertySheetExtension::CCustomWidget PropertySheetExtension(QObject * apParent,
QDesignerPropertySheetExtension & apDefaultPropertySheetExtension)
: QObject(apParent)
, QDesignerPropertySheetExtension()
, mpDefaultPropertySheetExtension(apDefaultPropertyS heetExtension)
, mPropertyCount(mpDefaultPropertySheetExtension.cou nt() + 1)
{}

CCustomWidgetPropertySheetExtension::~CCustomWidge tPropertySheetExtension()
{}

...

int CCustomWidgetPropertySheetExtension::count() const
{
return mPropertyCount;
}

int CCustomWidgetPropertySheetExtension::indexOf( const QString& name ) const
{
if (name == "MyCustomProperty")
return mPropertyCount - 1;
else
return mpDefaultPropertySheetExtension.indexOf(name);
}

QString CCustomWidgetPropertySheetExtension::propertyName( int index ) const
{
if (index == mPropertyCount - 1)
return "MyCustomProperty";
else
return mpDefaultPropertySheetExtension.propertyName(index );
}

QString CCustomWidgetPropertySheetExtension::propertyGroup ( int index ) const
{
if (index == mPropertyCount - 1)
return "MyCustomPropertyGroup";
else
return mpDefaultPropertySheetExtension.propertyGroup(inde x);
}

bool CCustomWidgetPropertySheetExtension::isVisible( int index ) const
{
if (index == mPropertyCount - 1)
return true;
else
return mpDefaultPropertySheetExtension.isVisible(index);
}

QVariant CCustomWidgetPropertySheetExtension::property( int index ) const
{
if (index == mPropertyCount - 1)
return QVariant(true);
else
return mpDefaultPropertySheetExtension.property(index);
}

Compile and you will see the custom property "MyCustomProperty" displayed in the designer in the property group "MyCustomPropertyGroup" for the widget "CCustomWidget".

Hope this helps.

So lonG
Daniel

Olumide
23rd October 2014, 16:33
Hello Daniel, thanks for your post. I've tried to follow your example. Unfortunately the cast



QDesignerPropertySheetExtension* defaultPropertySheetExtension = qt_extension<QDesignerPropertySheetExtension*>( m_extensionManager , object );


makes Qt Designer crash.

ViRuSTriNiTy
24th October 2014, 10:29
...makes Qt Designer crash.

What kind of crash exactly? Do you get a stack strace, exception message or anything like that?

Olumide
24th October 2014, 12:25
What kind of crash exactly? Do you get a stack strace, exception message or anything like that?
I've just found the cause of the problem. I did not guard against recursive calls to createExtension(). I've just done so however and I no longer get crashes, although I used a static variable like so



QObject* CustomWidgetFactory::createExtension(QObject *object, const QString &iid, QObject* parent ) const
{
static bool extensionSheetIsInitialized = false;

if( !extensionSheetIsInitialized )
{
extensionSheetIsInitialized = true;
...
}

return 0;
}


There is now the larger issue of how to proceed. How do I assign my indexes to my widget properties? So that I can control whether they are shown in the the properties editor? Ideally what I'd like to do is to display a subset of relevant properties of the widget depending on the value of a master property. For example, the master property could represent the shape of the wigdet while the remaining properties could include radius (for circles), length for squared etc, where the radius property is displayed only of the mater property is set to circle.

ViRuSTriNiTy
24th October 2014, 14:15
I've just found the cause of the problem. I did not guard against recursive calls to createExtension()

Yep, thats why i added the red comment to mCreateExtensionCalled, back then i stumble upon this recursion too.



How do I assign my indexes to my widget properties? So that I can control whether they are shown in the the properties editor? Ideally what I'd like to do is to display a subset of relevant properties of the widget depending on the value of a master property. For example, the master property could represent the shape of the wigdet while the remaining properties could include radius (for circles), length for squared etc, where the radius property is displayed only of the mater property is set to circle.

The only solution i had found was to use a "index"-"property name" map. With that in mind i would suggest you

1) enumerate all properties of your widget in the constructor of the property sheet class
2) remember those properties in the "index"-"property name" map
3) override each virtual function of the property sheet and return the appropriate property values by using the "index"-"property name" map

I'm sorry that i cannot post any additional code because it's lost. But i closely remember what i was doing back then.

Olumide
24th October 2014, 14:58
The only solution i had found was to use a "index"-"property name" map. With that in mind i would suggest you

1) enumerate all properties of your widget in the constructor of the property sheet class
2) remember those properties in the "index"-"property name" map
3) override each virtual function of the property sheet and return the appropriate property values by using the "index"-"property name" map

I'm sorry that i cannot post any additional code because it's lost. But i closely remember what i was doing back then.

Thanks for your reply.

Unfortunately, I'm getting really confused. First, I have difficulty understanding the the way indexes are assigned by Qt Designer to properties. Surely custom properties have indexes even if a custom property sheet does not exist. So does a custom property sheet prevent Qt Designer from assigning a property to a custom property? And what index ranges should be assigned to custom properties? From 0 (as Santosh Reddy suggests (http://www.qtcentre.org/threads/42671-Custom-Property-On-Custom-Widget?p=195474#post195474)) or from mpDefaultPropertySheetExtension.count() , as you suggest? Also custom properties display the correct names given to them when declared with Q_PROPERTY(). What then is the purpose of QDesignerPropertySheetExtension::propertyName()?

What's more confusing is that nothing I do in my custom property sheet extension appears to show up in Qt Designer, even an exit() call or QCoreApplication::exit() does nothing. Furthermore without the use of a debugger I can't be absolutely sure that qt_extension<> returns a non-NULL default property sheet and that my custom property sheet is instantiated.

Very confused.

:(

ViRuSTriNiTy
24th October 2014, 16:38
Thanks for your reply.

Unfortunately, I'm getting really confused.


Dont be sad, being confused is a normal reaction to this stuff as almost no example code exists. I had to solve almost everything on my own by building a debug version of Qt and debugging the inner core of Qt to get this custom property stuff working. This was a hell of a nightmare as in the end only version 4.7.0 and 4.8.2 compiled successfully without showing Qt crashes later on.



First, I have difficulty understanding the the way indexes are assigned by Qt Designer to properties. Surely custom properties have indexes even if a custom property sheet does not exist.


As far as i remember: the Qt designer uses the custom property sheet if defined. A custom property sheet overrides the complete property list displayed in the designer. Since you are enumerating all properties in the constructor of the property sheet class, you can implement count() like so:


... ::count ()
{
return mpIndexPropertyNameMap.size();
}

The Qt designer will then execute a index loop from 0 to count() - 1 and call additional methods like propertyName(), isVisible() and so on to display the properties accordingly in the property editor.



What's more confusing is that nothing I do in my custom property sheet extension appears to show up in Qt Designer, even an exit() call or QCoreApplication::exit() does nothing. Furthermore without the use of a debugger I can't be absolutely sure that qt_extension<> returns a non-NULL default property sheet and that my custom property sheet is instantiated.


This is what i was referring to in the first comment of this post. After hours of trying to solve this via trail and error with exit() or writing log text files i compiled Qt as debug build. But you don't really have to go that hellish route, just try to write out a log file first. This should be a quite easy task.

Olumide
24th October 2014, 17:14
Thanks.

I'm going to try logging for a start. Its really strange that exit() doesn't work yet leaving out the boolean variable/guard causes the application to call the QExtensionFactory::createExtension() recursively.

In the interim I've setting a property as DESIGNABLE with a boolean member function as a value instead of true or false. But this causes the property to be grayed out and not hidden.

BTW, what do you think of this recommendation (http://www.qtcentre.org/threads/6206-Deleting-and-disabling-properties-in-designer-for-custom-plugins?p=32185#post32185)? Trouble is that I'm not quite sure which class does this? QDesignerCustomWidgetInterface perhaps?

ViRuSTriNiTy
24th October 2014, 19:27
Its really strange that exit() doesn't work yet leaving out the boolean variable/guard causes the application to call the QExtensionFactory::createExtension() recursively.

This has something to do with the internals of Qt, you could see why it doesn't work when using a debug version of Qt. I cannot remember what it was exactly but i had the same problem too, then i changed to logging via text files.


I've setting a property as DESIGNABLE with a boolean member function as a value instead of true or false. But this causes the property to be grayed out and not hidden.

Hehe, i had the same issue too. As far as i remember the designable thing is a bit buggy, just skip it if you don't really need it. If you need a read only property in the property editor then use QDesignerPropertySheetExtension::isVisible(), there's a reason why it exists ;)



BTW, what do you think of this recommendation (http://www.qtcentre.org/threads/6206-Deleting-and-disabling-properties-in-designer-for-custom-plugins?p=32185#post32185)?

Yes, this is the way to make properties a bit more dynamic. You should keep in mind that the Qt designer buffers many values returned by QDesignerPropertySheetExtension (i cannot recall which ones exactly), it's done when Qt designer starts. I used this approach to dynamically hide / show properties by using a context menu.



Trouble is that I'm not quite sure which class does this? QDesignerCustomWidgetInterface perhaps?

Depends on what you want to achieve. As i stated above, for me it was the click handler of the context menu that executed this code. Just place it where you need it as it's based on a static function.

Olumide
27th October 2014, 11:36
Logging to a text file is surprisingly instructive. 'Shows that for some strange strange reason QExtensionFactory::createExtension() is called for QCheckBox only!??? :confused:

ViRuSTriNiTy
27th October 2014, 18:34
@Olumide

Hmm strange. You should upload your complete source code somewhere so i can have a look at it.

Olumide
28th October 2014, 13:16
Hmm strange. You should upload your complete source code somewhere so i can have a look at it.
That was bug/typo. Its now working properly, although I had to create the property extension on the parent extension and not on the widget itself, as shown below:



QDesignerPropertySheetExtension* defaultPropertySheetExtension = qt_extension<QDesignerPropertySheetExtension*>( m_extensionManager , parent );