Реализация контейнера - PullRequest
0 голосов
/ 11 мая 2010

Я работаю в программном проекте Train Traffic Controller. В этом проекте я отвечаю за разработку графического интерфейса пользователя для железной дороги.

Мы реализуем проект с помощью Qt. К настоящему времени я использую QGraphicsLinearLayout для хранения своих предметов. Я использую макет, потому что я не хочу рассчитывать координаты каждого элемента. До сих пор я писал классы элементов для добавления макета. Например, класс SwitchItem символизирует стрелочный перевод в реальном мире. Каждый класс предметов отвечает за свою живопись и события. Пока все хорошо.
Теперь мне нужен составной элемент, который может содержать два или более элемента. Этот класс будет отвечать за покраску предметов, содержащихся в нем. Мне нужен этот класс, потому что я должен поместить два или более элементов в одну ячейку макета. Если я не помещу их в одну ячейку, я не смогу использовать макет. Смотрите изображение ниже.

Составной элемент 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 голосов
/ 11 мая 2010

Вы можете использовать QGraphicsScene :: sendEvent (), чтобы передать событие детям. Вот пример, который должен решить вашу проблему.

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

Мне кажется, что составной элемент можно понимать как многослойный макет с одним элементом, нарисованным поверх другого. Такой раскладки еще не существует, но я не удивлюсь, если они появятся в будущем.

...