Je travaille sur le projet logiciel Train Traffic Controller. Ma responsabilité dans ce projet est de développer l'interface graphique de chemin de fer visuel.Mise en œuvre d'un article conteneur
Nous mettons en œuvre le projet avec Qt. Maintenant j'utilise QGraphicsLinearLayout pour contenir mes objets. J'utilise la mise en page parce que je ne veux pas calculer les coordonnées de chaque article. Jusqu'à présent, j'ai écrit des classes d'éléments pour ajouter la mise en page. Par exemple, la classe SwitchItem symbolise le commutateur de chemin de fer dans le monde réel. Chaque classe d'objets est responsable de sa propre peinture et de ses propres événements. Jusqu'ici tout va bien.
Maintenant j'ai besoin d'un objet composite qui peut contenir deux objets ou plus. Cette classe sera responsable de peindre les objets qui y sont contenus. J'ai besoin de cette classe parce que je dois mettre deux ou plusieurs éléments dans la même cellule de mise en page. Si je ne les mets pas dans la même cellule, je ne peux pas utiliser la mise en page. Voir l'image ci-dessous.
Composite Item http://img169.imageshack.us/img169/9079/composite1.jpg BlockSegmentItem et SignalItem dans la même cellule.
fichier d'en-tête de CompositeItem
#include <QtCore/QList>
#include <QtGui/QGraphicsLayoutItem>
#include <QtGui/QGraphicsItemGroup>
#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QGraphicsSceneContextMenuEvent>
#include "fielditem.h"
class CompositeItem : public FieldItem
{
Q_OBJECT
public:
CompositeItem(QString id,QList<FieldItem *> _children);
~CompositeItem();
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF(-1,-1)) const;
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /* = 0 */);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
private:
QList<FieldItem *> children;
};
//CompositeItem implementation
#include "compositeitem.h"
CompositeItem::CompositeItem(QString id,QList<FieldItem *> _children)
{
children = _children;
}
CompositeItem::~CompositeItem()
{
}
QRectF CompositeItem::boundingRect() const
{
FieldItem *child;
QRectF rect(0,0,0,0);
foreach(child,children)
{
rect = rect.united(child->boundingRect());
}
return rect;
}
void CompositeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
FieldItem *child;
foreach(child,children)
{
child->paint(painter,option,widget);
}
}
QSizeF CompositeItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
QSizeF itsSize(0,0);
FieldItem *child;
foreach(child,children)
{
// if its size empty set first child size to itsSize
if(itsSize.isEmpty())
itsSize = child->sizeHint(Qt::PreferredSize);
else
{
QSizeF childSize = child->sizeHint(Qt::PreferredSize);
if(itsSize.width() < childSize.width())
itsSize.setWidth(childSize.width());
itsSize.setHeight(itsSize.height() + childSize.height());
}
}
return itsSize;
}
void CompositeItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
qDebug()<<"Test";
}
//Code that add items to scene
//Subclass of QGraphicsWidget for future extension
FieldItemContainer *widget;
while(!itemGroupNode.isNull())
{
//Subclass of QGraphicsLinearLayout for future extension
FieldItemGroupLayout *layout = new FieldItemGroupLayout;
int layoutX = itemGroupNode.toElement().attribute("X").toInt();
int layoutY = itemGroupNode.toElement().attribute("Y").toInt();
layout->setSpacing(1);
QDomNode itemNode = itemGroupNode.toElement().namedItem("Item");
while (!itemNode.isNull() && itemNode.nodeType() == QDomNode::ElementNode)
{
FieldItem * item;
//Create proper item
item = FieldItemFactory::createFieldItem(itemNode);
//Add item to layout
layout->addItem(item);
itemNode = itemNode.nextSibling();
}
widget = new FieldItemContainer;
//Set layout to widget
widget->setLayout(layout);
widget->setPos(layoutX,layoutY);
//Add widget to scene
itsScene->addItem(widget);
itemGroupNode = itemGroupNode.nextSibling();
}
//FieldItemFactory implementation
FieldItem* FieldItemFactory::createFieldItem(QDomNode itemNode)
{
FieldItem *item;
//Common for all items
QString itemId = itemNode.toElement().attribute("id");
QString itemType = itemNode.toElement().attribute("type");
int x1 = itemNode.namedItem("X1").toElement().text().toInt();
int y1 = itemNode.namedItem("Y1").toElement().text().toInt();
//Only for Block part items
int x2 = 0;
int y2 = 0;
QString signalization = "";
////***********************
//Only for signal items
QString source = "";
int width = 0;
int height = 0;
///********************
//Only for switch items
QString normalPositionSource = "";
QString reversePositionSource = "";
///********************
///Labeling
QDomNode itemLabelNode = itemNode.namedItem("Label");
FieldItemLabel label;
label.x1 = itemLabelNode.namedItem("X1").toElement().text().toInt();
label.y1 = itemLabelNode.namedItem("Y1").toElement().text().toInt();
label.text = itemLabelNode.namedItem("Text").toElement().text().trimmed();
label.rotateAngle = itemLabelNode.namedItem("RotateAngle").toElement().text().toInt();
label.hideScale = itemLabelNode.namedItem("HideScale").toElement().text().toInt();
///*****************
if(itemType == FieldProperty::BLOCKSEGMENTITEM)
{
x2 = itemNode.namedItem("X2").toElement().text().toInt();
y2 = itemNode.namedItem("Y2").toElement().text().toInt();
signalization = itemNode.namedItem("Signalization").toElement().text().trimmed();
item = new BlockSegmentItem(itemId,x1,y1,x2,y2,
label,signalization);
}
else if(itemType == FieldProperty::SWITCHITEM)
{
normalPositionSource = itemNode.namedItem("Normal").toElement().text().trimmed();
reversePositionSource = itemNode.namedItem("Reverse").toElement().text().trimmed();
item = new SwitchItem(itemId,x1,y1,FieldProperty::SWITCH_WIDTH,
FieldProperty::SWITCH_HEIGHT,label,
normalPositionSource,reversePositionSource);
}
else if(itemType == FieldProperty::SIGNALITEM)
{
source = itemNode.namedItem("Source").toElement().text().trimmed();
width = itemNode.namedItem("Width").toElement().text().toInt();
height = itemNode.namedItem("Height").toElement().text().toInt();
item = new SignalItem(itemId,x1,y1,width,height,label,source);
}
else if(itemType == FieldProperty::TRAINIDBOXITEM)
{
item = new TrainIdBox(itemId,x1,y1);
}
else if(itemType == FieldProperty::COMPOSITEITEM)
{
QList<FieldItem *> children;
for (int i = 0; i < itemNode.childNodes().count(); i++)
{
children.push_back(createFieldItem(itemNode.childNodes().at(i)));
}
item = new CompositeItem(itemId,children);
}
return item;
}
//At last part of xml data that I used to create BlockSegmentItem and SignalItem in same cell
<Item id="[email protected]" type="COMPOSITE">
<Item id="[email protected]" type="BLOCKSEGMENT">
<Label>
<Text>001BT</Text>
<X1>70</X1>
<Y1>10</Y1>
</Label>
<X1>0</X1>
<Y1>15</Y1>
<X2>150</X2>
<Y2>15</Y2>
</Item>
<Item id="[email protected]" type="SIGNAL">
<Label>
<Text>B2D</Text>
<X1>15</X1>
<Y1>40</Y1>
</Label>
<X1>0</X1>
<Y1>20</Y1>
<Width>40</Width>
<Height>10</Height>
<Source>Resources/graphics/signals/sgt3lr.svg</Source>
</Item>
</Item>
Ce code fonctionne bien avec la peinture, mais en ce qui concerne les événements de ce point est problématique. QGraphicsScene traite l'élément composite comme un seul élément qui convient à la mise en page mais pas aux événements. Parce que chaque élément a sa propre implémentation d'événement (par exemple SignalItem a son événement de menu contextuel spécial.)
Je dois gérer les événements d'élément séparément. J'ai également besoin d'une implémentation d'éléments composites pour la mise en page. Comment puis-je surmonter ce dilemme?
Les objets composites enfant sont superposés? Ou sont-ils disposés dans une disposition verticale à l'intérieur d'une cellule? – Lohrun
Les enfants ne sont pas vraiment superposés je pense. Les enfants sont seulement dans la même cellule – onurozcelik
Pouvez-vous donner la déclaration de classe? Et un exemple du code que vous avez utilisé pour produire l'exemple avec un BlockSegmentItem et SignalItem dans la même cellule (et principalement comment vous ajoutez l'élément composite à la scène). – Lohrun