容器项目实施

时间:2010-05-11 13:34:24

标签: c++ qt implementation qgraphicsview qgraphicsitem

我在Train Traffic Controller软件项目中工作。 我在这个项目中的职责是开发可视化铁路GUI。

我们正在用Qt实施该项目。 到现在为止,我正在使用QGraphicsLinearLayout来保存我的物品。 我正在使用布局,因为我不想计算每个项目的坐标。 到目前为止,我编写了项类来添加布局。例如,SwitchItem类符号化 现实世界中的铁路开关。每个项目类都负责自己的绘画和活动。 到目前为止一切顺利。
现在我需要一个可以包含两个或更多项的复合项。该类将负责绘制其中包含的项目。我需要这个类,因为我必须在同一个布局单元格中放置两个或更多个项目。如果我不把它们放在同一个单元格中,我就不能使用布局。见下图。

Composite Item http://img169.imageshack.us/img169/9079/composite1.jpg BlockSegmentItem和SignalItem在同一个单元格内。

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="CI5@MIT" type="COMPOSITE">
    <Item id="001BT@MIT" 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="B2D@MIT" 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>

此代码适用于绘画,但涉及项目事件则存在问题。 QGraphicsScene将复合项视为单个项目,该项目适用于布局,但不适用于事件。因为每个项目都有自己的事件实现。(例如,SignalItem有其特殊的上下文菜单事件。)

我必须单独处理项目事件。我还需要一个布局的复合项实现。我怎样才能克服这种困境?

1 个答案:

答案 0 :(得分:0)

您可以使用QGraphicsScene :: sendEvent()将事件传递给子级。这是一个应该解决您的问题的示例。

void CompositeItem::sceneEvent(QEvent * event)
{
  if (scene()) {
    FieldItem *child;
    foreach(child, children) {
      scene()->sendEvent(child, event);
    }
  }
}

在我看来,复合项目可以理解为分层布局,其中一个项目在另一个上面绘制。这样的布局还不存在,但如果它们出现在将来我也不会感到惊讶。