For several graphic objects I inherit from QGraphicsLineItem
, QGraphicsRectItem
and so on.
class CustomLine : public QGraphicsLineItem{};
class CustomRect : public QGraphicsRectItem{};
Those objects are added to a container, a custom subclass of a QGraphicsScene "scene"
that is meant for displaying and interacting with those items. this->scene->items()
returns a list of QGraphicItem
's: QList<QGraphicsItem* >
What I want to do is each custom object class to have the same custom interface methods, for example setModeX()
. Then I could do stuff like:
Foreach (BaseItem *item, this->scene->items()){
item->setModeX(...);
}
But how do I achieve that? If I make an interface like
class BaseItem{
public: setModeX(); [...]
private: Mode mode_;
}
and inherit
class CustomLine : public QGraphicsLineItem, BaseItem {};
So while the scene should only contain items based on BaseItem
(not sure if this is really needed for this task), I first retrieve a list of objects of one of its 2 base classes, namely QGraphicsItem
, and need to cast it to its other base class BaseItem
to use the interface methods. I will probably not be able to cast a CustomLine-item to BaseItem in the loop above, because it does not know about the other base class.
EDIT:
I use MinGW 4.8 32 bit (g++).
I noticed that when I start the foreach-loop, the items in my scene disappear (yet don't see the reason why)
Since scene
is a QGraphicsScene
, it only consists of QGraphicsItem
s. So you cannot directly iterate over scene
as BaseItem
s as you show in your code. You have to iterate over each QGraphicsItem
. You describe that you could downcast to your CustomLine
and then upcast back to a BaseItem
, but this only works if you know that all the items in the scene
are lines. If scene
contains other types of items, your technique would require you to iterate of each kind of item until you found a downcast that worked, and then cast it back to BaseItem
.
QGraphicsItem
\ BaseItem
QGraphicsLineItem /
\ /
CustomLine
A simple solution would have been available to you if the Qt library had used virtual inheritance on QGraphicsItem
. Then, you would simply need to use virtual inheritance on QGraphicsItem
from BaseItem
, and then down casting would have worked.
QGraphicsItem
/ \
QGraphicsLineItem BaseItem
\ /
CustomLineItem
Since Qt does not do so, you would either need to make custom changes to the Qt library, or code in your own conversion mechanism.
Assuming you are unwilling to make custom modifications to the Qt library itself, then one approach is to create a mapping between Qt's QGraphicsItem
and your BaseItem
. The mapping can be done within the constructor of your BaseItem
, and undone from BaseItem
s destructor. To make undoing the mapping more efficient, you can also create a link from the BaseItem
to the QGraphicsItem
.
class BaseItem {
static std::unordered_map<QGraphicsItem *, BaseItem *> map;
QGraphicsItem *link_;
public:
BaseItem (QGraphicsItem *q) : link_(q) {
//...
map[q] = this;
}
virtual ~BaseItem () {
map.erase(link_);
//...
}
static BaseItem * getBaseItem (QGraphicsItem *q) {
std::unordered_map<QGraphicsItem *, BaseItem *>::iterator i;
if ((i = map.find(q)) == map.end()) return NULL;
return i->second;
}
//...
};
//...
std::unordered_map<QGraphicsItem *, BaseItem *> BaseItem::map;
In your derived classes, you would simply need to pass this
to the BaseItem
constructor.
class CustomLine : public QGraphicsLineItem, public BaseItem {
public:
CustomLine () : BaseItem(this) {
//...
};
//...
};
And then your loop would use the static BaseItem::getBaseItem()
function to convert from a QGraphicsItem
pointer to a BaseItem
pointer. Thus, since there is no way to create a useable inheritance relationship between QGraphicsItem
and BaseItem
, their relationship is recorded in a table.
QGraphicsItem <-----------------. link
\ BaseItem <--' map
QGraphicsLineItem /
\ /
CustomLine
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments