0

私はTrain Traffic Controllerソフトウェアプロジェクトに取り組んでいます。このプロジェクトでの私の責任は、視覚的な鉄道 GUI を開発することです。

Qtでプロジェクトを実装しています。

今ではQGraphicsLinearLayoutを使用してアイテムを保持しています。各アイテムの座標を計算したくないので、レイアウトを使用しています。

ここまでは、レイアウトを追加するアイテム クラスを作成しました。たとえば、SwitchItem クラスは

現実世界の鉄道スイッチ。各アイテム クラスは、独自のペイントとイベントを担当します。ここまでは順調ですね。

ここで、2 つ以上のアイテムを含むことができる複合アイテムが必要です。このクラスは、それに含まれるアイテムのペイントを担当します。同じレイアウト セル内に 2 つ以上のアイテムを配置する必要があるため、このクラスが必要です。それらを同じセルに配置しないと、レイアウトを使用できません。下の画像を参照してください。

複合アイテム

同じセル内の 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 には特別なコンテキスト メニュー イベントがあります)。

アイテム イベントを個別に処理する必要があります。また、レイアウト用の複合アイテムの実装も必要です。どうすればこのジレンマを克服できますか?

4

1 に答える 1

0

QGraphicsScene::sendEvent() を使用して、イベントを子に渡すことができます。あなたの問題に対処する必要がある例を次に示します。

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

複合アイテムは、あるアイテムが他のアイテムの上に描かれたレイヤード レイアウトとして理解できるように思えます。このようなレイアウトはまだありませんが、将来登場しても不思議ではありません。

于 2010-05-11T14:03:47.553 に答える