PDA

View Full Version : QGraphicRectItem update colour in foreach statement



Docas
31st January 2020, 14:23
Good day everyone


I am trying to change the colour of the QGraphicsRectItems that is on a scene. QGraphicsRectItems are nodes of a network, I want that each item to be updated by a colour indicating status of the node. my question how do i write a code that will update and iterate to the next item on the scene but keep the update ? THis one below it updates them all at the same time, i mean if one is green it turns all green or if one is red it turns all red which is not desired. it does not update each item at a time and keep the status and pass to the next, which is i would like to see. my code is below




else if (_eType == QSubscription::UPDATED_TITLES)
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();
m_pLinkStateListModel->addTitle(pState);


foreach(QGraphicsItem *item,scene->items()) // foreach statement that updates all at the same time
{

QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem *>(item);
if (!rect)
continue;
getTitleDescriptionColor(pState, m_backgroundColor, rect);
}




The function that gets the colour (getTitleDescriptionColour) is





QColor getTitleDescriptionColor(const DM::Object *_pTitle, const QColor &_colorBackground, QGraphicsRectItem *_row)
{

QColor ret = _colorBackground;

if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;


if (pState->m_eState == C2DM::LinkState::EIS_GOOD)
{
ret = QColor(Qt::green).darker(120);
_row->setBrush(ret);
}
else if (pState->m_eState == C2DM::LinkState::EIS_BUSY)
{
ret = QColor(Qt::yellow).darker(120);
_row->setBrush(ret);
}
else
{
ret = QColor(Qt::red).darker(120);
_row->setBrush(ret);
}

}
else if (CORE::checkPtrType<C2DM::RouteState>(_pTitle) == true)
{
C2DM::RouteState *pState = (C2DM::RouteState*)_pTitle;

if (pState->m_bEnabled == false)
{
ret = QColor(Qt::blue).lighter(120);
}
else if ((pState->m_eTxLinkState != C2DM::LinkState::EInterfaceState::EIS_GOOD) ||
(pState->m_eRxLinkState != C2DM::LinkState::EInterfaceState::EIS_GOOD))
{
ret = QColor(Qt::yellow).darker(120);
}
else
{
ret = QColor(Qt::green).darker(120);
}

}

return ret;
}

d_stranz
31st January 2020, 18:55
Your logic in the getTitleDescriptionColor() is suspect. In the first half of it, your determine the color, and set a brush on the rectangle. In the second half of it, you determine the color but don't do anything with the rectangle, and in the calling function, you call the routing and completely ignore the return value.

You haven't shown much context for the calling function, just one else-if clause, so are you sure this is where the rect colors are being set and not in some other clause? The part of the getTitle...() method that actually sets rect colors seems to be OK.

Docas
31st January 2020, 22:47
Hello d_stranz


I appreciate a lot for your response,thank you. The second half of getTitleDescriptionColor() is not adding the QGraphicsRectItems yet only the first part adds successfully, I was still going to check why it is not adding the items, but the first part of it adds them successfully ,thank you for pointing out my error there.

On your question, I also call method addRect() which adds them on the scene and gives them color(see code), and I wanted to now only update the color based on the status of the link by calling method getTitleDescriptionColor(pState, m_backgroundColor, rect); with the foreach statement

I do not understand what you mean by your statement(and in the calling function, you call the routing and completely ignore the return value.), how do I ignore the return value?

Please find my attachment to see what i have so far. I have attached how the links(QGraphicsRectItems) are added on addRect(pState); statement as explained in the commentary below, the attachment also shows that all items are green at the same time, which is not true because other links are down, it supposed to show red to whomever is disconnected and green to where there is connection.




