PDA

View Full Version : Position of QGraphicsItem on Scene.



Andre008
10th February 2016, 13:53
Hello,

im new in progamming wie Qt, so i had try some tutorials.
One of is:

http://www.bogotobogo.com/Qt/Qt5_QGraphicsView_QGraphicsScene.php

This is the basic of my code.

What i want to do:

On the graphics view i add two ellipse (ellipse1, ellipse2) and a line.

I change the flags of the ellipses to moveable.

So now i want that the start point of the line (x1,y2) is on the center of ellipse1 and the end point of the line (x2,y2) is at the center of ellipse2.
If one ellipse move per mouse, the line should move too.

If i add the line in this way:


line = scene->addLine(ellipse1->scenePos().x(),ellipse1->scenePos().y(),
ellipse2->scenePos().x(),ellipse2->scenePos().y(),outlinePen);


For the scenPos() function i get 0. But i dont know why.

Did anyone has an idee ?

Thx

11688116871168811687

anda_skoa
10th February 2016, 14:24
Derive a custom class from QGraphicsEllipseItem.

Add a setter method that takes a line element and an indicator to which end of the line your item is connected to.

Then in the itemChange() method of your item class, you move "your" end of the line when ever the item moves.

Cheers,
_

Andre008
10th February 2016, 17:53
thx for the answer:

i had try the modification, on basic of:

http://www.bogotobogo.com/Qt/Qt5_QGraphicsView_QGraphicsScene_QGraphicsItems.ph p

A additional class customItem is inherit from QGraphicsItem.

With this approach i get now the positions on scene from ellipse1 and ellipse2.

But drawing a line is not possible until now.

Now there are different options. The line can be member of the Dialog class or is a member of the customItem class.
For the second option, it should be drawn two lines.
One from ellipses1 to ellipses2, and one from ellipses2 to ellipses1


11689116901169111692

anda_skoa
10th February 2016, 18:58
My suggestion was to use a QGraphicsLineItem and adjusting its end points

Cheers,
_

Andre008
10th February 2016, 22:18
ok, i had tried it with:

dialog.cpp
Here i send the line pointer .


Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);


scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);

// create our object and add it to the scene
ellipse1 = new customItem();
ellipse2 = new customItem();
line = new QGraphicsLineItem(100,100,450,300);


qDebug() << "ellipse1->scenePos().x(): " << ellipse1->scenePos().x();

ellipse1->setLine(line,false);
ellipse1->setLine(line,true);

scene->addItem(ellipse1);
scene->addItem(ellipse2);
scene->addItem(line);

}

dialog.h


class Dialog : public QDialog
{
Q_OBJECT

public:
explicit Dialog(QWidget *parent = 0);
~Dialog();

private:
Ui::Dialog *ui;

QGraphicsScene *scene;

customItem *ellipse1;
customItem *ellipse2;

QGraphicsLineItem *line;

};

#endif // DIALOG_H



customItem.cpp



void customItem::setLine(QGraphicsLineItem* newLine, bool side)
{
line = newLine;
startOrEnd = side;
}

void customItem::mouseReleaseEvent(QGraphicsSceneMouseE vent *event)
{
Pressed = false;
update();
QGraphicsItem::mouseReleaseEvent(event);

if(startOrEnd == false)
{
line->line().setLine(this->scenePos().x(),this->scenePos().y(),
line->line().x2(),line->line().y2() );
}

if(startOrEnd == true)
{
line->line().setLine(line->line().x1(),line->line().y1(),
this->scenePos().x(),this->scenePos().y());

}

qDebug()<<"xPos: " << this->scenePos().x();
qDebug()<<"yPos: " << this->scenePos().y();
}




class customItem : public QGraphicsItem
{
public:
customItem();
.....

void setLine(QGraphicsLineItem line, bool side);

.....

public:

bool Pressed;

QGraphicsLineItem *line;
bool startOrEnd;

};


But it dont work.

anda_skoa
10th February 2016, 23:11
First, I thought you wanted the line to be between the two centers, so why are you using x/y?
Also why use scenePos() instead of just the position?

The center would be just


QPointF center = rect().center();


It would also be easier to get the updated position after it has changed, instead of manually reacting to mouse events.

Cheers,
_

Andre008
11th February 2016, 12:27
youre right:

i had change it to:

customItem.cpp
here ist the member variable for the center: mCenter for the ellipse


void customItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF ellipse = boundingRect();

mCenter = ellipse.center();

