I need to do something similar to QPainter::drawImage, but drawing a triangle part of the given picture (into a triangular region of my widget) instead of working with rectangles.
Any idea how I could do that, besides painfully trying to redraw every pixel?
Thanks for your insights!
If it is feasible for you to use a QPixmap instead of a QImage, you can set a bitmap mask for the QPixmap which defines which of the pixels are shown and which are transparent:
myPixmap->setMask(myTriangleMask);
painter->drawPixmap(myPixmap);
Here is another solution based on QImage:
MaskWidget::MaskWidget(QWidget* parent) : QWidget(parent) {
img = QImage("Sample.jpg"); // The image to paint
mask = QImage("Mask.png"); // An indexed 2-bit colormap image
QPainter imgPainter(&img);
imgPainter.drawImage(0, 0, mask); // Paint the mask onto the image
}
void MaskWidget::paintEvent ( QPaintEvent * event ) {
QPainter painter(this);
painter.drawImage(10, 10, img);
}
Mask.png is an image file with the same size as Sample.jpg. It contains an alpha channel to support transparency. You can create this file easily with The GIMP, for example. I added an alpha channel, changed all areas I want to have painted to transparent and all other areas to white. To reduce the size, I finally converted it to an indexed 2-bit image.
You could even create the mask image programmatically with Qt, if you need your triangle be computed based on various parameters.
Related
I'm trying to show a rounded avatar QPixMap with a white border around it. However, I have no clue as to how I could add the border... Is it even possible?
This is the function I have to draw the avatar image.
void AccountDropDownMenu::setAvatar(
const QByteArray& bytes)
{
QPixmap new_avatar;
new_avatar.loadFromData(bytes);
new_avatar = new_avatar.scaledToHeight(40, Qt::SmoothTransformation);
QBitmap map(new_avatar.size());
map.fill(Qt::color0);
QPainter painter(&map);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::color1);
painter.setPen(QPen(Qt::white, 10));
painter.drawRoundedRect(
m_avatar_label->x(),
m_avatar_label->y(),
new_avatar.width(),
new_avatar.height(),
45,
45);
new_avatar.setMask(map);
avatar_label->setPixmap(new_avatar);
}
Update
Thanks to dtech I was able to get the desired output using the following updated function.... Although it's a bit pixly (the border).
void AccountDropDownMenu::setAvatar(
const QByteArray& bytes)
{
QPixmap new_avatar;
new_avatar.loadFromData(bytes);
new_avatar = new_avatar.scaledToHeight(40, Qt::SmoothTransformation);
QBitmap map(new_avatar.size());
map.fill(Qt::color0);
QPainter painter(&map);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::color1);
painter.drawRoundedRect(
QRectF(
avatar_label->x(),
avatar_label->y(),
new_avatar.width(),
new_avatar.height()),
40,
40);
new_avatar.setMask(map);
QPainter painter2(&new_avatar);
painter2.setRenderHint(QPainter::Antialiasing);
painter2.setPen(QPen(Qt::white, 1));
painter2.drawRoundedRect(
QRectF(
avatar_label->x(),
avatar_label->y(),
new_avatar.width(),
new_avatar.height()),
40,
40);
avatar_label->setPixmap(new_avatar);
}
In Qt you draw fills with a brush, but outlines are drawn with a QPen.
I haven't used QPainter in a long time, but IIRC, the default pen is zero width, which would explain why you aren't getting anything - you are not setting a pen.
Also, you don't need "another" rounded rectangle, you can apply fill and outline to the same geometry, as demonstrated in this answer.
Update:
Your updated code sets a mask, which sets an alpha channel. That cuts away from what you already have, it could not possibly add anything. You have to paint on the pixmap. Simply use new_avatar as the paint device - QPainter painter(&new_avatar); and get rid of the rest.
When I rotate an image in Qt, It automatically enlarge the image to encapsulate the bigger dimensions which is great and intelligent. However, it fills the newly created void with white color which is not what I want. How can I make it fill with a totally transparent color?
Here is may code.
QImage lSource("/path/to/an/image/file");
QTransform lRotation;
lRotation.rotate(30.0);
QImage lRotated(lSource.transformed(lRotation, Qt::SmoothTransformation));
ui.labelImage->setPixmap(QPixmap::fromImage(lRotated));
Thanks a lot!
Based on my comment above...
I suspect, that the void areas of the rotated image are transparent, and the white color is the background color of your label. If you want to set a specific background to your rotated image, you can do the following:
// Create rotated image.
QImage lSource("source.png");
QTransform lRotation;
lRotation.rotate(30.0);
QImage lRotated(lSource.transformed(lRotation, Qt::SmoothTransformation));
// Create resulting image with specific background.
QImage img(lRotated.size(), QImage::Format_ARGB32);
img.fill(Qt::red); // Set the red background
QPainter p(&img);
p.drawImage(0, 0, lRotated);
QLabel l;
l.setPixmap(QPixmap::fromImage(img));
l.show();
How to highlight the pixels in a QImage to highlight or draw a overlay on the pixels which user selected using mouse. I just want to know how i can specify the particular area.
Ex: With a 400x400 QImage data how i can increase or decrease the pixel intensity or overlap an image on top of it where the user selected.
You can involve QPainter (please refer to the documentation) to draw over your QImage. It allows drawing another images, rectangles, lines etc.
void View::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// Draws your original image.
painter.drawImage(0, 0, myImage);
// Draws a blue rectangle over the image.
QPen rectPen(Qt::blue);
rectPen.setStyle(Qt::DashLine);
painter.setPen(rectPen);
painter.drawRect(0, 0, 100, 100);
[..]
}
You can maintain your mouse clicks and movement and draw corresponding stuff in paint event handler.
I have a QGraphicsScene with rather small point markers. I would like to enlarge the area of these markers to make dragging easier. The marker is a cross, +/- 2 pixels from the origin. I have reimplemented
QGraphicsItem::contains(const QPointF & point ) const
{
return QRectF(-10,-10,20,20);
}
and
void hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
setPen(QPen(Qt::red));
update();
}
but the marker only turns red when it is directly hit by the cursor (and even that is a bit picky). How can I enlarge the "hover area"?
As stated in the short comment:
Usually those things are handled via the bounding rect or the shape function, try overloading those. Take a look into the qt help of QGraphicsItem under shape (http://doc.qt.io/qt-4.8/qgraphicsitem.html#shape):
Returns the shape of this item as a QPainterPath in local coordinates.
The shape is used for many things, including collision detection, hit
tests, and for the QGraphicsScene::items() functions.
The default implementation calls boundingRect() to return a simple
rectangular shape, but subclasses can reimplement this function to
return a more accurate shape for non-rectangular items. For example, a
round item may choose to return an elliptic shape for better collision
detection. For example:
QPainterPath RoundItem::shape() const {
QPainterPath path;
path.addEllipse(boundingRect());
return path; } The outline of a shape can vary depending on the width and style of the pen used when drawing. If you want to include
this outline in the item's shape, you can create a shape from the
stroke using QPainterPathStroker.
This function is called by the default implementations of contains()
and collidesWithPath().
So what basically happens is that all functions that want to access the "Zone" which is associated with an item, call shape and then do e.g. a containment or collision detection with the resulting painterpath.
Thus if you have small items you should enlargen the shape zone.
Lets for instance consider a line that is your target, than your shape implementation could look like the following:
QPainterPath Segment::shape() const{
QLineF temp(qLineF(scaled(Plotable::cScaleFactor)));
QPolygonF poly;
temp.translate(0,pen.widthF()/2.0);
poly.push_back(temp.p1());
poly.push_back(temp.p2());
temp.translate(0,-pen.widthF());
poly.push_back(temp.p2());
poly.push_back(temp.p1());
QPainterPath path;
path.addPolygon(poly);
return path;
}
Pen is a member of the segment, and I use its width to enlarge the shape zone. But you can take anything else as well that has a good relation to the actual dimension of your object.
I am rendering a QPixmap inside of a QThread. the code to paint is inside a function. If I declare the painter inside the drawChart function everything seems ok but if I declare the painter inside the run function the image is wrong in the sense that at the edge of a black and white area, the pixels at the interface are overlapped to give a grey. Does anyone know why this is so? Could it be because of the nature of the run function itself?
//This is ok
void RenderThread::run()
{
QImage image(resultSize, QImage::Format_RGB32);
drawChart(&image);
emit renderedImage(image, scaleFactor);
}
drawChart(&image)
{
QPainter painter(image);
painter.doStuff()(;
...
}
//This gives a image that seems to have artifacts
void RenderThread::run()
{
QImage image(resultSize, QImage::Format_RGB32);
QPainter painter(image);
drawChart(painter);
emit renderedImage(image, scaleFactor);
}
drawChart(&painter)
{
painter.doStuff();
...
}
//bad
.
//good
.
From C++ GUI Programming with Qt 4 by Jasmin Blanchette and Mark Summerfield:
One important thing to understand is
that the center of a pixel lies on
“half-pixel” coordinates. For example,
the top-left pixel covers the area
between points (0, 0) and (1, 1), and
its center is located at (0.5, 0.5).
If we ask QPainter to draw a pixel at,
say, (100, 100), it will approximate
the result by shifting the coordinate
by +0.5 in both directions, resulting
in the pixel centered at (100.5,
100.5) being drawn.
This distinction may seem rather
academic at first, but it has
important consequences in practice.
First, the shifting by +0.5 only
occurs if antialiasing is disabled
(the default); if antialiasing is
enabled and we try to draw a pixel at
(100, 100) in black, QPainter will
actually color the four pixels (99.5,
99.5), (99.5, 100.5), (100.5, 99.5), and (100.5, 100.5) light gray, to give
the impression of a pixel lying
exactly at the meeting point of the
four pixels. If this effect is
undesirable, we can avoid it by
specifying half-pixel coordinates, for
example, (100.5, 100.5).