i m working on a project that consist on creating an uml tool with qt and for now i have a problem with drawingtext on an arrow so this is my code :
void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem * ,QWidget *)
{
if (myStartItem->collidesWithItem(myEndItem))
return;
QPen myPen = pen();
myPen.setColor(myColor);
qreal arrowSize = 20;
painter->setPen(myPen);
painter->setBrush(myColor);
QLineF centerLine(myStartItem->pos(), myEndItem->pos());
QPolygonF endPolygon = myEndItem->polygon();
QPointF p1 = endPolygon.first() + myEndItem->pos();
QPointF p2;
QPointF intersectPoint;
QLineF polyLine;
for (int i = 1; i < endPolygon.count(); ++i)
{
p2 = endPolygon.at(i) + myEndItem->pos();
polyLine = QLineF(p1, p2);
QLineF::IntersectType intersectType =
polyLine.intersect(centerLine, &intersectPoint);
if (intersectType == QLineF::BoundedIntersection)
break;
p1 = p2;
}
setLine(QLineF(intersectPoint, myStartItem->pos()));
double angle = ::acos(line().dx() / line().length());
if (line().dy() >= 0)
angle = (Pi * 2) - angle;
QPointF arrowP1 = line().p1() + QPointF(sin(angle + Pi / 3) * arrowSize,
cos(angle + Pi / 3) * arrowSize);
QPointF arrowP2 = line().p1() + QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
cos(angle + Pi - Pi / 3) * arrowSize);
arrowHead.clear();
arrowHead << line().p1() << arrowP1 << arrowP2;
painter->drawLine(line());
//painter->drawPolygon(arrowHead);
if (isSelected())
{
painter->setPen(QPen(myColor, 1, Qt::DashLine));
QLineF myLine = line();
myLine.translate(0, 4.0);
painter->drawLine(myLine);
myLine.translate(0,-8.0);
painter->drawLine(myLine);
QPoint point = QPoint( 10, 20 );
painter->drawText( point, "You can draw text from a point..." );
}
}
and nothing happens i can draw the arrow but the text does not appear on the arrow what should i do ? please i need some help
IMO you did that wrong.
You should compose your graphics item using QGraphicsPathItem, QGraphicsPolygonItem, QGraphicsRectItem, and QGraphicsSimpleTextItem instead drawing everything yourself.
Just provide some root item responsible for managing children (lines text and polygons). It will be easier to do this properly.
Secondly your paint method is faulty. You should restore initial state of the painter!
And finally I'm pretty sure your problem is caused by incorrect implementation of boundingRect. It is quite common mistake when performing such complex drawing in paint method.
Related
I am doing a program to draw an arc and move/rotate it according to the position of the mouse cursor :
This image illustrates the scene when the program is first run.
The following images illustrates what is displayed after a mouse click. The mouse click happened at the top right point of the line.
After getting the coordinates of the intersection point (between the line and the circle), I want to set the position of the center of the arc to the intersection point.
But, as you can see, the arc is not where I wish it was. Strangely, when I draw a rectangle whose topLeft point is at the intersection point, it works :
I guess the problem has to be with scene/parent/item coordinates... But I can't find where :/
Here is a sample of the code : (DrawingScene inherits QGraphicsScene)
void DrawingScene::drawStates(){
m_ellipse = new QGraphicsEllipseItem(80.0, 80.0,120.0,120.0);
addItem(m_ellipse);
m_arc = new GraphicArcItem(62.0, 62.0,50.0,50.0);
m_arc->setParentItem(m_ellipse);
m_arc->setStartAngle(0);
m_arc->setSpanAngle(270 * 16);
QLineF line_vertical(140.0,80.0,140.0,200.0);
addLine(line_vertical);
QLineF line_horizontal(80.0,140.0,200.0,140.0);
addLine(line_horizontal);
QLineF line(QPointF(62.0,62.0), QPointF(140.0,140.0));
m_lineToCenter = new QGraphicsLineItem(line);
addItem(m_lineToCenter);
}
void DrawingScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
p1 = event->scenePos();
QLineF lineToCenter(QPointF(140.0,140.0), p1);
m_lineToCenter->setLine(lineToCenter);
QLineF horizontalLine(QPointF(140.0,140.0),QPointF(200.0,140.0));
double angleBetweenLines = horizontalLine.angleTo(lineToCenter);
double x = 60.0 * cos(angleBetweenLines * 3.14 / 180.0);
double y = -60.0 * sin(angleBetweenLines * 3.14 / 180.0);
QPointF intersectionPoint(x,y);
QPointF topLeft = intersectionPoint + QPointF(140.0,140.0);
addRect(QRectF(topLeft, QSizeF(60.0,60.0)));
m_arc->setPos(topLeft);
}
Any help would be more than welcome :)
edit :
Working code for moving the arc :
p1 = event->scenePos();
QLineF lineToCenter(QPointF(140.0,140.0), p1);//center of circle to mouse position
double angleBetweenPositions = lineToCenter.angleTo(m_lineToCenter->line());
m_lineToCenter->setLine(lineToCenter);
QLineF horizontalLine(QPointF(140.0,140.0),QPointF(200.0,140.0));
double angleBetweenLines = horizontalLine.angleTo(lineToCenter);
double x = 60.0 * cos(angleBetweenLines * 3.14 / 180.0);
double y = -60.0 * sin(angleBetweenLines * 3.14 / 180.0);
QPointF newPoint(x,y);
QPointF ellipse_center = m_ellipse->rect().center();
QPointF intersection_point = intersection_point + ellipse_center;
GraphicArcItem *arc2 = new GraphicArcItem(intersection_point.rx()- 25.0,
intersection_point.ry() - 25.0,50.0,50.0);
addItem(arc2);
m_arc->setPos(intersection_point.rx()-85.0, intersection_point.ry() - 85.0);//why 85 ??
Code for the rotation :
m_arc->setCurrentRotation(m_arc->getCurrentRotation() + angleBetweenPositions);
m_arc->setTransformOriginPoint(m_arc->getCenter());
m_arc->setRotation(m_arc->getCurrentRotation());
Edit : Here are the key parts of the code solving the problem :
/*Return the center point of the arc in the parent coordinates*/
QPointF GraphicArcItem::getCenter(){
int xCenter = rect().x() + rect().width()/2;
int yCenter = rect().y() + rect().height()/2;
QPointF center = /*mapToParent(*/QPointF(xCenter,yCenter)/*)*/;
return center;
}
p1 = event->scenePos();
QPointF ellipse_center = m_ellipse->rect().center();
QLineF lineToCenter(ellipse_center, p1);//center of circle to mouse position
double angleBetweenPositions = lineToCenter.angleTo(m_lineToCenter->line());
QLineF horizontalLine(ellipse_center,QPointF(200.0,140.0));
double angleBetweenLines = horizontalLine.angleTo(lineToCenter);
double x = 60.0 * cos(angleBetweenLines * 3.14 / 180.0);
double y = -60.0 * sin(angleBetweenLines * 3.14 / 180.0);
QPointF newPoint(x,y);
QPointF intersection_point = newPoint + ellipse_center;
m_arc->setPos(intersection_point.rx() - 85.0, intersection_point.ry() - 85.0);
m_arc->setCurrentRotation(angleBetweenPositions);
QPointF rotation_center = m_arc->mapFromItem(m_arc, m_arc->getCenter());
m_arc->setTransformOriginPoint(rotation_center);
m_arc->setRotation(m_arc->getCurrentRotation());
The rectangle and the arc have different parents (the scene is the parent of the rectangle and m_ellipse of the arc, hence the references of their coordinates are different. To test it just add a new arc/circle (different from m_arc and without using setParentItem(m_ellipse);) to the scene - it should have the correct screen position. To achieve the desired result I would suggest you to play with mapTo<something>/mapFrom<something> methods. I presume that mapToParent will do the trick, but you should check it anyway.
I have redefined my own QGraphicsItem to show a LineString. I redefined this because I need to create my own boundingbox and painter redefined the abstract method.
Now I have this code:
QRectF myQGraphicsLine::boundingRect() const
{
double lx = qMin(myLine->getX(0), myLine->getX(1));
double rx = qMax(myLine->getX(0), myLine->getX(1));
double ty = qMin(-myLine->getY(0), -myLine->getY(1));
double by = qMax(-myLine->getY(0), -myLine->getY(1));
return QRectF(lx-size/2,ty, rx -lx + size, by-ty).;
}
void myQGraphicsLine::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
pen.setColor(Qt::red);
QLine line(myLine->getX(0), -myLine->getY(0), myLine->getX(1), -myLine->getY(1));
pen.setWidth(size);
painter->setPen(pen);
painter->setBrush(brush);
painter->drawLine(line);
}
This all work fine, but I have a little problem with the boundingRec.
If the line follows the x- or y-axis I get this result:
And in other position I get this:
and I need this:
Does anyone know any way to rotate the boundinRec? Thanks a lot!
I fixed the problem redefining QGraphicsItem::shape() method.
For use this, I created a QPoligonF with my line shape, in my case I used this function:
void myQGraphicsLine::createSelectionPolygon()
{
QPolygonF nPolygon;
QLineF line(myLine->getX(0), -myLine->getY(0), myLine->getX(1), -myLine->getY(1));
qreal radAngle = line.angle()* M_PI / 180; //This the angle of my line vs X axe
qreal dx = size/2 * sin(radAngle);
qreal dy = size/2 * cos(radAngle);
QPointF offset1 = QPointF(dx, dy);
QPointF offset2 = QPointF(-dx, -dy);
nPolygon << line.p1() + offset1
<< line.p1() + offset2
<< line.p2() + offset2
<< line.p2() + offset1;
selectionPolygon = nPolygon;
update();
}
The paint(),boundingRect() and shape() methods stayed like this:
QRectF myQGraphicsLine::boundingRect() const
{
return selectionPolygon.boundingRect();
}
void myQGraphicsLine::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
pen.setColor(Qt::red);
QLineF line(myLine->getX(0), -myLine->getY(0), myLine->getX(1), -myLine->getY(1));
pen.setWidth(size);
painter->setPen(pen);
painter->setBrush(brush);
painter->drawLine(line);
}
QPainterPath myQGraphicsLine::shape() const
{
QPainterPath path;
path.addPolygon(selectionPolygon);
return path;
}
Thanks all for your answers!!
A QRect or QRectf cannot be rotated. Its sides are always parallel to the vertical and horizontal axes. Think of it as a point (x, y) giving the upper left corner of the rectangle plus 'width' and 'height' members. No matter what transformation you perform on the QRect it will always interpret 'width' as a horizontal distance and 'height' as vertical distance.
You can extract the four corners of the QRect as points and then rotate those points, but there will be no way to convert the rotated points back to a QRect unless they just happen to be parallel to the sides of the viewport/window.
I am using Qt 5.6, I want to draw a number of text labels around a circle and rotate the text labels to orientate the text according to its position around the circle, so 12 o'clock would have a 0 degree rotation, 3 o'clock would be rotated 90 degrees, 6 o'clock would be rotated 180 degrees etc.
I am aligning the text around its centre position:
void drawText(QPainter* pobjPainter, qreal fltX, qreal fltY
,int intFlags, const QString* pobjText) {
const qreal fltSize = 32767.0;
QPointF ptfCorner(fltX, fltY - fltSize);
if ( (intFlags & Qt::AlignHCenter) == Qt::AlignHCenter ) {
ptfCorner.rx() -= fltSize / 2.0;
} else if ( (intFlags & Qt::AlignRight) == Qt::AlignRight ) {
ptfCorner.rx() -= fltSize;
}
if ( (intFlags & Qt::AlignVCenter) == Qt::AlignVCenter ) {
ptfCorner.ry() += fltSize / 2.0;
} else if ( (intFlags & Qt::AlignTop) == Qt::AlignTop ) {
ptfCorner.ry() += fltSize;
}
QRectF rctPos(ptfCorner, QSizeF(fltSize, fltSize));
pobjPainter->drawText(rctPos, intFlags, *pobjText);
}
I would like to apply a rotation to the text.
I would like to reproduce something similar to what is shown:
http://www.informit.com/articles/article.aspx?p=1405545&seqNum=2
It seems that the rotate function rotates the entire painter canvas, so the coordinates must take into account the rotation which is really giving me a hard time. I want to position the text around the ellipse and then rotate it, how do I know what the coordinates should be?
Sticking with the clock example you could try something like...
virtual void paintEvent (QPaintEvent *event) override
{
QPainter painter(this);
double radius = std::min(width(), height()) / 3;
for (int i = 0; i < 12; ++i) {
int numeral = i + 1;
double radians = numeral * 2.0 * 3.141592654 / 12;
/*
* Calculate the position of the text centre as it would be required
* in the absence of a transform.
*/
QPoint pos = rect().center() + QPoint(radius * std::sin(radians), -radius * std::cos(radians));
/*
* Set up the transform.
*/
QTransform t;
t.translate(pos.x(), pos.y());
t.rotateRadians(radians);
painter.setTransform(t);
/*
* Specify a huge bounding rectangle centred at the origin. The
* transform should take care of position and orientation.
*/
painter.drawText(QRect(-(INT_MAX / 2), -(INT_MAX / 2), INT_MAX, INT_MAX), Qt::AlignCenter, QString("%1").arg(numeral));
}
}
I can change the resolution of an image with a slider. I use QGraphicsScene for displaying image.But the problem is when I resize image, I couldn't focus to a point.You can see my code here.
This is first part, so its for resize an image.It's working.
void MainWindow::on_sld_scale_valueChanged(int value) {
//for resize an image with slider
int w = m_image->width();
int h = m_image->height();
int new_w = (w * value)/100;
int new_h = (h * value)/100;
m_pixmap = QPixmap::fromImage(
m_image->scaled(new_w, new_h, Qt::KeepAspectRatio, Qt::FastTransformation));
This second part of my function.It is my algorithm for focus to middle point of widget but it doesn't work.
//for focus to middle point of widget
auto views = m_scene->views();
Q_ASSERT(views.size() == 1);
auto view = views.first();
int pos_x = view->horizontalScrollBar()->value();
int pos_y = view->verticalScrollBar()->value();
show_pixmap();
qDebug() << "x" << pos_x << "w" << view->width()
<< "sbw" << view->verticalScrollBar()->width()
<< "y" << pos_y << "h" << view->height()
<< "sbh" << view->horizontalScrollBar()->height();
int a = pos_x + (view->width()) + (view->verticalScrollBar()->width());
int b = pos_x + (view->height()) + (view->horizontalScrollBar()->height());
a = a * (value/100.0);
b = b * (value/100.0);
m_pixmap = QPixmap::fromImage(*m_image);
view->horizontalScrollBar()->setValue(a);
view->verticalScrollBar()->setValue(b);
}
This is my source code.I can resize the image but I can't focus to middle point.Can you help me to solve this problem?
Don't you mix QScrollBar::width to QScrollBar::maximum? They have difficult relation.
You better don't touch view's scroll bars. Try QGraphicsView::centerOn or even QGraphicsView::fitInView (did you mean "focus" to be "place to center of the view"?). Like this:
const qreal centerX = pixmapItem->x() + pixmapItem->width() / 2;
const qreal centerY = pixmapItem->y() + pixmapItem->height() / 2;
viewer->centerOn(centerX , centerY);
where pixmapItem is a QGraphicsPixmapItem.
As #mvidelgauz supposed, why don't you scale a QGraphicsPixmapItem in the scene? May this be a better solution to rescale it?
Don't recreate pixmap, use QGraphicsView's methods setResizeAnchor() and scale()
I have a Qt application for creating some composite graphic pictures purpose. E.g. user selects some image component from the toolbar, places it on the QGraphicsScene, transforms it and gets profit.
The question is: how to perform scale and rotation operations on the selected items.
For now i have inherited my own Scene class and using this code for mouseMoveEvent:
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *evt)
{
QGraphicsScene::mouseMoveEvent(evt);
//bool isLMB = QApplication::mouseButtons() & Qt::LeftButton;
bool isShift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
bool isCtrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
if (this->newChip)
{
qreal ox = this->newChip->pos().x();
qreal oy = this->newChip->pos().y();
qreal nx = evt->scenePos().x();
qreal ny = evt->scenePos().y();
qreal dx = nx - ox;
qreal dy = ny - oy;
qreal w = this->newChip->transform().m11() * this->newChip->boundingRect().width();
qreal h = this->newChip->transform().m22() * this->newChip->boundingRect().height();
qreal l = sqrt(pow((float) dx, (float) 2.f) + pow((float) dy, (float) 2.f));
if (!isShift && !isCtrl)
{
this->newChip->setPos(evt->scenePos());
}
if (isShift)
{
//this->newChip->scale(fabs(w / dx), fabs(h / dy));
qDebug() << "scale: " << dx / w << dy / h;
}
if (isCtrl)
{
//this->newChip->rotate((qreal) (acos((float) (dx / l)) * 180.f / 3.14f));
qDebug() << "rotate: " << acos((float) (dx / l)) * 180.f / 3.14f;
}
}
}
But i do want a more pretty rotation and scale operations, just like any painting software when operating a selection, for example:
P.S.: yeah, i've started modifying the 40000 chips demo application =)