How to draw and fill a triangle with QPainter? - qt

This is what I tried, it gave me no output. Where am I going wrong?
// Start point of bottom line
qreal startPointX1 = 600.0;
qreal startPointY1 = 600.0;
// End point of bottom line
qreal endPointX1 = 600.0;
qreal endPointY1 = 1200.0;
// Start point of top line
qreal startPointX2 = 600.0;
qreal startPointY2 = 600.0;
// End point of top line
qreal endPointX2 = 800.0;
qreal endPointY2 = 1200.0;
QPainterPath path;
// Set pen to this point.
path.moveTo (startPointX1, startPointY1);
// Draw line from pen point to this point.
path.lineTo (endPointX1, endPointY1);
path.moveTo (endPointX1, endPointY1);
path.lineTo (endPointX2, endPointY2);
path.moveTo (endPointX2, endPointY2);
path.lineTo (startPointX1, startPointY1);
painter.setPen (Qt :: NoPen);
painter.fillPath (path, QBrush (QColor ("blue")));
I have just tried to create a path between these 3 points and fill the area, but there is no output shown.

I think you do not need to call moveTo() function after you call lineTo() because the current position already updated to the the end point of the line you draw. Here is the code that draws a rectangle for me:
// Start point of bottom line
qreal startPointX1 = 600.0;
qreal startPointY1 = 600.0;
// End point of bottom line
qreal endPointX1 = 600.0;
qreal endPointY1 = 1200.0;
// Start point of top line
qreal startPointX2 = 600.0;
qreal startPointY2 = 600.0;
// End point of top line
qreal endPointX2 = 800.0;
qreal endPointY2 = 1200.0;
QPainterPath path;
// Set pen to this point.
path.moveTo (startPointX1, startPointY1);
// Draw line from pen point to this point.
path.lineTo (endPointX1, endPointY1);
//path.moveTo (endPointX1, endPointY1); // <- no need to move
path.lineTo (endPointX2, endPointY2);
//path.moveTo (endPointX2, endPointY2); // <- no need to move
path.lineTo (startPointX1, startPointY1);
painter.setPen (Qt :: NoPen);
painter.fillPath (path, QBrush (QColor ("blue")));

If you want use QRectF
QRectF rect = QRectF(0, 0, 100, 100);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
painter.fillPath(path, QBrush(QColor ("blue")));

The documentation says: "Moving the current point will also start a new subpath (implicitly closing the previously current path when the new one is started)".
This means you should once move to the origin of the path, then use only lineTo in order to draw the shape to be filled.
I added this answer because the answer "I think you do not need to call moveTo() function after you call lineTo() because the current position already updated to the the end point of the line you draw." is quite misleading. The moveTo is not unnecessary, it's actually causing the problem.

Related

QT using QCustomPlot - Need to detect mouse not over chart

I am using QCustomPlot in a application which is focused on the graph which displays results from a external device. I have a cursor which uses the QMouseEvent. Whenever I get the mouse event it draws a horizontal line and vertical line from the mouse position to the axis.
void PlotClass::ChartMouseMove(QMouseEvent* mouse){
double x = ui->customplot->xAxis->pixelToCoord(mouse->pos().x());
double y = ui->customplot->yAxis->pixelToCoord(mouse->pos().y());
//QCPItemStraightLine *infLine = new QCPItemStraightLine(ui->customplot);
// infLine->point1->setCoords(x, 0); // location of point 1 in plot coordinate
// infLine->point2->setCoords(2, 1); // location of point 2 in plot coordinate
qDebug() << x << y;
// ui->customplot->xAxis->range().minRange();
double xLow = ui->customplot->xAxis->range().lower;
double xHigh = ui->customplot->xAxis->range().upper;
double yLow = ui->customplot->yAxis->range().lower;
double yHigh = ui->customplot->yAxis->range().upper;
infLinex->start->setCoords(x, yLow); // location of point 1 in plot coordinate
infLinex->end->setCoords(x, yHigh); // location of point 2 in plot coordinate
infLiney->start->setCoords(xLow, y); // location of point 1 in plot coordinate
infLiney->end->setCoords(xHigh, y); // location of point 2 in plot coordinate
ui->customplot->replot();
}
What I need to do is remove the cursor when the mouse is no longer over the chart. Not sure how to do this.
Also would be nice to paint the actual cursor position onto the lines in text (the values from the axis.)
Ok figured it out. I just put this function call in the timer event (possibly not the best way but it works)
void PlotClass::CheckHidecursor(void){
if(!Hidecursor && !ui->customplot->underMouse()){
Hidecursor = true;
infLinex->setVisible(false);
infLiney->setVisible(false);
yLabel->setVisible(false);
xLabel->setVisible(false);
qDebug() << "Hide";
ui->customplot->replot();
}
}
So it hides the lines and the numbers I am putting on if the mouse is no longer over the chart widget. The key function I found was QWidget::underMouse() which gives a true/false reponse.