qDebug() << "mCenter: " << mCenter;

if(Pressed)
{
QPen pen(Qt::red, 3);
painter->setPen(pen);
painter->drawEllipse(ellipse);
}
else
{
QPen pen(Qt::black, 3);
painter->setPen(pen);
painter->drawEllipse(ellipse);
}
}


void customItem::mouseReleaseEvent(QGraphicsSceneMouseE vent *event)
{
Pressed = false;



if(startOrEnd == false)
{
line->line().setP1(mCenter);
}


if(startOrEnd == true)
{
line->line().setP2(mCenter);
}


update();
QGraphicsItem::mouseReleaseEvent(event);
}


but the line do not move:(

anda_skoa
11th February 2016, 13:13
If look at the method signature of QGraphicsLineItem::line(), which is generally a good idea, you will see that it returns a QLineF object.
Not a pointer, not a reference, an object.

You are modifying that object but are not storing or using it in any way.
So you are modifying a temporary object.

How would that change anything for the line item?

Cheers,
_

Andre008
11th February 2016, 14:52
If look at the method signature of QGraphicsLineItem::line(), which is generally a good idea, you will see that it returns a QLineF object.
Not a pointer, not a reference, an object.

You are modifying that object but are not storing or using it in any way.
So you are modifying a temporary object.

Cheers,
_

Ok, now i change the specific parts to QLineF:

customItem.cpp

I thought the storing could be made with the function setLine or how can i implement it, to assign a line to an ellipses?.




void customItem::setLine(QLineF newLine, bool side)
{
line = newLine;
startOrEnd = side;
}

void customItem::mouseReleaseEvent(QGraphicsSceneMouseE vent *event)
{
Pressed = false;

if(startOrEnd == false)
{
line.setP1(mCenter);
}


if(startOrEnd == true)
{
line.setP2(mCenter);
}

update();
QGraphicsItem::mouseReleaseEvent(event);
}




customItem.h



class customItem : public QGraphicsItem
{
public:
customItem();

QRectF boundingRect() const;

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

void setLine(QLineF newLine, bool side);

qreal getXPosEllipse();
qreal getYPosEllipse();
public:

bool Pressed;

bool startOrEnd;
//QGraphicsLineItem *line;

QLineF line;
QPointF mCenter;
};






Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);


scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);

QBrush grayBrush(Qt::gray);
QPen outlinePen(Qt::black);
outlinePen.setWidth(2);

// create our object and add it to the scene


ellipse1 = new customItem();
ellipse2 = new customItem();
line = new QGraphicsLineItem(100,100,450,300);

ellipse1->setLine(line->line(),false);
ellipse2->setLine(line->line(),true);


qDebug() << "ellipse1->getXPosEllipse(): " << ellipse1->getXPosEllipse();
qDebug() << "ellipse1->getYPosEllipse(): " << ellipse1->getYPosEllipse();
scene->addItem(ellipse1);
scene->addItem(ellipse2);
scene->addItem(line);



qDebug() << "ellipse1->getXPosEllipse(): " << ellipse1->getXPosEllipse();
qDebug() << "ellipse1->getYPosEllipse(): " << ellipse1->getYPosEllipse();

}

anda_skoa
11th February 2016, 15:48
That doesn't even make sense. Now you not even attempting to update the line item anymore.

The ellipse items need to update the position of the line item, so they need the line item.

But instead of your previous attempt of changing some random temporary line value, you need to update the line item's value.

Lets have a look at some C++ basics


int a = 5;
int b = a;
b = 3;
// which value does a have?


Cheers,
_

Andre008
11th February 2016, 18:32
That doesn't even make sense. Now you not even attempting to update the line item anymore.

The ellipse items need to update the position of the line item, so they need the line item.

But instead of your previous attempt of changing some random temporary line value, you need to update the line item's value.

Lets have a look at some C++ basics


int a = 5;
int b = a;
b = 3;
// which value does a have?


Cheers,
_



My idee was to make something like this:



int a=5;
int *b;

b= &a;
*b = 3;


For this reason i create a local variable line in the class custonItem.

anda_skoa
11th February 2016, 19:05
Yes, your code in comment #7 has the line as a pointer, that is fine.

But instead of modifying that line, you are modifying an object return by one of its getters, an object that is discarded immediately afterwards (it is a temporary object).

You write


line->line().setP1(mCenter);

which is equivalent to


QLineF a = line->line();
a.setP1(mCenter);

