容器项目实施
我正在从事列车交通控制器软件项目。 我在这个项目中的职责是开发可视化铁路 GUI。
我们正在使用 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 有其特殊的上下文菜单事件。)
我必须单独处理项目事件。我还需要一个布局的复合项目实现。我怎样才能克服这个困境?
I am working in Train Traffic Controller software project.
My responsibility in this project is to develop the visual railroad GUI.
We are implementing the project with Qt.
By now I am using QGraphicsLinearLayout to hold my items.
I am using the layout because I do not want to calculate coordinates of each item.
So far I wrote item classes to add the layout. For instance SwitchItem class symbolizes
railroad switch in real world. Each item class is responsible for its own painting and events.
So far so good.
Now I need a composite item that can contain two or more item. This class is going to be responsible for painting the items contained in it. I need this class because I have to put two or more items inside same layout cell. If I don' t put them in same cell I can' t use layout. See the image below.
BlockSegmentItem and SignalItem inside same cell.
CompositeItem header file
#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>
This code works good with painting but when it comes to item events it is problematic.
QGraphicsScene treats the composite item like a single item which is right for layout but not for events. Because each item has its own event implementation.(e.g. SignalItem has its special context menu event.)
I have to handle item events seperately. Also I need a composite item implementation for the layout. How can I overcome this dilemma?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以使用 QGraphicsScene::sendEvent() 将事件传递给子级。这是一个应该可以解决您的问题的示例。
在我看来,复合项目可以理解为分层布局,其中一个项目绘制在另一个项目之上。这样的布局目前还不存在,但如果将来出现,我也不会感到惊讶。
You can use the QGraphicsScene::sendEvent() to pass the event to children. Here is an example that should address your issue.
It seems to me that composite item can be understood as layered layout with one item drawn on top of the other. Such layout does not exist yet but I wouldn't be surprised if they appeared in the future.