QPainter drawing lines - configuring line softness (horizontal opacity gradient) - qt

I am looking for a way to apply a horizontal opacity gradient when painting QLine elements using QPainter. Put simply, I want to be able to have the line opacity decrease the further away from the line center it is being painted. The effect I want to achieve corresponds to what a lot of image editing tools comonly describe as hardness of a brush.
Here is a sample image that compares a line using a hard brush to a soft one:
This would be a minimum example for painting a regular QLine:
QPainter p;
p.setPen(QPen(Qt::black, 12, Qt::SolidLine, Qt::RoundCap));
p.drawLine(QPointF(0,0), QPointF(1024,1024));
How and where would I configure the line hardness I am describing? Is there something like a fall-off property when painting QLine elements?
In the docs I could only find examples for how to apply linear gradients between set points, which is not what I am looking for.

That's not QPen painting, that's brush painting, like in say photoshop, and Qt doesn't really support such functionality out of the box.
But it is quite easy to implement, you need a brush stencil pixmap, and you simply draw that pixmap on your target paint device along a line at a given step.
The line interpolation part is already answered here.
It is recommended that the brush stencil is an 8 bit grayscale QImage, then you can easily get a colorized version of that by using the greyscale value as an alpha value for the solid color of choice. QImage is preferable, as it offers individual pixel access. This allows to have any type of brush aside from hard and soft, including certain artistic brushes.
Naturally, if all you need is a soft brush, you can generate that directly in the desired color by utilizing Qt's existing gradients and skip the stencil colorizing part. You can use QPainter to procedurally draw colorizable or full color stencils to use as a brush.

Related

Animate LineSeries with pulsating gradient along line Qt QML

Is there a way to animate a LineSeries with a gradient along the path?
I only found options to animate it statically by replacing the lineseries coloring brush with one that has a gradient for some fixed value. I could set the gradient now everytime a new point is added. But then the gradient would still be only along the y axis. Is there a way to color it along the path.
For example by setting a callback in between the paint function that takes each XYPoint and uses the value to compute a color value using a QVariantAnimation? I'm lacking the experience with QML to know whether or how to approach this, should I subclass something in Python and register a type or how is this customization best approached?

QT : Masking an image - Suggestions?

I dont know if I am using the correct term here. However this is what I am trying to achieve and I would like some suggestions on how I could achieve that. I want to have a circle with border visible. Now here is the hard part and something I dont even know how to start with. I want to manipulate the circle in such a way that the borders of the circle are visible and its center is not (i.e Pretty much that it has a hole in it and would show what ever is placed under it)I would then like to have another image placed under the circle such that only the part of the image that is under the transparent part of the circle is shown the parts outside the transparent boundary of the circle become invisible. Any suggestions on how I could achieve this. It seems that googling isnt helping me.
I would suggest the alternative way for unmasking a circular area of an image. You can specify the clip region - the area where you need to perform painting. For example:
[..]
QPainter painter(this);
// Sample circular area.
QRegion r(QRect(100, 100, 200, 200), QRegion::Ellipse);
painter.setClipRegion(r);
[..]
painter.drawImage(0, 0, image);
[..]
This will draw only those parts of your image that are inside of the circle with radius 200. All the rest pixels will be hidden.
You can handle mouse move event to move this "circle" over the image like a loupe.
UPDATE
Below is the sample code that generates an image with circular mask and insert it into the label:
QPixmap target(500, 500); // the size may vary
QPixmap source("image.png");
QPainter painter(&target);
QRegion r(QRect(100, 100, 200, 200), QRegion::Ellipse);
painter.setClipRegion(r);
painter.drawPixmap(0, 0, source);
QLabel l;
l.setPixmap(target);
l.show();
You might want to have a look at the Composition Example.
In short you could draw the first image and then use one of the Composition Modes to draw the second image on top (or the other way around). Make sure to convert the images to ARGB32 before using them.
To make the inner Part of the Circle transparent you can adjust the Alpha Channel accordingly.
Here is a small Example using Composition mode:
QPainter p(&imageCircle);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.drawImage(image);
p.end()
Here you can find the Qt Documentation of QPainter.

Semi-transparent QWidget over QGLWidget: Strange results

