Drawing an arc correctly with Qt5 painter - c++

I am trying to draw an arc at the edge of the bounding rectangle. This is important since I want it to scale with the control. However, This leads to the edges getting clipped off when using boundingRect() as an argument for the drawArc().
QBrush brush(QColor("#007430"));
painter->setBrush(brush);
QPen pen;
pen.setStyle(Qt::SolidLine);
float lineWidth1 = 6.0;
pen.setWidthF(lineWidth1);
painter->setPen(pen);
painter->setRenderHint(QPainter::Antialiasing);
painter->drawArc(boundingRect(), 45*16, 270*16);
To make it work correctly, I must pass in a rectangle that is 1/2 of the pen width smaller on each side. Is there a more direct way to do this in QT without manual calculating/adjusting?
QRectF arcRect(0 + lineWidth1/2,
0 + lineWidth1/2,
boundingRect().width() - lineWidth1,
boundingRect().height() - lineWidth1);
painter->drawArc(arcRect, 45*16, 270*16);

I am sorry to bring you the real answer which unfortunately is no, there is no automatic way to scale to bounding box of drawing operations with QPainter in Qt5. You will therefore have to calculate this in your own code on a per case basis.
On the bright side, this calculation is not very hard, and by doing it yourself you are certain to maintain full control over the process.

Related

Is there any way to smoothly scale and draw a section of a QPixmap that's defined by a QRectF?

I'm working on a Qt 5 mac application and I'm looking for a method to smoothly scale and render a fractional section of a QPixmap as defined by a QRectF.
What I want to do actually is exactly what void QPainter::drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect) is supposed to do:
QRectF sourceRect = ...;
QRectF targetRect = ...;
painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
painter.drawPixmap(targetRect, pixmap, sourceRect);
However, as mentioned in this thread and this bug report, QPainter::SmoothPixmapTransform is ignored and this method does not work.
Short of writing my own efficient bilinear scaling method from scratch (which I am not inclined to do), is there any other method I can use to accomplish the same thing?
QPixmap::scaled() only allows scaling to integer dimensions, and provides no way to crop out a section defined by a QRectF, so that's a non-starter.
Just tossing out ideas here, but is it possible to somehow use OpenGL to render the portion of the image I want at the right scale and then render it back into a QImage or QPixmap? This kind of scaling is one of the most basic things OpenGL can do very easily and quickly, but I don't have any experience using OpenGL in a Qt desktop app so far so I'm not sure if this is possible.

Qt MainWindow with QOpenGLWIdget in Retina display displays wrong size

