PDA

View Full Version : Dreaded Diamond Issue with QGraphicsWidget and QObject



perden
15th June 2010, 21:19
In short, I'm looking for a way to get around a multiple inheritance problem and would like to know if there's a Qt-esque solution for what I'm trying to achieve. Read on for more info.

I've developed an abstract base class, Foo, as a subclass of QGraphicsProxyWidget for a number of different QWidget-based graphics items, WarmFoo and HotFoo. These items share a lot of the same code in the Foo, most importantly various features of QObject, including Q_PROPERTY definitions, and signals / slots. Everything has worked fine, but I want to expand the capabilities a bit more.

I want to derive a new subclass of QGraphicsWidget, ColdFoo, to take advantage of some of the performance increases you get by avoiding QGraphicsProxyWidget. But I'd also like to derive the same class from Foo to use the methods and attributes I've built up for the widget-based items. The class inheritances that I've come up with so far all have a dreaded diamond (http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8) leading to a ambiguous base class error.

Here is the current inheritance diagram along with some other structures I've considered to solve the problem.


/*
* QGraphicsProxyWidget
* ^
* |
* Foo <------ HotFoo
* ^
* |
* WarmFoo
*
* This is how the inheritance works now.
*/

/*
* QGraphicsWidget <--- QGraphicsProxyWidget
* ^ ^
* | |
* Foo <---------------- FooProxy <------ HotFoo
* ^ ^
* | |
* ColdFoo WarmFoo
*
* In this possiblity, Foo is re-subclassed to
* extend QGraphicsWidget and FooProxy maintains
* a specialization for already made widgets. The
* dreaded diamond extends down from QGraphicsWidget
* to FooProxy.
*/

/*
* /----- QGraphicsWidget <-------- QGraphicsProxyWidget
* | ^ ^
* v | |
* QObject FooWidget <--- ColdFoo /--- FooProxy <--- HotFoo
* ^ | | ^
* | v | |
* \---------- Foo <-----------------/ WarmFoo
*
* In this possiblilty, Foo is completely detached
* from the graphics subsystem, yet still extends
* QObject because of the object subsystem features
* it uses. The dreaded diamond exists down from
* QObject to FooWidget and FooProxy.
*/

Is there a way to handle or get around such a thing using the facilities available in Qt? Maybe use QGraphicsProxyWidget in such a way that it behaves like a QGraphicsWidget?

Or perhaps what I really need to be do is pull the QObject-related info out of the new Foo class and move it to each of Foo's subclasses. I'd like to avoid maintaining the same code in multiple places as a top priority, for obvious reasons.

Thoughts? Ideas? Thanks for the feedback!

If nothing else, I hope you enjoyed my ASCII inheritance diagrams... :p

wysota
15th June 2010, 22:27
Use the "bridge" design pattern or derive your Foo from QGraphicsWidget and for the items that require a proxy instantiate a proxy widget and make it a child of a blank Foo subclass that will forward all request to the embedded item. AFAIR that's a "proxy" design pattern.

perden
16th June 2010, 15:11
I've been weighing and testing some of the the trade-offs between bridges (http://en.wikipedia.org/wiki/Bridge_pattern) and proxies (http://en.wikipedia.org/wiki/Proxy_pattern)... it's looking like the proxy solution is more feasible for what I've got implemented already. I'll let you know how it turns out.

Other thoughts are also appreciated!

perden
16th June 2010, 21:29
I'm looking into a couple of ways to run proxy-esque patterns to achieve what I'm trying to do.

In this first configuration based off of the first possibility in my first post, the inheritance between QGraphicsProxyWidget and FooProxy has been removed.


/*
* QGraphicsWidget <--- QGraphicsProxyWidget
* ^
* |
* Foo <---------------- FooProxy <------ HotFoo
* ^ ^
* | |
* ColdFoo WarmFoo
*/

FooProxy now contains an attribute of type QGraphicsProxyWidget pointer called _proxy. Methods are written to redirect calls to _proxy where necessary. As a simplified example, consider the following.


class FooProxy : public Foo {
QGraphicsProxyWidget * _proxy;

public:
// this works fine for simple calls...
void setWidget(QWidget * widget) {_proxy->setWidget(widget);}

protected:
// ...but what happens now?
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent * event) {
// this method is protected in _proxy and cannot be called in this context
_proxy->contextMenuEvent(event);

// maybe a potential solution is something like this
QCoreApplication::sendEvent(_proxy, event);
}

// ...but that technique wont help here
virtual bool focusNextPrevChild(bool next) {
// this method is also protected in _proxy
return _proxy->focusNextPrevChild(next);
}
}

I can't continue on this structure until I find a way to get at the protected methods in _proxy, or find another way of calling these methods.

For an alternative configuration based off of the second possibility from my earlier post, I could re-subclass Foo to extend QObject, and remove the graphics subsystem inheritance from FooWidget and FooProxy.


/*
* /----- QGraphicsWidget <-------- QGraphicsProxyWidget
* |
* v
* QObject FooWidget <--- ColdFoo /--- FooProxy <--- HotFoo
* ^ | | ^
* | v | |
* \---------- Foo <-----------------/ WarmFoo
*/

To guarantee that FooWidget and FooProxy always maintain a graphics system like appearance (in terms of available methods), each relevant method will need to be added as pure virtual in Foo. That's a lot of methods! I'm still working through this before I can show some code example.

Thoughts?

wysota
16th June 2010, 21:42
Call event() which is public - the event will get forwarded to the proper event handler by it. Or resend the event to the other object using regular means, the effect will be similar.

Although I'd probably use the bridge pattern, for me it seems cleaner in this situation, at least on the first look.

perden
18th June 2010, 19:54
After developing into it, yes the bridge pattern made more sense because less code would have to be maintained over few classes. And I ended up using an event filter for handling the events. When the graphic is added to the scene, I pass in Foo::_widget via an accessor rather than Foo itself as I had been doing.

So here's the (bastardized) bridge configuration that I settled on, with a little code example for Foo, and everything seems to work well! :D


/*
* /----- QGraphicsWidget <-------- QGraphicsProxyWidget
* | ^
* v |
* QObject ColdFoo
* ^ | |
* | v
* \---------- Foo <--- HotFoo - - - - - - - - /
* ^ |
* |
* WarmFoo - - - - - - - - - - - - - /
*
* The final configuration is based off the second
* possibility from my first post. FooWidget and
* FooProxy could be removed because of the bridge
* pattern. HotFoo and WarmFoo extend Foo with a
* QGraphicsProxyWidget passed into the constructor.
* ColdFoo does the same with other
* QGraphicsWidget-based subclasses.
*/

class Foo : public QObject
{
Q_OBJECT

// All of the properties that I need for QGraphicsWidget-based classes are defined here
Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)
...

protected:
// The widget to bridge to
QGraphicsWidget * _widget;

public:
// Instantiate our Foo and _widget with an event filter
Foo(QGraphicsWidget * widget, QObject * parent = 0) : QObject(parent), _widget(widget) {
_widget->installEventFilter(this);
}

bool eventFilter(QObject * watched, QEvent * event) {
// Redirects events to event filter methods defined below
...
}

protected:
// Filtered relevant events
virtual bool mousePressEventFilter(QGraphicsSceneMouseEvent * event) {...}
...

private:
// Accessors for properties declared above
QRectF geometry() const {return _widget->geometry();}
void setGeometry(const QRectF rect) {_widget->setGeometry(rect);}
...

}