I have a full size QGLWidget which paints the application background using QPainter (might change to native openGL commands in the future).
On top of this QGLWidget I use QWidgets (non-GL) for the user interface elements. These are, for example, QLineEdits and QPushButtons. I put them into a custom painted QWidget which uses semi-transparent background painting. The paintEvents of the QLineEdit and QPushButton are overwritten and use semi-transparent backgrounds, too.
The whole UI should look like the following (This is a screenshot where I disabled OpenGL and used QWidget instead of QGLWidget for the background. Note the semi-transparent top bar which also draws a shadow (within its own region)):
When the QLineEdit has the focus, it should have a higher opacity but still not fully opaque:
So now, with OpenGL enabled (The background then is a QGLWidget), the semi-transparent widgets above don't paint on top of the background but on (it seems to be) uninitialized data. The image shining through the top bar is sometimes the whole window itself and sometimes other windows currently being on my desktop.
This currently looks like the following (In this screenshot, the data on which the semi-transparent painting operations are painted on seems to be an image of the widget itself, having an offset.):
When I wrote text into the line edit (here: "This is some text which has been there before!"), removed it and set the focus back to the background widget (so the magnifier icon and the placeholder text appear), the previously painted things still shine through (Note that the visible border should not be visible anymore, but also still shines through):
So the problem is: Instead of being painted on top of the underlying widgets, the semi-transparent widget is painted on top of the old results, initially being something like "uninitialized memory".
Why does this happen? How can I solve the problem?
Things you should know before answering:
The background scene is a composition of tiles which are rendered off-screen. So it can be painted very fast and repainting of the background for every little change of the overlay isn't problematic.
The top bar is a custom QWidget with manual painting and arrangement of the contained two widgets (the button and line edit).
The two widgets overwrite the paintEvent, only draw their own (semi-transparent) background when they have focus and don't use frames already provided by Qt. So the white border in the second screenshot is drawn in my custom paintEvent.
I want the background and the composition of overlay widgets to be separately implementable. The background is an AbstractMapView which has some concrete map view classes. The whole window is an AbstractView (there are multiple concrete views, too), which contains both a concrete map view and the overlay widgets, composed in a way itself decides. Therefore, I don't want the logic of the overlay widgets to be part of the underlying map view. (I hope you understood this, as it is a bit complicated.)
This sounds like an issue where the GL content (i.e. your background aka the QGLWidget) is not in the Qt context. While I'm not a pro on GL painting with Qt, you may want to look at this discussion regarding GL painting and a QLabel for some direction/potential hints.
http://www.qtcentre.org/threads/40335-QLabel-on-top-of-a-QGLWidget-background-issue
In short, we here at the office use OpenGL painting and offscreen rendering of maps and it's very important to make sure Qt is aware of the pixels so your foreground widgets can have the semi-transparency applied to their backgrounds.
The particular product we use also renders the map in tiles and supports providing the GL output in a buffer (i.e. it's call a snapshot and is provided as a bitmap) at which point we use the paintEvent of a regular QWidget to paint the buffer so that the painted pixels are in Qt context.
You can define a Qframe with Qt::SplashScreen flag as the search box and set its opacity. Put your widgets inside it such as the search textbox and positon it where it should appear on the mainwindow. It will also be a good idea to reposition it as the mainwindow is moved or resized overriding its moveEvent.

How to draw rounded corners with QPainter::drawPolyLine

I'm trying to create a custom container widget at the moment using QGroupBox as a base and drawing the new frame in the paint event, which is all working fine using drawPolyLine to create it, but I'd like to draw the frame with rounded corners. Has anyone come across a way to do it with drawPolyLine or would I need to rewrite my code to implement them?
When it comes to custom flexible shapes, QPainterPath is the most powerful class of them all. You could for example use QPainterPath::arcTo() in order to draw single rounded corners, though painting the full shape might require some math.
Another possibility is defining single shapes and merging them using intersected() or subtracted(), as already suggested by cbamber85 in the comments.
QPen has a "Cap Style" option of Qt::RoundCap which could result in rounded corners depending on pen width.

Qt mouse cursor transparency

I would like to change the stock cursor with a translucent one, a simple filled circle, of various sizes, depending on the level of zoom in the underlying widget (say, RGBA = 200, 200, 200, 128).
Is this at all possible with Qt? If not, is it a limitation in Qt or the underlying libs? Do you have suggestions as to how this could be accomplished by other means, e.g., hiding the cursor and overlaying a transparent pixmap at the cursor position (albeit slower)? TIA
QCursor can take a QPixmap which does support alpha channel. So I don't see why it can't be done.
I just figured this out for a project of my own. I did it with this code in the constructor of the relevant widget:
m_LPixmap = new QPixmap(32,32);
m_LPixmap->fill(Qt::transparent); // Otherwise you get a black background :(
QPainter painter(m_LPixmap);
QColor red(255,0,0,128);
painter.setPen(Qt::NoPen); // Otherwise you get an thin black border
painter.setBrush(red);
painter.drawEllipse(0,0,32,32);
m_Cursor = QCursor(*m_LPixmap);
setCursor(m_Cursor);

Resources