void GatewayWidget::receivedTitlesEx(const QString &_rStrDescription, const CORE::Time &_rSimTime, const QList<std::shared_ptr<DM::Object> > &_rTitles, int _eType)
{

if (_rStrDescription == "LINK.*.STATE")
{
foreach (const std::shared_ptr<DM::Object> &objPtr, _rTitles)
{

if (CORE::checkPtrType<C2DM::LinkState>(objPtr) == true)
{
if (_eType == QSubscription::NEW_TITLES)
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();

addRect(pState);// this is where i call the method that adds the QGraphicsRectItem(nodes) on the scene

}
else if (_eType == QSubscription::UPDATED_TITLES)// this clause updates the status of the links of the network(QGraphicsRectItems) , for instance when there is a physical connection of a link or disconnection. Disconnection is red,connected green.That is where I want the colors of the QGraphicsRectItems to change as the status changes
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();



foreach(QGraphicsItem *item, scene->items())// this is the foreach that i call when trying to update each QGraphicsRectItem on the scene, which it does update but it keeps alternating the colors at the same time
{

QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem *>(item);
if (!rect)
continue;

getTitleDescriptionColor(pState, m_backgroundColor, rect);
}

}
else if (_eType == QSubscription::DELETED_TITLES)// still yet to implement a delete node(QGraphicsRectItem)
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();


}
}

}



}
else if (_rStrDescription == "ROUTE.*.STATE") //still yet to implement routes was first starting with the links
{
foreach (const std::shared_ptr<DM::Object> &objPtr, _rTitles)
{
if (CORE::checkPtrType<C2DM::RouteState>(objPtr) == true)
{
if (_eType == QSubscription::NEW_TITLES)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)objPtr->_clone();



}
else if (_eType == QSubscription::UPDATED_TITLES)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)objPtr->_clone();



}
else if (_eType == QSubscription::DELETED_TITLES)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)objPtr->_clone();

}
}



}//end for each statement
}// end else if


addRect(); method. This method adds links(QGraphicsReciItems) on the scene and adds color to them on startup. The color is supposed to update as links may be disconnected or connected which is what I am trying to achieve.

void GatewayWidget::addRect(DM::Object *_pTitle)
{
if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;


QGraphicsTextItem *textColor = scene->addText(QString::fromStdString(pState->m_strLinkId));

int x = (qrand() % 4);
int pos = (x * 75) - 75 / 2;
textColor->setPos(400, pos + 200);
textColor->setFlag(QGraphicsItem::ItemIsMovable);
textColor->setFlag(QGraphicsItem::ItemIsSelectable);
QGraphicsRectItem *rectangleColor = new QGraphicsRectItem(QRectF(0, 0, 50, 50), textColor);


rectangleColor->setPos(0, 25);
//rectangleColor->setFlag(pState::ItemIsMovable);
rectangleColor->setFlag(QGraphicsItem::ItemIsSelectable);

getTitleDescriptionColor(pState, m_backgroundColor, rectangleColor);// I add colour on startup based on the initial state of the link


}
else if (CORE::checkPtrType<C2DM::RouteState>(_pTitle) == true)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)_pTitle;

QGraphicsRectItem *rectangle;
QGraphicsRectItem *rectangle1;
QGraphicsTextItem *text;
QGraphicsTextItem *text1;
QPen pointpen(Qt::green);
pointpen.setWidth(4);
QPen linepen(Qt::red);
linepen.setWidth(2);
QColor ret = m_backgroundColor;


text = scene->addText(QString::fromStdString(pRoute->m_strSource));
text->setPos(600, (qrand() % 400));

text1 = scene->addText(QString::fromStdString(pRoute->m_strDestination));
text1->setPos(900, (qrand() % 400));


QString MessageCountSrc = QString::number(pRoute->m_dTxBps);
rectangle = new QGraphicsRectItem(QRectF(0, 0, 60, 60), text1);

rectangle->setPos(0, 25);
//rectangle->setBrush(greenBrush);
rectangle->setFlag(QGraphicsItem::ItemIsSelectable);



QString MessageCount = QString::number(pRoute->m_uMsgCount);
rectangle1 = new QGraphicsRectItem(QRectF(0, 0, 60, 60), text);

rectangle1->setPos(0, 25);// sets position of the name of the link e.g HS1

rectangle1->setFlag(QGraphicsItem::ItemIsSelectable);

//scene->update();
QGraphicsLineItem*line = new QGraphicsLineItem();

line->setLine(QLineF(rectangle1->scenePos(), rectangle->scenePos()));

line->setZValue(-1);//sets the line behind the rectangle box
//scene->addLine(myLine, pointpen);
scene->addItem(line);

getTitleDescriptionColor(pRoute, m_backgroundColor, rectangle1);

}
}

d_stranz
1st February 2020, 01:36
how do I ignore the return value?