I have a Qt application with a MainWindow.
I embed a QOpenGLWidget in it. Everything works fine until I start using an Apple Retina Display and run my app in High DPI mode: my QOpenGLWidget is just 1/4 of the size it was supposed to have (i.e., it only fills the bottom-left part of the area it is supposed to fill). This widget is displaying raw OpenGL data (actually, an OpenSceneGraph context)
What can I do to solve this?
Found out that the best option for now, for OpenGL related widgets and events, is to use the QPaintDevice::devicePixelRatio() (http://doc.qt.io/qt-5/qpaintdevice.html#devicePixelRatio)
This implies multiplying everything that uses pixel coordinates, i.e., mouse events, resizing events, and so on. Example:
void MyGLWidget::resizeGL(int width, int height) {
width *= Application::desktop()->devicePixelRatio();
height *= Application::desktop()->devicePixelRatio();
...
// Continue with previous code
}
When running in low resolution mode in a Retina/HighDPI display, or when running in a regular display, this ratio is 1, so this seems portable to me.
From Qt docs (section OsX),
Note: The scaling is not applied to Open GL windows
I didn't try this approach on Mac, but it helped with the same issue on my Windows machine. I'm not sure if it's the best solution, though, there could be easier. Try and see if it works.
The main idea is to scale up your OpenGL content sizes manually.
First, Define the amount of scale to be performed. You can use the characteristic of physical dot per inch:
QApplication app(argc, argv);
int x = QApplication::desktop()->physicalDpiX();
int y = QApplication::desktop()->physicalDpiY();
// values 284 and 285 are the examples of reference values that we determined when DPI scaling was disabled
double scaleX = 284.0/double(x);
double scaleY = 285.0/double(y);
physicalDpi* makes possible to judge how many pixels we have for an inch. In order to define the scale, detect how much is reference value of density and then scale proportionally against the density of the physical device (next step).
Second, you have to use the scaleX and scaleY inside your QOpenGLWidget and make sure we manually scale:
sizes such as QOpenGLWidget::width() and QOpenGLWidget::height() will turn into this->width()*m_scaleX and this->height()*m_scaleY
mouse events coordinates, e.g., event->x()*m_scaleX and event->y()*m_scaleY

Qt - Using a QTransform (or similar), scale inner QRect to/from QGraphics

Some background -
Say you have QGraphicsScene, and only one view, which is a 1-1 scale with the scene.
You have a QRect A, which is represents an external view of the scene, with a pre-defined pixel size.
You have a QRect A1 which is a smaller rect inside of A.
How do you translate A1 to the scene, such that it is scaled correctly (i.e. if it's 1/4 of rect A, it will occupy 1/4 of the scene), and then undo that transform to scale a rect created in the scene to fit in rect A correctly?
I can do all this brute force, but I'm wondering if there's a way using Qt's built in classes...
After looking over some examples to try and find similar uses, I realized I'm totally missing the point - I can just set A/A1 directly to the scene, and scale the view (via the totally obvious but somehow completely overlooked until now QGraphicsView::fitInView(..)) to fit the rects inside. No rect transforms necessary. Total 'duh' moment. :)
I will need to transform mouse clicks and points in the view when interacting with it, but there is a whole nice set of mapTo* mapFrom* that will handle that nicely.
TL;DR - Use fitInView()

Qt 4.8.5 QGraphicsView::fitInView fails to fit exactly

I Have a problem trying to fit my content (stretch) of a scene into my QDeclarativeView. I load a QML file the common way. I overrode the showEvent and resizeEvent method with the code below:
QGraphicsItem* rootItem = this->scene()->items.at(0);
QRectF rootRect = rootItem->sceneBoundingRect(); // it gives me a QRectF(0,0,1920,1080)
this->fitInView(rootRect, Qt::IgnoreAspectRatio); // Aspect doesn't matters.
The problem is that it keeps showing a little white border (almost 4 pixels) around the content. I've tested in 1920x1080, 1920x1200 and 1440x900 and all those resolutions on my desktop shows the content with the same problem. Even out of fullscreen mode it keeps the little white border.
Just to make sure it was nothing from the content, I have set the view's background brush to black and the white border became black (in other words, the content is being scaled down too much to fit in view).
Subtracting values from rectangle hardcoding is not an option once it's varying the background portion depending on the content size. (It should adapt dynamically).
Any suggestions?
Just encountered the problem as well. Since the bug is not about to be solved, I'll post my partial solution. I subclassed QGraphicsView and added a method myFitInView(), that does the required scaling and centering automatically.
I guess if you need more performance you could also directly fill the the matrix, but I don't need this and therefore scale and center seperately.
Also, any previous view transformations are lost with this approach, but you could probably account for that as well, by getting the current matrix and modifying/multiplying it accordingly.
void MyGraphicsView::myFitInView(QRectF const &rect)
{
QRectF viewRect = frameRect();
double scaleX = viewRect.width() / rect.width();
double scaleY = viewRect.height() / rect.height();
QTransform trans;
trans.scale(scaleX, scaleY);
setTransform(trans, false);
centerOn(rect.width() / 2, rect.height() / 2);
}
seems like a bug in Qt: https://bugreports.qt-project.org/browse/QTBUG-11945.
my ugly workaround for that is
QRectF removeMargin11945(const QRectF& sceneRect, const QSize& viewerSize){
const int bugMargin = 2;
const double mx = sceneRect.width()/viewerSize.width()*bugMargin;
const double my = sceneRect.height()/viewerSize.height()*bugMargin;
return sceneRect.adjusted(mx, my, -mx, -my);
}
What makes it particularly ugly is that sometimes it is not required and things just work fine, be careful to distinguish those cases.

Fast QPixmap scaling

I develop a browser-like application, where the canvas has large height and "ordinary" width, something like 1024x999999. I display a picture using 512 cached QPixmap blocks (1024x128), re-using them to display newly drawing areas. So if user scrolls around some given area of the large image, CPU is not busy, the cached blocks is used. So, this is how my engine works, briefly.
Want to implement a zoom. Don't know - smooth or discrete (x2, x3, x4...). Performance questions:
Is there any effective way to scale QPixmap on-the-fly in paintEvent() without allocating too much memory?
or maybe i should think about "zoom-layers" that cache zoomed picture for different zoom factors? But this makes smooth zooming impossible...
If you take a look at the documentation, you'll see that the paintEvent in fact receives a QPaintEvent object. This object has a getter method named region() that returns a QRect detailing the region to be repainted.
void QWidget::paintEvent ( QPaintEvent * event )
{
QRect region = event->region();
...
}
So... you just need to repaint the part of the widget that is exactly inside that rectangle.
For your application, I recommend to calculate which image or images are within the rectangle, and redraw them accordingly, but only those images.
For the zoom part, Qt has optimized the way images are painted in QPainter objects if images are QPixmap objects. Or so they say...
So, you can write inside the paintEvent() method something like:
QPainter painter(this);
...
painter.drawPixmap(pos_x, pos_y, width, height, pixmap);
...
Hope that helped!