How do you expect "line" to move when you are not changing its values?

You need to change the data your "line" has access to, not some copy of that data.

Cheers,
_

Andre008
12th February 2016, 11:38
now it runs:

Thx for your patience anda_skoa :)

Here ist the full code:

dialog.h



#include <QDialog>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QDebug>

#include "customitem.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
Q_OBJECT

public:
explicit Dialog(QWidget *parent = 0);
~Dialog();

private:
Ui::Dialog *ui;

QGraphicsScene *scene;

customItem *ellipse1;
customItem *ellipse2;

QGraphicsLineItem *line;


};


dialog.cpp



Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);


scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);

QBrush grayBrush(Qt::gray);
QPen outlinePen(Qt::black);
outlinePen.setWidth(2);


ellipse1 = new customItem();
ellipse2 = new customItem();
line = new QGraphicsLineItem(ellipse1->scenePos().x() + ellipse1->mCenter.x(),
ellipse1->scenePos().y() + ellipse1->mCenter.y(),
ellipse2->scenePos().x() + ellipse2->mCenter.x(),
ellipse2->scenePos().y() + ellipse2->mCenter.y());

ellipse1->setLine(line,false);
ellipse2->setLine(line, true);


scene->addItem(ellipse1);
scene->addItem(ellipse2);
scene->addItem(line);
}



cusomItem.h



#include <QPen>
#include <QGraphicsItem>
#include <QPainter>

#include <QDebug>

class customItem : public QGraphicsItem
{
public:
customItem();

QRectF boundingRect() const;

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

void setLine(QGraphicsLineItem *newLine, bool side);

qreal getXPosEllipse();
qreal getYPosEllipse();
public:

bool Pressed;

bool startOrEnd;
QGraphicsLineItem *customLine;

QPointF mCenter;
};



customItem.cpp



#include "customitem.h"

customItem::customItem()
{
Pressed = false;
setFlag(ItemIsMovable);

}

QRectF customItem::boundingRect() const
{
// outer most edges
return QRectF(0,0,100,100);
}

void customItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF ellipse = boundingRect();

mCenter = ellipse.center();

//qDebug() << "mCenter: " << mCenter;

if(Pressed)
{
QPen pen(Qt::red, 3);
painter->setPen(pen);
painter->drawEllipse(ellipse);
}
else
{
QPen pen(Qt::black, 3);
painter->setPen(pen);
painter->drawEllipse(ellipse);
}
}



qreal customItem::getXPosEllipse()
{
return this->scenePos().x();
}
qreal customItem::getYPosEllipse()
{
return this->scenePos().y();
}

void customItem::mousePressEvent(QGraphicsSceneMouseEve nt *event)
{
Pressed = true;
update();
QGraphicsItem::mousePressEvent(event);

}

void customItem::mouseReleaseEvent(QGraphicsSceneMouseE vent *event)
{
Pressed = false;

if(startOrEnd == false)
{
customLine->setLine( this->scenePos().x() + mCenter.x(),this->scenePos().y()+ mCenter.y(),
customLine->line().x2(), customLine->line().y2() );
}


if(startOrEnd == true)
{
customLine->setLine( customLine->line().x1(), customLine->line().y1(),
this->scenePos().x() + mCenter.x(),this->scenePos().y()+ mCenter.y());
}

update();
QGraphicsItem::mouseReleaseEvent(event);
}


void customItem::setLine(QGraphicsLineItem *newLine, bool side)
{
qDebug() << "-------------------------------------------";
qDebug() << "&customLine:\t" << &customLine << "\t&newLine:\t" << &newLine;
qDebug() << "customLine:\t" << customLine << "\tnewLine:\t" << newLine;

customLine = newLine;
startOrEnd = side;

qDebug() << "customLine:\t" << customLine << "\tnewLine:\t" << newLine;
}

anda_skoa
12th February 2016, 13:37
Just a general suggestion for improvement:
when you have a toggle state like startOrEnd in your case, using a bool is often not a good way regarding readability.

I.e. consider reading "startOrEnd == true" in several months, what does "true" mean? An either/or question can usually not be answered with yes/no.

A way better approach is to use an enum with two values, each value appropriately named



enum ManagedLineEnd {
StartOfLine,
EndOfLine
};

ManagedLineEnd m_myLineEnd;



if (m_myLineEnd == StartOfLine)


For an example on how that is used in Qt see the two enum arguments of QString::split().

Cheers,
_