The getTitleDescriptionColor() method returns a QColor (as "ret"). In your function that calls this method (line 31 of your first code blockabove and several places in the second code block), you do not assign this return value to anything. Basically, you are ignoring it. This means that the second half of the getTitleDescriptionColor method, where you compute the color only, is doing nothing that the calling routine (or anything else) actually uses.


C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();
C2DM::RouteState *pRoute = (C2DM::RouteState*)objPtr->_clone();

Are these shared pointers? If not, then every one of these statements (except the first one, where you call addRect() afterwards) is probably a memory leak. Unless you have not posted the complete code in these parts of the if / else clauses, then here you create a "pState" or "pRoute" instance by cloning your object, then you do nothing with it. The pState" / "pRoute" variable goes out of scope at the end of that section of the conditional clause, so unless they are shared pointers, it is probably a memory leak. Depending on how often that function is called, it could be a big leak over time.

But, OK, now I see your bug:

- each time you call getTitleDescriptionColor(), you pass in -one- value for the LinkState, but you are calling the function for -every- rect in your scene, regardless of whether that LinkState applies to it or not. So the net result of iterating over all the rects will be to change every one of them to the same color based on that specific LinkState. When the LinkState changes again, every rect changes to the same color as a result.

So what you need to do is to add an association between each rect and its specific LinkState value. You might be able to do this using QGraphicsItem::setData() for this, or a map< LinkState, QGraphicsRectItem *>, or some similar data structure. The basic problem is your code has no idea which rect has which LinkState.

Docas
1st February 2020, 11:50
Thank you again for your help,I appreciate.

Concerning the cloning of the object under pRoute, I was still yet to implement the method. In my mind I wanted the links(Linkstate *pPstate) to work first, then implement the (Linkstate *pRoute) by also calling addRect(); I will comment it out to avoid the memory leaks, thank you.


Thank you for the explanation of the bug, I was really confused. I am asking if you can help me with an example of how i can implement the
void QGraphicsItem::setData ( int key, const QVariant & value ), honestly I dont think i know how to do it.I am still a newbie,Please. Should int key be QGraphicsRectItem and const QVariant & value be the color? should this method be implemented outside the calling function and the call it on the foreach?

Thanks in advance for your help!

d_stranz
1st February 2020, 15:52
void QGraphicsItem::setData ( int key, const QVariant & value )

This is a method in the base class for QGraphicsRectItem (QGraphicsItem). The "key" can be integer you want it to be. The reason it is there is to allow you to store more than one value for each item. Since you will probably be storing only one value in each QGraphicsRectItem, you can use 0 (zero) as the key.

For the value, I would use the enum C2DM:: LinkState:: m_eType (eg. C2DM:: LinkState:: EIS_GOOD, EIS_BUSY, etc.). Your addRect() code would be modified to look like this:


void GatewayWidget::addRect(DM::Object *_pTitle)
{
if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;


QGraphicsTextItem *textColor = scene->addText(QString::fromStdString(pState->m_strLinkId));

int x = (qrand() % 4);
int pos = (x * 75) - 75 / 2;
textColor->setPos(400, pos + 200);
textColor->setFlag(QGraphicsItem::ItemIsMovable);
textColor->setFlag(QGraphicsItem::ItemIsSelectable);
QGraphicsRectItem *rectangleColor = new QGraphicsRectItem(QRectF(0, 0, 50, 50), textColor);
rectangleColor->setData( 0, int( pState->m_eType ) ); // Automatically creates a QVariant<int>
// etc. for the other rectangle


Your code for getTitleDescriptionColor() needs to check that the rectangle's LinkState type value matches the value you passed in:



QColor getTitleDescriptionColor(const DM::Object *_pTitle, const QColor &_colorBackground, QGraphicsRectItem *_row)
{

QColor ret = _colorBackground;

if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;

// If the state type passed in does not match the state type stored in the rect, then do nothing
int stateType = int( pState->m_eType );
int rectState = _row->data( 0 ).toInt();
if ( stateType != rectState )
return ret;

// Otherwise, continue with the original code to change the rect color

// ...



You will need to add code to change the value stored in the QGraphicsRectItem whenever its LinkState changes.

Docas
1st February 2020, 23:43
Thank you for the response and generous help!

I implemented it and it does change the color for a specific QGraphicsRectItem with its connection status(thank you), however when it updates the status correctly it also add new QGraphicsRectItems(see attachment),a repeat of QGraphicsRectItems that was on the scene but with an updated status. I called the addRect function on the update statement like this:

void GatewayWidget::receivedTitlesEx(const QString &_rStrDescription, const CORE::Time &_rSimTime, const QList<std::shared_ptr<DM::Object> > &_rTitles, int _eType)
{

if (_rStrDescription == "LINK.*.STATE")
{
foreach (const std::shared_ptr<DM::Object> &objPtr, _rTitles)
{

if (CORE::checkPtrType<C2DM::LinkState>(objPtr) == true)
{
if (_eType == QSubscription::NEW_TITLES)
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();

addRect(pState);// this is the first call of the method that adds the QGraphicsRectItem(nodes) on the scene

}
else if (_eType == QSubscription::UPDATED_TITLES)
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();

addRect(pState);//on updated status i call the addRect() again. When it updates the link status and change the color, it also adds the new QGraphicsRectItem when it is was already added on the first call of addRect() above.
}



my understanding was that I am supposed to call addRect() again on the UPDATED_TITLES as seen on the else if clause above, am I correct?

I think that on the addRect() function, I always add QGraphicsRectItems on the scene through this lines:



QGraphicsTextItem *textColor = scene->addText(QString::fromStdString(pState->m_strLinkId));
QGraphicsRectItem *rectangleColor = new QGraphicsRectItem(QRectF(0, 0, 50, 50), textColor);
rectangleColor->setData(0, int(pState->m_eState));


then whenever I call addRect() it creates another QGraphicsRectItem with the updated status. my question is am I supposed to write another statement on addRect() that will check if the QGraphicsRectItems are already added on the scene, and if they are, then just change color only and not add a new QGraphicsRectItem?

on your statement"You will need to add code to change the value stored in the QGraphicsRectItem whenever its LinkState changes." do you mean I should change the value int Rectstate when _eType changes here :
}
else if (_eType == QSubscription::UPDATED_TITLES) //this is where _eType changes(which is the LinkState),is this where I am supposed to write the code? how do I access the value int rectState on this function?
{
C2DM::LinkState *pState = (C2DM::LinkState*)objPtr->_clone();
addRect(pState);

function addRect() I updated it as you advised:

void GatewayWidget::addRect(DM::Object *_pTitle)
{
if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;


QGraphicsTextItem *textColor = scene->addText(QString::fromStdString(pState->m_strLinkId));

int x = (qrand() % 4);
int pos = (x * 75) - 75 / 2;
textColor->setPos(400, pos + 200);
textColor->setFlag(QGraphicsItem::ItemIsMovable);
textColor->setFlag(QGraphicsItem::ItemIsSelectable);
QGraphicsRectItem *rectangleColor = new QGraphicsRectItem(QRectF(0, 0, 50, 50), textColor);
rectangleColor->setData(0, int(pState->m_eState));

rectangleColor->setPos(0, 25);

rectangleColor->setFlag(QGraphicsItem::ItemIsSelectable);

getTitleDescriptionColor(pState, m_backgroundColor, rectangleColor);


}

Function getTitleDescription() as follows:

QColor getTitleDescriptionColor(const DM::Object *_pTitle, const QColor &_colorBackground, QGraphicsRectItem *_row)
{

QColor ret = _colorBackground;

if (CORE::checkPtrType<C2DM::LinkState>(_pTitle) == true)
{

C2DM::LinkState *pState = (C2DM::LinkState*)_pTitle;

// If the state type passed in does not match the state type stored in the rect, then do nothing
int stateType = int(pState->m_eState);
int rectState = _row->data(0).toInt();
if (stateType != rectState)
{

return ret;

}
else
{

if (pState->m_eState == C2DM::LinkState::EIS_GOOD)
{
ret = QColor(Qt::green).darker(120);
_row->setBrush(ret);
}
else if (pState->m_eState == C2DM::LinkState::EIS_BUSY)
{
ret = QColor(Qt::yellow).darker(120);
_row->setBrush(ret);
}
else
{
ret = QColor(Qt::red).darker(120);
_row->setBrush(ret);
}

}


}

Please pardon me for slow understanding, please help me on the way forward.Thank you for your help in advance.

d_stranz
2nd February 2020, 03:30
however when it updates the status correctly it also add new QGraphicsRectItems(see attachment),a repeat of QGraphicsRectItems that was on the scene but with an updated status. I called the addRect function on the update statement like this:

Think about what you are saying here. If the status of a rect changes, why would you want to add a new rect? Just change the status of the rect that already exists. You can call setData() for a rect any time, not just when you create it. If the status changes from EIS_GOOD to EIS_BUSY, then simply call setData( 0, EIS_BUSY ) for that rect, don't create a new one with EIS_BUSY status.

You need to take a step back and really think through the logic of your program. It seems like you are just copying and pasting without really thinking about what you want the code to do.

Docas
2nd February 2020, 20:47
I think you misunderstood me. What I meant is I do not want a new rect to be added everytime a status change,how do I this? is my question. setdata() is setup correctly because the color of the rect changes as i expect,But i do not want a new rect to be added with the new status. On my addRect() function, the first thing i do is to create a new rect, then setdata on that rect,like this:



void GatewayWidget::addRect(DM::Object *_pTitle)
{

QGraphicsTextItem *textColor = scene->addText(QString::fromStdString(pState->m_strLinkId));
QGraphicsRectItem *rectangleColor = new QGraphicsRectItem(QRectF(0, 0, 50, 50), textColor); //create rect
rectangleColor->setData(0, int(pState->m_eState));//then set data



my question is, is there a way I can setdata() without creating a new rect? how do i call setdata() for an existing rect on the scene?

Please see the attached image i sent, you can see that setdata is correct but i do not know how to prevent the creation of a new rect

d_stranz
2nd February 2020, 21:26
is there a way I can setdata() without creating a new rect? how do i call setdata() for an existing rect on the scene?

Of course you can call setData() on an existing rect.

I think your basic problem is that you do not have any connection between the data you are trying to model (your data tree of links and nodes) and the graphical items you are using to display the model on the screen (the QGraphicsRectItem and QGraphicsLineItem instances).

If you want the graphics items on the screen to reflect the status of the data items in your model, then you have to create another data structure that connects a specific item from your model with the graphics item that represents it on screen. This is what I meant several replies ago when I said:


So what you need to do is to add an association between each rect and its specific LinkState value.

You can't add items to your graphics scene and then just forget about them. If they represent something in your model, you need to create an explicit association between the model and the graphics item.

What I would do is create something like this:


std::map< DM::Object *, QGraphicsItem * >

as a data structure in the GatewayWidget class (or some other class if it is more appropriate). Each time you create a new graphics item, you add an entry to the map that associates the Object with the graphics item. When you need to update a graphics item because the Object status has changed, you use the map to retrieve the graphics item pointer using the Object pointer as the key.

Docas
4th February 2020, 11:06
Hello

Thank you for your help one again. I used std::map and it updates correctly. I have a problem with the routes(source and destination). I sometimes have multiple source address(string) going to different destinations and I read that with std::map() i cannot have multiple keys. The mapping that worked for the links(unique keys and values) is


std::map< std::string, QGraphicsItem * > QgraphicRectItems;//declared in my .h file

rectangleColor->setData(0, int(pState->m_eState));
QgraphicRectItems.insert(std::make_pair(pState->m_strLinkId, rectangleColor));//make a pair of QGraphicRect items on the scene and their link IDs

then on my update function i call the mappings like this:

std::map< std::string, QGraphicsItem *>::iterator it = QgraphicRectItems.find(pState->m_strLinkId);
if (it != QgraphicRectItems.end()) {
QGraphicsItem *name = it->second;
name->setData(0, int(pState->m_eState));
QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem *>(name);
getTitleDescriptionColor(pState, m_backgroundColor, rect);


But for the routes this doesn't work because sometimes i have multiple keys, so I implemented std::multimap like this:



std::multimap<std::string, QGraphicsItem *> RouteSourceDuplicates;//declared on my .h file

else if (CORE::checkPtrType<C2DM::RouteState>(_pTitle) == true)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)_pTitle;

rectangle = new QGraphicsRectItem(QRectF(0, 0, 60, 60), text);

rectangle->setData(0, int(pRoute->m_eTxLinkState));
RouteSourceDuplicates.insert(std::make_pair(pRoute->m_strSource, rectangle));//make a pair of QGraphicRect items on the scene and their source IDs


then on my calling function i did like this:


else if (_eType == QSubscription::UPDATED_TITLES)
{
C2DM::RouteState *pRoute = (C2DM::RouteState*)objPtr->_clone();

for (std::multimap<std::string, QGraphicsItem *>::iterator Values = RouteSourceDuplicates.begin();
Values != RouteSourceDuplicates.end(); ++Values)
{

QGraphicsItem *nameSource = Values->second;
nameSource->setData(0, int(pRoute->m_eTxLinkState));
QGraphicsRectItem *rectSource = qgraphicsitem_cast<QGraphicsRectItem *>(nameSource);//it breaks on this qgraphicitem_cast

getTitleDescriptionColor(pRoute, m_backgroundColor, rectSource);

}



It only works the first time i load the data, second time it just crushes. it gives an Access violation executing location error(please see attachment,it has the error where it breaks). I am struggling to fix it. I am asking for your guidance once again to make it work, please.
the duplicate keys in my data is like this:



pRoute->m_strSource[Node A] pRoute->m_strDestination[Node B] //same source address but different destinations
pRoute->m_strSource[Node A] pRoute->m_strDestination [Node C]

and I am using pRoute->m_strSource as my key and QgraphicsItem as a value in std::multimap

d_stranz
4th February 2020, 16:03
second time it just crushes.

The error seems to be due to an invalid QGraphicsRectItem pointer. Either you are storing a NULL pointer in your map / multimap or you have deleted a QGraphicsRectItem and you have not updated the map / multimap to remove that pointer. So when qgraphicsitem_cast() tries to access the item (the rect pointer) it is accessing a NULL or invalid pointer and it crashes.

I still do not understand why you are calling clone() every time you access one of your DM:: Object instances. Why? You don't save these cloned copies anywhere. Just cast the "objPtr" to whatever it should be and use it. Don't clone it, because you are simply wasting time and fragmenting memory by repeatedly creating new instances of things, only to throw them away a few lines later when the pointer goes out of scope at the end of the else if() clause.

Docas
5th February 2020, 16:12
Thank you for your help once more, its working now, it was an invalid pointer like you said. Concerning the clone() , its the only way I knew how to retrieve the object instances, I am still newbie in this. I am going to try and implement as you advise.Thank you.

I got another question please if I am still allowed. The nodes(QGraphicsRectItems) in the scene keeps crossing paths. I know I am supposed to set the setPos() for each of the nodes on the scene but I do not how to store the items so that I can set individual setPos(). I read my data from the object instance like this:




void GatewayWidget::addRect(DM::Object *_pTitle)
{

C2DM::RouteState *pRoute = (C2DM::RouteState*)_pTitle;
QGraphicsRectItem *rectangle;
QGraphicsTextItem *text;

text = scene->addText(QString::fromStdString(pRoute->m_strSource));// pRoute->Source may contain 4, 8 or any number of nodes that are retried from the data
text->setPos(600, (qrand() % 400)); //try to setpos() but its ugly
rectangle = new QGraphicsRectItem(QRectF(0, 0, 60, 60), text); //then put it in QGraphicsRectItems



I tried to set randomly position them, which is not ideal. Now this makes the QGraphicsLineItems *line that I use to connect source and destination to cross paths(please see attachment). So I am asking for your guidance, on how I can store the nodes(pRoute->Source) ,which will enable me to retrieve and setPos() for each QGraphicRectItem?

d_stranz
5th February 2020, 18:44
how I can store the nodes

I think you need to do some research online. Start by using Google for "directed graph c++" and read about the different ways to implement graph data structures. (Your DM Object trees and the matching QGraphicsItem tree) are directed graphs in computer science / mathematics terminology.

Next you need to use Google to find information on "directed graph layout". There are many ways to lay out graphs, but this Wikipedia article on "Layered graph drawing" (https://en.wikipedia.org/wiki/Layered_graph_drawing) might be good for your needs. To draw a layered graph, you create a "row" for each level in your tree. You then count the number of nodes at each level, and the divide the row up into that many boxes and put one node in each box, under the parent it belongs to. You can see an example on the Wikipedia page.