I want to paint the background of a slider. I tried this but the color covers up the whole slider. This is in an inherited class of QSlider
void paintEvent(QPaintEvent *e) {
QPainter painter(this);
painter.begin(this);
painter.setBrush(/*not important*/);
// This covers up the control. How do I make it so the color is in
// the background and the control is still visible?
painter.drawRect(rect());
painter.end();
}
To set the background of a widget you could set the style sheet:
theSlider->setStyleSheet("QSlider { background-color: green; }");
The following will set the background of the widget, allowing you to do more:
void paintEvent(QPaintEvent *event) {
QPainter painter;
painter.begin(this);
painter.fillRect(rect(), /* brush, brush style or color */);
painter.end();
// This is very important if you don't want to handle _every_
// detail about painting this particular widget. Without this
// the control would just be red, if that was the brush used,
// for instance.
QSlider::paintEvent(event);
}
And btw. the following two lines of your sample code will yield a warning:
QPainter painter(this);
painter.begin(this);
Namely this one using GCC:
QPainter::begin: A paint device can only be painted by one painter at
a time.
So make sure, as I do in my example, that you either do QPainter painter(this) or painter.begin(this).
Related
I have an issue with pixmaps created for drag events. For drag events of my derived QGraphicsRectItem I create a semi-transparent pixmap from that item.
In the debug build everything looks fine.
But in the release build the drag pixmap has some periodic and random artefacts
here is the code:
QPixmap MyGraphicsRectItem::toPixmap() const
{
QRect r = boundingRect().toRect();
QPixmap pixmap(r.width(), r.height());
QColor dragColor(color);
dragColor.setAlphaF(0.5);
QPainter painter;
painter.begin(&pixmap);
painter.fillRect(pixmap.rect(), dragColor);
painter.setPen(Qt::white);
QFont font("SegoeUI");
font.setBold(true);
painter.setFont(font);
painter.drawText(pixmap.rect(), QString(" ") + textItem->toPlainText());
if (pixItem != nullptr) {
painter.setOpacity(0.5);
painter.drawPixmap(pixItem->pos(), pixItem->pixmap());
}
painter.end();
return pixmap;
}
Could that be a kind of memory issue?
The QPixmap is initialized with uninitialized data. In Debug, this is often set to a fixed pattern, but in Release it is garbage.
You should fill the pixmap with transparent color before using it.
QPixmap::QPixmap(int width, int height)
Constructs a pixmap with the given width and height. If either width or height is zero, a null pixmap is constructed.
Warning: This will create a QPixmap with uninitialized data. Call fill() to fill the pixmap with an appropriate color before drawing onto it with QPainter.
(From Qt Docs)
I'm trying to create the following custom button:
To do this I've create the class and overrided paintEvent:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen(Qt::darkGray, 7, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen);
painter.drawEllipse(QPointF(width()/2, height()/2), width()/2.1,height()/2.1);
QPen pen2(Qt::lightGray, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen2);
painter.drawEllipse(QPointF(width()/2, height()/2), width()/2.15, height()/2.15);
QPen pen1(Qt::gray, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen1);
painter.drawEllipse(QPointF(width()/2, height()/2), width()/3.5, height()/3.5);
}
But, I'm not able to create the button like below with appropriate gradients and picture in the center.
Can you please help me?
The key thing here is to make the gradients. And you cannot make gradients on strokes, only on fills. Meaning that you will have to implement the outline as a fill.
There is the button's components digested, listed in the order you should draw them:
Mind you that only the first component is a solid color, everything else is gradients. It is those gradients which get the effect.
In order to get gradients for the inner ring you will have to use QPainterPath, fist add the outer circle, and then add a slightly smaller inner circle, which will effectively punch a hole in the first circle, giving you something that looks like an outline but is actually a fill, a fill you can use a gradient on.
As mentioned in the comments - this involves a lot of operations and is not ideal. It would be preferable to have such buttons as images instead of painting them with QPainter.
Update: here is some more help, to show you how to draw a gradient outline in order to achieve some 3D illusion:
class Test : public QWidget {
Q_OBJECT
public:
Test() { resize(200, 200); }
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(rect(), Qt::gray);
QPainterPath p;
p.addEllipse(rect().center(), 50, 50);
p.addEllipse(rect().center(), 45, 45);
QLinearGradient g(QPointF(0, 50), QPointF(0, 150));
g.setColorAt(0, Qt::white);
g.setColorAt(1, Qt::black);
QBrush b(g);
painter.fillPath(p, b);
QPainterPath p2;
p2.addEllipse(rect().center(), 45, 45);
p2.addEllipse(rect().center(), 40, 40);
QLinearGradient g1(QPointF(0, 50), QPointF(0, 150));
g1.setColorAt(0, Qt::black);
g1.setColorAt(1, Qt::white);
QBrush b1(g1);
painter.fillPath(p2, b1);
QPainterPath p3;
p3.addEllipse(rect().center(), 40, 40);
painter.fillPath(p3, b);
}
};
And the result:
Considering the question reworded as:
How to use QPainter to generate the given picture?
Using QPainter:
Get a vectorial image of the button, ideally in SVG.
Look the SVG code to extract colors, gradients, sizes, etc.
Reproduce them with QPainter, you will see that for each SVG command, you have an nearly equal equivalent in QPainter. QPainterPath will help.
Using QPixmap:
Get an SVG image of your button
Use QSvgRenderer to generate a QImage of the given size, and then convert it to a QPixmap.
Paint the QPixmap when required.
Benefits of each options:
QPixmap is simpler, and allows you to switch the button aesthetic easily.
QSvgRenderer allows you to manage hover easily
QPainter allows you to override colors with those of the OS
QPainter give you more flexibility for effects, animations, etc.
am I using drawPixmap() correctly?
Essentially my goal is to take an tileset image, and replace an individual tile with a custom tile image.
I'm able to get both images to load on the screen, but when I call drawPixmap(), then original image doesn't change at all.
Thanks in advance.
void replaceCustomTile(QPixmap custom, QPixmap target, int whichTile) {
QRect rect(0, 0 + (squareTileSize * whichTile), squareTileSize, squareTileSize);
QRect customRect = custom.rect();
QPainter painter(this);
painter.drawPixmap(rect, target, customRect);
painter.end();
}
EDIT:
This is how replaceCustomTile is called:
QPixmap terrainTiles(":/static/Terrain.png");
QPixmap customTile(":/static/Smiles.png");
replaceCustomTile(customTile, terrainTiles, 0);
To intialize QPainter by this it must be called from the widget paintEvent(QPaintEvent *) if you want to draw it on some widget. So, replaceCustomTile() should be called from the event handler in that case.
To draw some pixmap on top of another pixmap QPainter should be initialized by the target pixmap using QPainter::begin():
QPainter painter;
painter.begin(&target);
painter.drawPixmap(rect, custom);
painter.end();
The above code draws QPixmap custom into given QRect rect over QPixmap target. The target is modified.
How to change opacity of QPixmap?
I've set an image as background actually I want to change its opacity, Here is my code:
Call.h:
private:
QPixmap m_avatar;
Call.cpp:
void Call::resizeEvent(QResizeEvent *e)
{
QPalette pal = palette();
pal.setBrush(backgroundRole(), m_avatar.scaled(e->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
setPalette(pal);
}
I've changed resizeEvent function, but it doesn't change background's opacity.
void Call::resizeEvent(QResizeEvent *e)
{
QPixmap result_avatar(m_avatar.size());
result_avatar.fill(Qt::transparent);
QPainter painter;
painter.setOpacity(0.5);
painter.begin(&result_avatar);
painter.drawPixmap(0, 0, m_avatar);
painter.end();
QPalette pal = palette();
pal.setBrush(backgroundRole(), result_avatar.scaled(e->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
setPalette(pal);
}
Any suggestion?
You are not using local QPainter object. According to QWidget Events:
paintEvent() is called whenever the widget needs to be repainted.
Every widget displaying custom content must implement it. Painting
using a QPainter can only take place in a paintEvent() or a function
called by a paintEvent().
Here it works:
void Call::paintEvent(QPaintEvent *)
{
// create a new object scaled to widget size
QPixmap result_avatar = m_avatar.scaled(size());
QPainter painter(this);
painter.setOpacity(0.5);
// use scaled image or if needed not scaled m_avatar
painter.drawPixmap(0, 0, result_avatar);
}
Update for paiting on pixmap case
If it is needed only to paint with some opacity on a pixmap using QPainter, the opacity must be set only after QPainter activation by QPainter::begin(). So, after changing the order the pixmap result_avatar has two images (one resized with opacity 1 and original pixmap on top with opacity 0.5):
QPainter painter;
painter.begin(&result_avatar);
painter.setOpacity(0.5);
painter.drawPixmap(0, 0, m_avatar);
painter.end()
I can get a border to display on my QLabels just fine:
But when I try to display a pixmap in them, the border goes away:
I set the frame properties in the constructor of my QLabel subclass:
ObjectSlot::ObjectSlot(int index) {
setIndex(index);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Raised);
setLineWidth(3);
setMidLineWidth(3);
setAlignment(Qt::AlignCenter);
return;
}
The pixmap is set in the paintEvent:
void ObjectSlot::paintEvent(QPaintEvent* event) {
QPixmap* image = new QPixmap("://images/Box.png");
setPixmap(image->scaled(width(),height(),Qt::KeepAspectRatio));
QLabel::paintEvent(event);
}
Why does the border go away? Why is life so cruel?
As doc said:
Setting the pixmap clears any previous content. The buddy shortcut, if
any, is disabled.
So it seems that it is impossible, but I found next solution, you shouldn't setPixmap(), you need just drawPixmap() when all correct label was painted:
void ObjectSlot::paintEvent(QPaintEvent *e)
{
QLabel::paintEvent(e);
//label painted
QPainter p(this);
QPixmap image("G:/2/qt.png");
p.drawPixmap(QPoint(1,1),image.scaled(100,100,Qt::KeepAspectRatio));
}
Result:
Not the best solution because you should adapt it to your purposes, but currently better than nothing.