2010-05-11 3 views
0

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?

+0

Les objets composites enfant sont superposés? Ou sont-ils disposés dans une disposition verticale à l'intérieur d'une cellule? – Lohrun

+0

Les enfants ne sont pas vraiment superposés je pense. Les enfants sont seulement dans la même cellule – onurozcelik

+0

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

Répondre

0

Vous pouvez utiliser QGraphicsScene :: sendEvent() pour transmettre l'événement aux enfants. Voici un exemple qui devrait résoudre votre problème. Il me semble que cet élément composite peut être compris comme une disposition en couches avec un élément dessiné l'un sur l'autre. Une telle disposition n'existe pas encore, mais je ne serais pas surpris si elles apparaissaient dans le futur.

+0

En raison d'un conseil, j'ai édité le code boundingRect. Je suppose que c'est une bonne idée d'unir les childs boundingRects. Mais cet élément de temps ne peut pas attraper des événements comme contextmenuevent et mousepressevent. Donc, mon problème antérieur est d'attraper des événements. Avez-vous une idée de ce que cela peut causer? – onurozcelik

+0

Est-ce que votre CompositeItem dérive d'un QGraphicsItem? Il me manque la déclaration de la classe CompositeItem pour avoir une vue complète du problème. – Lohrun

+0

J'ai ajouté les détails à la publication originale. J'espère que c'est assez. Si vous avez besoin de plus d'explications, demandez juste;) – onurozcelik