How to draw a line with x length in the direction of the mouse position in unity3D?

My question seems rather simple but i cant figure it out myself.
I want to draw a line with a fixed length from my transform.position in the direction where the mouse cursor is.
The things i figured out:
var mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
lazer.setPosition(0, transform.position);
// here is where the failing starts. i need to calculate the end position.
lazer.setPosition(1, ?)
Thanks A.
I think what you are looking for is the variable normalized on either the Vector2 or Vector3 class. Something like this will give you a new vector with the same length (magnitude, actually) every time:
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 offsetPos = mousePos - transform.position;
Vector3 newVec = offsetPos.normalized * scale; // this is the important line
newVec += transform.position;

Drawing QImage on a QPainter which has inverted y-axis

I have a scene with an inverted y-axis. Everything is correctly drawn except QImages.
I use drawIage() as:
QRectF aWorldRect = ...
QRectF anImageRect = QRectF(0, 0, theQImage.width(), theQImage.height())
thePainter->drawImage(aWorldRect, theQImage, anImageRect;
I get undefined graphics outside (to the top of) where the image should be displayed. This is normal because y-axis is inverted. So I expected something like that may fix the issue:
QRectF anImageRect = QRectF(0, 0, imgWidth, -imgHeight)
It has the same effect. If I do aWorldRect = aWorldRect.noralized() before calling drawImage(), I get the image in the correct rectangle but mirrored so I did aQImage = aQImage.mirrored(). Now the image is correctly displayed in the correct rectangle
I consider this a workaround which I don't like to keep. So, can someone tell me what should be done to get the image displayed, the right way?
Update
Here I put a minimal sample of my problem that is ready to compile:
Update 2014-04-09 10:05 EET
Updated the sample code little bit to make really work using the workaround
#include <QtGui>
const int WIDTH = 640;
const int HEIGHT = 480;
class View : public QGraphicsView
{
protected:
void drawBackground(QPainter *p, const QRectF & rect)
{
QImage img = QImage("/usr/share/backgrounds/images/stone_bird.jpg"); // or any other
/* The next three lines makes everything displayed correctly but
should be considered a workaround */
/* I ignore the rect that is passed to the function on purpose */
QRectF imageRect = QRectF(QPointF(0, 0), QPointF(img.width(), img.height()));
QRectF theSceneRect = sceneRect().normalized();
p->drawImage(theSceneRect, img.mirrored(), imageRect);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
View w;
/* I don't want to change the code below */
w.setScene(new QGraphicsScene(QRectF(QPointF(0, HEIGHT), QPointF(WIDTH, 0))));
w.scale(1, -1);
w.scene()->addLine(0, HEIGHT, WIDTH, 0);
w.showMaximized();
return a.exec();
}
The approach of reversing the Y coordinate value is right but the implementation was faulty.
QRectF's documentation shows that it takes (x, y, width, height). Giving height as negative makes little sense. Instead try the other constructor which takes topLeft and bottomRight.
QRectF anImageRect(QPointF(0.0f, -imgHeight), QPointF(imageWidth, 0.0f));
EDIT:
It seems that the only drawings like line, arc, etc. are affected by the scale (1, -1) transform you set on the view. drawImage continues to render upside down due to the scale set. The simple fix is to set the scale back to (1, -1). Here's the updated code:
void drawBackground(QPainter *p, const QRectF & rect)
{
QImage img = QImage("/usr/share/backgrounds/images/stone_bird.jpg");
// backup the current transform set (which has the earlier scale of (1, -1))
const QTransform oldTransform = p->transform();
// set the transform back to identity to make the Y axis go from top to bottom
p->setTransform(QTransform());
// draw
QRectF theSceneRect = sceneRect().normalized();
p->drawImage(theSceneRect, img);
// revert back to the earlier transform
p->setTransform(oldTransform);
}
Updated on 2014-04-14 14:35 EET
I could finally solve the problem reliably by replacing the two lines
QRectF theSceneRect = sceneRect().normalized();
p->drawImage(theSceneRect, img.mirrored(), imageRect);
of my question to
QRectF theSceneRect = sceneRect(); // Not normalized. It is no more workaround :)
qreal x = theSceneRect.x();
qreal y = theSceneRect.y();
qreal w = theSceneRect.width();
qreal h = theSceneRect.height();
qreal sx = imageRect.x();
qreal sy = imageRect.y();
qreal sw = imageRect.width();
qreal sh = imageRect.height();
p->translate(x, y);
p->scale(w / sw, h / sh);
p->setBackgroundMode(Qt::TransparentMode);
p->setRenderHint(QPainter::Antialiasing, p->renderHints() &
QPainter::SmoothPixmapTransform);
QBrush brush(img);
p->setBrush(brush);
p->setPen(Qt::NoPen);
p->setBrushOrigin(QPointF(-sx, -sy));
p->drawRect(QRectF(0, 0, sw, sh));
p->restore();
This is inspired by the implementation of the QPainter::drawImage() which is not reliable in such cases due to many if statements handling rectangles with negative values of width or height.
It would be better if I made the solution in another function but I kept it this way to be more compatible with the code in my question.

How to draw an arc with Qt?

Consider the following diagram:
I have information about the center point of both the lines, the angle in between, and the length of both the lines.
The issue is to draw an arc starting at the end of the bottom line and touching the above slanting line (the way shown below):
/
/
/
/.
/ .
/___.
I saw these arc drawing functions of Qt:
http://qt-project.org/doc/qt-5.1/qtgui/qpainter.html#drawArc
These functions need a rectangle as an argument where as I don't have any.
How should I use these functions to draw the arc as shown above?
QPointF O; // intersection of lines
QPointF B; // end point of horizontal line
QPointF A; // end point of other line
float halfSide = B.x-O.x;
QRectF rectangle(O.x - halfSide,
O.y - halfSide,
O.x + halfSide,
O.y + halfSide);
int startAngle = 0;
int spanAngle = (atan2(A.y-O.y,A.x-O.x) * 180 / M_PI) * 16;
QPainter painter(this);
painter.drawArc(rectangle, startAngle, spanAngle);
You have to calculate the boundary rectangle, than the angle between the lines using atan.

QGraphicsItem's - selection & rotation

I'd like to implement application which allows user to select few QGraphicsItems and then rotate them as a group. I know that I could add all items into one QGraphicsItemGroup but I need to keep Z-value of each item. Is it possible?
I also have a second question.
I'm trying to rotate QGraphicsItem around some point (different from (0,0) - let's say (200,150)). After that operation I want to rotate this item once more time but now around (0,0). I'm using code below:
QPointF point(200,150); // point is (200,150) at first time and then it is changed to (0,0) - no matter how...
qreal x = temp.rx();
qreal y = temp.ry();
item->setTransform(item->transform()*(QTransform().translate(x,y).rotate(angle).translate(-x,-y)));
I noticed that after second rotation the item is not rotated around point (0,0) but around some other point (I don't know which). I also noticed that if I change order of operations it all works great.
What am I doing wrong?
Regarding your first problem, why should the z-values be a problem when putting them into a QGraphicsGroup?
On the other hand you could also iterate through the selected items and just apply the transformation.
I guess this snippet will solve your 2nd problem:
QGraphicsView view;
QGraphicsScene scene;
QPointF itemPosToRotate(-35,-35);
QPointF pivotPoint(25,25);
QGraphicsEllipseItem *pivotCircle = scene.addEllipse(-2.5,-2.5,5,5);
pivotCircle->setPos(pivotPoint);
QGraphicsRectItem *rect = scene.addRect(-5,-5,10,10);
rect->setPos(itemPosToRotate);
// draw some coordinate frame lines
scene.addLine(-100,0,100,0);
scene.addLine(0,100,0,-100);
// do half-cicle rotation
for(int j=0;j<=5;j++)
for(int i=1;i<=20;i++) {
rect = scene.addRect(-5,-5,10,10);
rect->setPos(itemPosToRotate);
QPointF itemCenter = rect->pos();
QPointF pivot = pivotCircle->pos() - itemCenter;
// your local rotation
rect->setRotation(45);
// your rotation around the pivot
rect->setTransform(QTransform().translate(pivot.x(), pivot.y()).rotate(180.0 * (qreal)i/20.0).translate(-pivot.x(),-pivot.y()),true);
}
view.setScene(&scene);
view.setTransform(view.transform().scale(2,2));
view.show();
EDIT:
In case you meant to rotate around the global coordinate frame origin change the rotations to:
rect->setTransform(QTransform().translate(-itemCenter.x(), -itemCenter.y()).rotate(360.0 * (qreal)j/5.0).translate(itemCenter.x(),itemCenter.y()) );
rect->setTransform(QTransform().translate(pivot.x(), pivot.y()).rotate(180.0 * (qreal)i/20.0).translate(-pivot.x(),-pivot.y()),true);

Resources