Drawing a scalable QIcon using QPainter - c++

I want to design a new QIcon and want it to look like a fixed text with a rounded rectangle around it
.-----.
| Phy |
`-----´
The icon is supposed to scale without the "pixel-blocks" effect when painting on a QPainter that eventually has a scale transformation applied (for example when I paint into a widget that is part of a QGraphicsView with a scale applied on its scene).
Therefor, I have difficulties knowing how I should paint my QIcon. If I do it in the following way, I will paint a QPixmap that always has a fixed amount of pixels, thus introducing the pixel-blocks effect inevitably when the scale is large enough
void MyWidget::drawIcon(QPainter *painter, QPoint pos) {
QPixmap pixmap = icon.pixmap(QSize(22, 22),
isEnabled() ? QIcon::Normal
: QIcon::Disabled,
isChecked() ? QIcon::On
: QIcon::Off);
painter->drawPixmap(pos, pixmap);
}
What I am looking for is a way similar to how QFont with drawText works. Regardless on how large my scale is, when I draw fonts it always looks sharp and I cannot detect individual pixels.
I imagine that I could tell QPainter to paint my icon into a given pixel rectangle, and QPainter transforms the rectangle itself before letting my QIconEngine::paint render the item into a possibly larger rectangle or pixmap. But I see no way how I could do something like this.
Am I just being stupid and not seeing the obvious solution?

I was indeed completely dump. I can just use QIcon::paint and pass it the rectangle. It will correctly delegate the request to the icon engine.

I do this by creating my icons/images as SVG files, and using QSvgRenderer to paint them onto a QPainter. The required classes are in the SVG module.

Related

Why QWidget::Mask based on QPixmap works fine, but Bitmap doesn't

Target: make my widget rounded, using QWidget::setMask
At first, I want to make a mask using QRegion, but later I found that, make a rounded Rect based on region is a not simple way.
So I decided to use QBitmap.
QBitmap pixmap(size());//create image
QPainter pixmapPainter(&pixmap);
QPainterPath path;
path.addRoundedRect(0, 0, width(), height(), m_borderRadius, m_borderRadius);//fill rounded rect
pixmapPainter.fillPath(path, Qt::color1);
QWidget::setMask(pixmap.mask());
Result is:
But when I change my type to QPixmap and add the fill method
QBitmap pixmap(size());//
pixmap.fill(Qt::transparent);//
QPainter pixmapPainter(&pixmap);
pixmapPainter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
path.addRoundedRect(0, 0, width(), height(), m_borderRadius, m_borderRadius);//
pixmapPainter.fillPath(path, Qt::white);
QWidget::setMask(pixmap.mask());
It shows me:
What's wrong with my first code?
As the comment by G.M. explains, the bitmap needs to be cleared first.
Also, a widget mask is not really the best choice here. A widget mask is intended for defining the areas where your widget will take mouse input. On most window systems, it will also cause transparency. But the edges will be aliased and not pretty. In order to make a non-rectangular widget with nice edges, one should use translucent widget background and anti-aliased painting - in addition to the widget mask for the mouse input.
This is shown and explained in Qt's shapedclock example.

Qt C++ Can QGraphicsItem's paint() method access underlying pixels?

For visual markers in an imaging application on top of images I would like to enhance the contrast of said markers by using a fill color with high contrast to the local background (e.g. inverted). This requires for the object to read its background in a QGraphicsScene.
Is there an efficient (built-in) way of doing this or does it require something like rendering the scene without the marker, reading pixels in its position and then paint() the marker accordingly?
There is no straight way to get the rendered color of some point in QGraphicsScene. You should actually render the scene and see the color. One workaround is to render the scene to a QImage and pick the color from desired pixel:
QImage image=QImage(width,height,QImage::Format_RGB32);
QPainter painter( &image );
painter.setRenderHint(QPainter::Antialiasing);
myScene->render( &painter, image.rect(),QRectF(0,0,width,height), Qt::KeepAspectRatio );
painter.end();
QColor color = QColor(image.pixel(x,y));

Drag a rectangle over image in Qt

I guess there are lots of ways to achieve this. I have an application in which a video stream is shown over a custom QWidget that I have subclasses from QLabel, and painting frames using QPainter. Given that, is it possible to let the user to drag a rectangle over the image and retrieve the coordinates? The requirement is that the rectangle must be visible during the dragging.
Thanks in advance,
Have a look at QRubberBand. It allows you to place such a rect on top of e.g. a QLabel. The documentation also contains an example how to move and resize the rubberband using the mouse.
the QGraphicsView has the void setRubberBandSelectionMode ( Qt::ItemSelectionMode mode ) but i dont know if the QLabel has some similar feature ...
maybe you have to draw your own rectangle while the user drags the rectangle and catch it on mouserelease
soo long zai
In you widget you could track mouse pressed and released events and track where on the widget the corners of the selection rect are. For drawing the rectangle, I'd take a look at QStyle::drawFocusRect. I think the intent of that is to draw a rect you'd be able to see regardless of what's behind it.
Or perhaps try this:
QStylePainter painter(this);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().color(QPalette::Background);
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);

How can I apply a dark mask layer on a QPixmap?

I am fairly new to Qt and have been doing a lot of reading and practicing exercises.
I have searched for this one quite a lot but I could not find any examples.
I have a QPixmap object to which I load a .png image.
I need to create a copy of this QPixmap with a dark mask applied to it.
Basically I want this QPixmap's image to be covered with a layer of solid black of which the opacity is set to 50%.
I know how to set the opacity of a QPixmap's image but how can I add a layer of solid black with opacity on it?
Thank you!
You can use a QPainter and you a semi-transparent QBrush to paint that dark layer onto your QPixmap.
Assuming pic is a QPixmap loaded with your image:
QPainter p(&pic);
QBrush b(QColor(0,0,0,128)); // adjust color and alpha to taste
p.setBrush(b);
p.drawRect(0, 0, 200, 200);
Effect (before/after):
   vs   
The opaque black border can be removed by setting an semi-transparent pen before painting.
Copy the pixmap before applying the "mask" if you want to preserve the original.

QGraphicsView not repainting on scroll event

QT 4.7
I have a QGraphicsView / QGraphicsScene. The scene has a custom QGraphicsItem the whole scene is not displayed at one time, so it has a viewport.
I'm overriding the paint() method of my QGraphicsItem as follows:
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->setClipRect(option->exposedRect);
painter->save();
// Default identity matrix
QTransform transform;
// Apply some transform later
// The next line breaks stuff
painter->setTransform(transform);
// m_image is just a preloaded pixmap (i.e. watermark on all of these items).
painter->drawImage(QPoint(0,0), this->m_image);
painter->restore();
// Snip, do more drawing...
}
If I ever try to setTransform on the QPainter (i.e. if I'm trying to rotate the item), the view stops repainting the scene as a response to the horizontal or vertical scrollbars used to pan. The view also stops resizing the scene when I zoom in or zoom out.
The view will refresh if I resize the window or drag the window offscreen and then back onscreen. I've been looking over the QPainter Documentation as well as the examples and I can't quite figure out what I'm doing wrong. I'm assuming it's something to do with the coordinate system.
A guess:
The QPainter that comes to your paint method already has a transform on it that takes into account the viewport attributes (scale, rotation, etc.). When you call setTransform within your paint method you are blowing all that away. You probably want to perform a matrix operation on the existing transform rather than creating a new one.
Since you are calling setClipRect on your painter but then trying to paint under a completely different transform, you are painting outside your clip rect and nothing is happening.
It works when you resize or drag the window off screen because that forces a "redraw everything," so your clip rect includes your painting area in your alternate transform. Although I'm surprised it would appear in the correct location.