PDA

View Full Version : Correct Member Sheet Extension Implementation



PeeAeMKAy
16th July 2009, 21:04
I'm using Qt 4.5.2 and Qt 4 Visual Studio Add-In.

I've created a custom widget plugin for Qt Designer with Visual Studio's Qt Plugin template.
The widget itself shows up in Qt Designer and works correctly if I compile a Qt Application Project which includes the plugin.

Additionally, the widget implements custom signals and slots.
Since I would like to use Visual Studio's Qt Application template and Qt Designer, I want these signals and slots to show up in Qt Designer, so I don't have to add them manually in the generated code files.

To achieve that, I've tried to implement my own Member Sheet Extension.
The plugin actually compiles without any errors or warnings, but if I try to open the popup menus in the "signals and slots" window in Qt Designer, it crashes.
Unfortunately, it doesn't give me any error messages or exceptions, it just shuts down.

So, I guess my implementation of QDesignerMemberSheetExtension (http://doc.trolltech.com/4.5/qdesignermembersheetextension.html) isn't correct or contains a bad method, but I don't have any clues what the problem is.
So far, I couldn't find a correct example implementation either so I can't compare my code to a working plugin.

I took the Task Menu Extension Example (http://doc.trolltech.com/4.5/designer-taskmenuextension.html), QDesignerMemberSheetExtension (http://doc.trolltech.com/4.5/qdesignermembersheetextension.html) and QExtensionFactory (http://doc.trolltech.com/4.5/qextensionfactory.html) as reference.

To implement the membersheet methods, I use QMetaObject (http://doc.trolltech.com/4.5/qmetaobject.html) and QMetaMethod (http://doc.trolltech.com/4.5/qmetamethod.html):


Qt4D3DCMemberSheetExtension::Qt4D3DCMemberSheetExt ension(Qt4Direct3DContainer *p_d3dc, QObject *parent) : QObject(parent)
{
m_qt4D3DC = p_d3dc;
m_qMetaObject = m_qt4D3DC->metaObject();
}

bool Qt4D3DCMemberSheetExtension::isSignal( int index ) const
{
QMetaMethod method = m_qMetaObject->method(index);
return method.methodType() == QMetaMethod::Signal;
}

Is that a reasonable thing to do?

I took the implementation of the QExtensionFactory (http://doc.trolltech.com/4.5/qextensionfactory.html) code primarily from the Task Menu Extension Example (http://doc.trolltech.com/4.5/designer-taskmenuextension.html):


QObject * Qt4D3DCExtensionFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const
{
if (iid != Q_TYPEID(QDesignerMemberSheetExtension))
return 0;

if (Qt4Direct3DContainer *widget = qobject_cast<Qt4Direct3DContainer*>(object))
{
return new Qt4D3DCMemberSheetExtension(widget, parent);
}

return NULL;
}


void Qt4Direct3DContainerPlugin::initialize(QDesignerFo rmEditorInterface *formEditor)
{
if (initialized)
return;

QExtensionManager *manager = formEditor->extensionManager();
Q_ASSERT(manager != 0);

manager->registerExtensions(new Qt4D3DCExtensionFactory(manager), Q_TYPEID(QDesignerMemberSheetExtension));

initialized = true;
}

Has anything gone wrong there?


Is there any reasonable way of debugging a custom widget plugin?

wysota
16th July 2009, 22:54
No no no... that's a wrong way. All you need to do for a slot or signal to be available in designer for a custom widget is to declare them as such in the class and remember about placing the Q_OBJECT macro. The same goes for properties - use the Q_PROPERTY macro. You seldom need to touch the member sheet extension.


class SomeWidget : public QWidget {
Q_OBJECT
Q_PROPERTY(int someProp READ getSomeProp WRITE setSomeProp)
public:
// ...
public slots:
void myCustomSlot() { ... }
signals:
void myCustomSignal(int);
};

PeeAeMKAy
17th July 2009, 00:21
No no no... that's a wrong way. All you need to do for a slot or signal to be available in designer for a custom widget is to declare them as such in the class and remember about placing the Q_OBJECT macro. The same goes for properties - use the Q_PROPERTY macro. You seldom need to touch the member sheet extension.


class SomeWidget : public QWidget {
Q_OBJECT
Q_PROPERTY(int someProp READ getSomeProp WRITE setSomeProp)
public:
// ...
public slots:
void myCustomSlot() { ... }
signals:
void myCustomSignal(int);
};

That's what didn't work in the beginning, which is why I've tried the member sheet approach in the first place.

These are the important parts of my widget header:


class Qt4Direct3DContainer : public QWidget
{
Q_OBJECT

public:
Qt4Direct3DContainer(QWidget *parent = 0);
~Qt4Direct3DContainer();

// ...

public slots:

void turn();

private:

// ...

};

I've had the Q_OBJECT macro in there all along, I also have one public slot.
Now, if I compile that plugin and place the files in the plugins/designer folder, the widget appears in Qt Designer, but if I open the popup menu for slots, I only see the standard QWidget and QObject slots, not my custom slot.

Have I overlooked something? What do I have to do to make my custom slot appear in Qt Designer?

wysota
17th July 2009, 00:38
It is hard to say. Try cleaning the project and rebuilding it from scratch. Also make sure your slot works from within Designer (try connecting to it from the plugin's QDesignerCustomWidgetInterface::createWidget() method. See if the connect succeeds or fails.

PeeAeMKAy
17th July 2009, 02:17
It is hard to say. Try cleaning the project and rebuilding it from scratch.

I did that, but it didn't seem to have any effect.


Also make sure your slot works from within Designer (try connecting to it from the plugin's QDesignerCustomWidgetInterface::createWidget() method. See if the connect succeeds or fails.

I'm not sure I know what you mean by that.
The custom slot itself does work. If I code the signal/slot connection manually in the ui header file generated by Qt, it compiles and works properly. In the compiled application, I can press the button sending the signal, and my custom slot will be executed correctly.


QObject::connect(actionCreate_Curve, SIGNAL(triggered()), qt4Direct3DContainer, SLOT(turn()));

This is the code I manually put into the generated ui header file. So it does work that way,
it's just that I don't want to do that for every connection and every custom signal or slot I'm about to have.

I actually found a way making my custom slot appear in Qt Designer. I don't like it though...
Maybe most of you already know it, I'll still describe here, since it doesn't seem to be covered in the documentation (http://doc.trolltech.com/4.5/designer-connection-mode.html) yet.
3447
As seen in the attached image,

I go into Signals and Slots Edit Mode,
connect my custom widget with anything, e.g. itself
in the edit connection window I click on "change"
and add my custom slot there manually


From that moment on, the custom slot appears in the popup menus and can be selected. I don't have to manually add any code, which is nice.

But this isn't the regular way of doing this, is it? I still hope Qt Designer somehow recognizes my custom signals and slots by itself.

wysota
17th July 2009, 08:00
Can you show us the complete code for this widget and its plugin?

PeeAeMKAy
17th July 2009, 11:27
Can you show us the complete code for this widget and its plugin?

qt4direct3dcontainer.h

#pragma once

#ifndef QT4DIRECT3DCONTAINER_H
#define QT4DIRECT3DCONTAINER_H

#include <QtGui/QWidget>
#include <qmessagebox.h>
#include <QResizeEvent>
#include <QTimer>

#include <QtDesigner/QDesignerExportWidget>

// Direct3D Header
#include <d3d9.h>
#include <d3dx9.h>

#include "Cube.h"

class QDESIGNER_WIDGET_EXPORT Qt4Direct3DContainer : public QWidget
{
Q_OBJECT

public:
Qt4Direct3DContainer(QWidget *parent = 0);
~Qt4Direct3DContainer();

BOOL Init();
void Shut();
PDIRECT3DDEVICE9 GetDevice(){ return m_pD3D9dev; };

QPaintEngine *paintEngine() const;
void paintEvent(QPaintEvent *p_event);
void resizeEvent(QResizeEvent *p_event);

public slots:

void turn();

private:

D3DPRESENT_PARAMETERS m_d3dPP;
PDIRECT3D9 m_pD3D9;
PDIRECT3DDEVICE9 m_pD3D9dev;

QTimer *m_pqTimer;

D3DXVECTOR3 m_vEye;
D3DXVECTOR3 m_vAt;
D3DXVECTOR3 m_vUp;

D3DXMATRIX m_mtxWorld;
D3DXMATRIX m_mtxView;
D3DXMATRIX m_mtxProjection;

float m_fTurn;
bool m_bTurn;

};

#endif // QT4DIRECT3DCONTAINER_H


qt4direct3dcontainer.cpp

#include "qt4direct3dcontainer.h"

Qt4Direct3DContainer::Qt4Direct3DContainer(QWidget *parent)
: QWidget(parent)
{
m_pD3D9 = NULL;
m_pD3D9dev = NULL;
ZeroMemory(&m_d3dPP, sizeof(m_d3dPP));

m_pqTimer = NULL;

setAttribute(Qt::WA_PaintOnScreen, true);

Init();
}

Qt4Direct3DContainer::~Qt4Direct3DContainer()
{
Shut();
}

BOOL Qt4Direct3DContainer::Init()
{
Shut();

// Initialize Direct3D 9
m_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);

// Test for Success
if(m_pD3D9 == NULL)
{
QMessageBox::information(this, "Direct 3D Initialization",
"Unable to initialize Direct3D 9.");
return FALSE;
}

// Presentation Parameters

// Backbuffer Parameters
m_d3dPP.BackBufferWidth = width();
m_d3dPP.BackBufferHeight = height();
m_d3dPP.BackBufferFormat = D3DFMT_A8R8G8B8;
m_d3dPP.BackBufferCount = 1;
m_d3dPP.SwapEffect = D3DSWAPEFFECT_FLIP;
m_d3dPP.FullScreen_RefreshRateInHz = 0;
m_d3dPP.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

// Multisampling(Antialiasing) Parameters
m_d3dPP.MultiSampleType = D3DMULTISAMPLE_NONE;
m_d3dPP.MultiSampleQuality = 0;

// Window Parameters
m_d3dPP.hDeviceWindow = winId();
m_d3dPP.Windowed = TRUE;

// Depth/Stencil Buffer Parameters
m_d3dPP.EnableAutoDepthStencil = TRUE;
m_d3dPP.AutoDepthStencilFormat = D3DFMT_D24S8;

// misc. Parameters
m_d3dPP.Flags = 0;

// Initialize Direct3D 9 Device
if(!SUCCEEDED(m_pD3D9->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
winId(),
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&m_d3dPP,
&m_pD3D9dev)))
{
QMessageBox::information(this, "Direct 3D Device Initialization",
"Unable to initialize Direct3D 9 Device.");
return FALSE;
}

//Vectors
m_vEye = D3DXVECTOR3(5.0f, 2.0f, 5.0f);
m_vAt = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
m_vUp = D3DXVECTOR3(0.0f, 1.0f, 0.0f);

//Matrices
m_fTurn = 0.0f;
m_bTurn = false;
D3DXMatrixRotationY(&m_mtxWorld, m_fTurn);
D3DXMatrixLookAtLH(&m_mtxView, &m_vEye, &m_vAt, &m_vUp);
D3DXMatrixPerspectiveFovLH(&m_mtxProjection, 0.14f, width() / height(), 0.1f, 10.0f);

if(InitCubeObjects(m_pD3D9dev))
{
cube_effect->SetMatrix(cube_world, &m_mtxWorld);
cube_effect->SetMatrix(cube_view, &m_mtxView);
cube_effect->SetMatrix(cube_projection, &m_mtxProjection);

cube_effect->CommitChanges();
}

//QTimer

m_pqTimer = new QTimer(this);
connect(m_pqTimer, SIGNAL(timeout()), this, SLOT(repaint()));
m_pqTimer->start(10);

return TRUE;
}

void Qt4Direct3DContainer::turn()
{
m_bTurn = !m_bTurn;
}

void Qt4Direct3DContainer::Shut()
{
// delete QTimer
if(m_pqTimer != NULL && m_pqTimer->isActive())
{
m_pqTimer->stop();
}
delete m_pqTimer;
m_pqTimer = NULL;

ReleaseCubeObjects();

// Release Direct3D 9 Device
if(m_pD3D9dev != NULL)
{
m_pD3D9dev->Release();
}
m_pD3D9dev = NULL;

// Zero Direct3D 9 Presentation Parameters
ZeroMemory(&m_d3dPP, sizeof(m_d3dPP));

// Release Direct3D 9
if(m_pD3D9 != NULL)
{
m_pD3D9->Release();
}
m_pD3D9 = NULL;

// Matrices
ZeroMemory(&m_mtxWorld, sizeof(D3DXMATRIX));
ZeroMemory(&m_mtxView, sizeof(D3DXMATRIX));
ZeroMemory(&m_mtxProjection, sizeof(D3DXMATRIX));

// Vectors
ZeroMemory(&m_vEye, sizeof(D3DXVECTOR3));
ZeroMemory(&m_vAt, sizeof(D3DXVECTOR3));
ZeroMemory(&m_vUp, sizeof(D3DXVECTOR3));
}

QPaintEngine *Qt4Direct3DContainer::paintEngine() const
{
return NULL;
}

void Qt4Direct3DContainer::paintEvent(QPaintEvent *p_event)
{
m_pD3D9dev->Clear( 0,
NULL,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_RGBA(64, 64, 96, 255),
1.0f,
0);

m_pD3D9dev->BeginScene();

m_pD3D9dev->SetVertexDeclaration(pDeclarationPosColorNormal);
m_pD3D9dev->SetStreamSource(0, cube_vertexBuffer, 0, sizeof(VertexPosColorNormal));
m_pD3D9dev->SetIndices(cube_indexBuffer);

//m_pD3D9dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

UINT uiNumberOfPasses,
uiPass;

if(m_bTurn)
{
m_fTurn += 0.01f;
D3DXMatrixRotationY(&m_mtxWorld, m_fTurn);
}
cube_effect->SetMatrix(cube_world, &m_mtxWorld);
cube_effect->SetMatrix(cube_view, &m_mtxView);
cube_effect->SetMatrix(cube_projection, &m_mtxProjection);

cube_effect->CommitChanges();

cube_effect->Begin(&uiNumberOfPasses, 0);
for(uiPass = 0; uiPass < uiNumberOfPasses; uiPass++)
{
cube_effect->BeginPass(uiPass);
/*m_pD3D9dev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,
0,
0,
cube_numberOfVertices,
0,
12);*/

m_pD3D9dev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST,
0,
cube_numberOfVertices,
12,
cube_indices,
D3DFMT_INDEX32,
&cube_vertices,
sizeof(VertexPosColorNormal));

cube_effect->EndPass();
}
cube_effect->End();

m_pD3D9dev->EndScene();

m_pD3D9dev->Present( NULL,
NULL,
NULL,
NULL);
}

void Qt4Direct3DContainer::resizeEvent(QResizeEvent *p_event)
{
QSize newSize = p_event->size();

m_d3dPP.BackBufferWidth = newSize.rwidth();
m_d3dPP.BackBufferHeight = newSize.rheight();

D3DXMatrixPerspectiveFovLH(&m_mtxProjection, 1.6f, (float)m_d3dPP.BackBufferWidth / (float)m_d3dPP.BackBufferHeight, 0.1f, 10.0f);

ReleaseCubeObjects();

m_pD3D9dev->Reset(&m_d3dPP);

InitCubeObjects(m_pD3D9dev);
}


qt4direct3dcontainerplugin.h

#ifndef QT4DIRECT3DCONTAINERPLUGIN_H
#define QT4DIRECT3DCONTAINERPLUGIN_H

#include <QDesignerCustomWidgetInterface>
#include <QDesignerMemberSheetExtension>
#include <QExtensionManager>
#include <QDesignerFormEditorInterface>

class Qt4Direct3DContainerPlugin : public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)

public:
Qt4Direct3DContainerPlugin(QObject *parent = 0);

bool isContainer() const;
bool isInitialized() const;
QIcon icon() const;
QString domXml() const;
QString group() const;
QString includeFile() const;
QString name() const;
QString toolTip() const;
QString whatsThis() const;
QWidget *createWidget(QWidget *parent);
void initialize(QDesignerFormEditorInterface *core);

private:
bool initialized;
};

#endif // QT4DIRECT3DCONTAINERPLUGIN_H


qt4direct3dcontainerplugin.cpp

#include "qt4direct3dcontainer.h"

#include <QtCore/QtPlugin>
#include "Qt4D3DCExtensionFactory.h"
#include "qt4direct3dcontainerplugin.h"


Qt4Direct3DContainerPlugin::Qt4Direct3DContainerPl ugin(QObject *parent)
: QObject(parent)
{
initialized = false;
}

void Qt4Direct3DContainerPlugin::initialize(QDesignerFo rmEditorInterface *formEditor)
{
if (initialized)
return;

initialized = true;
}

bool Qt4Direct3DContainerPlugin::isInitialized() const
{
return initialized;
}

QWidget *Qt4Direct3DContainerPlugin::createWidget(QWidget *parent)
{
return new Qt4Direct3DContainer(parent);
}

QString Qt4Direct3DContainerPlugin::name() const
{
return "Direct3D Container";
}

QString Qt4Direct3DContainerPlugin::group() const
{
return "Containers";
}

QIcon Qt4Direct3DContainerPlugin::icon() const
{
return QIcon(":/icons/directx.png");
}

QString Qt4Direct3DContainerPlugin::toolTip() const
{
return QString();
}

QString Qt4Direct3DContainerPlugin::whatsThis() const
{
return QString();
}

bool Qt4Direct3DContainerPlugin::isContainer() const
{
return false;
}

QString Qt4Direct3DContainerPlugin::domXml() const
{
return "<widget class=\"Qt4Direct3DContainer\" name=\"qt4Direct3DContainer\">\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";
}

QString Qt4Direct3DContainerPlugin::includeFile() const
{
return "qt4direct3dcontainer.h";
}

Q_EXPORT_PLUGIN2(qt4direct3dcontainer, Qt4Direct3DContainerPlugin)

PeeAeMKAy
17th July 2009, 13:13
Okay, next weird thing:

I've created another Qt Designer Plugin project in Visual Studio and created some dummy public slots.

Well after compiling and putting the files into he plugins folder, the dummy slots appear in Qt Designer without problems.

So, I guess something has to be missing from my actual Widget...

PeeAeMKAy
17th July 2009, 15:47
Alright,

I've solved the problem:

I've created a new Qt Plugin project and copied the code from the old one.
I can see the custom slots now.
Also, the preview of my plugin is working correctly now, previously Qt Designer wouldn't draw anything inside the bounding boxes of my widget.

So I can only guess some of the preferences or settings of my old project were incorrect.
Unfortunately, I can't tell which ones, but I'm glad it works now :-)


Try cleaning the project and rebuilding it from scratch.

Well that kind of was the solution. So, thanks wysota ;-)