Реализация элемента-контейнера

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

Мы реализуем проект с Qt.

Сейчас я использую QGraphicsLinearLayout для хранения моих предметов. Я использую макет, потому что не хочу вычислять координаты каждого элемента.

До сих пор я писал классы элементов, чтобы добавить макет. Например, класс SwitchItem символизирует

железнодорожный переключатель в реальном мире. Каждый класс предметов отвечает за свою картину и события. Все идет нормально.

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

Составной элемент

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 имеет свое специальное событие контекстного меню).

Я должен обрабатывать события элемента отдельно. Также мне нужна реализация составного элемента для макета. Как я могу преодолеть эту дилемму?


person onurozcelik    schedule 11.05.2010    source источник
comment
Дочерние составные элементы накладываются друг на друга? Или они расположены вертикально внутри ячейки?   -  person Lohrun    schedule 11.05.2010
comment
Чайлдс на самом деле не накладывается, я думаю. Чайлдс находится только внутри одной клетки   -  person onurozcelik    schedule 12.05.2010
comment
Можете ли вы дать объявление класса? И пример кода, который вы использовали для создания примера с BlockSegmentItem и SignalItem внутри одной ячейки (и, в основном, как вы добавляете составной элемент в сцену).   -  person Lohrun    schedule 12.05.2010
comment
Я не понимаю, почему событие не передается элементу в вашей сцене. Можете ли вы попытаться отправить событие явно, используя метод QGraphicsScene::sendEvent()? И убедитесь, что событие передается.   -  person Lohrun    schedule 14.05.2010


Ответы (1)


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

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

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

person Lohrun    schedule 11.05.2010
comment
По совету я отредактировал код boundingRect. Я думаю, это отличная идея объединить дочерние ограничивающие прямоугольники. Но на этот раз элемент не может перехватывать такие события, как contextmenuevent и mousepressevent. Итак, моя основная проблема заключается в том, чтобы поймать события. У вас есть идея, что может быть причиной этого? - person onurozcelik; 12.05.2010
comment
Является ли ваш CompositeItem производным от QGraphicsItem? Мне не хватает объявления класса CompositeItem, чтобы иметь полное представление о проблеме. - person Lohrun; 12.05.2010
comment
Я добавил подробности в исходный пост. Надеюсь, этого достаточно. Если вам нужно больше объяснений, просто спросите;) - person onurozcelik; 12.05.2010