How to use Qt QColormap in code? - qt

I want to achieve something like this .
I looked into the Qt QColormap but I did'nt get enough information to code it. If someone knows how to do this. Please share code snippet.

It's more question about color models then Qt really but basically you are trying to make a full circle around edge of HSL color model while keeping saturation.
To produce something like that in Qt you will utilize gradient brush; since we want continuous blend I used QLinearGradient. If you look at the color wheel above you will notice that red color is at 0 degrees, green is at 120 degrees and blue is at 240 degrees. QLinearGradient works with range from 0-1 so this will transform to 0, 1/3, 2/3 respectively. We also need need to add final stop which will complete the gradient back to red color.
I added bit of alpha channel to keep the color tone down so you can experiment with that; final code will look something like this:
class ColorScale : public QWidget {
Q_OBJECT
public:
using QWidget::QWidget;
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setOpacity(0.9);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
QLinearGradient gradient(0, 0, 0, height());
QGradientStops stops;
stops << QGradientStop(0, Qt::red);
stops << QGradientStop(1.0/3, Qt::blue);
stops << QGradientStop(2.0/3, Qt::green);
stops << QGradientStop(1, Qt::red);
gradient.setStops(stops);
painter.fillRect(rect(), gradient);
}
};
And it produces this:
You can add labels by calling QPainter::drawText.

Related

How do I add a border to a rounded QPixmap?

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.

Use Clipping in Qt

Is it possible to use clipping in an widgets painEvent, if the widget is using stylesheets?
The background and reason for my question is that I want to make the widget animating when it appears and disappears. (Something like a resizing circle or square, that gets bigger starting as a small area from the center).
My first (and only) thought on how to solve this, was to use the clipping of a QPainter, so that only the required area is drawn.
If I make the Background of the widget transparent and use the primitive drawing functions from QPainter it works fine. But how can I solve this, if the widget has a stylesheet applied? Is it even possible?
The used Qt version is Qt 4.8.6
My questions are:
Is it possible to achieve what I want with the mentioned strategy?
Is it possible in any way to clip all the children, too?
Is my strategy appropriate or is it a bad Idea to solve it that way?
Are there any other ideas, best practices, Qt Classes, ... that can give me what I want?
Additional Information
I haven't much code to show, because I stuck with this clipping things. But here is something to get an idea of what I have tried:
This works.
/* Shows a small red circle inside the widget as expected */
void MyAnimatingWidget::paintEvent(QPaintEvent *ev) {
QPainter painter(this);
QRect rect = this->geometry()
QStyleOption opt;
painter.setClipRegion(QRegion(rect.width()/2,
rect.height()/2,
150, 150,
QRegion::Ellipse));
painter.setPen(QColor(255, 0, 0));
painter.setBrush(QColor(255, 0, 0));
painter.setOpacity(1);
painter.drawRect(rect);
}
But the following doesn't change anything:
/* This shows the widget as usual */
void MyAnimatingWidget::paintEvent(QPaintEvent *ev) {
QPainter painter(this);
QRect rect = this->geometry();
QStyleOption opt;
painter.setClipRegion(QRegion(rect.width()/2,
rect.height()/2,
150, 150,
QRegion::Ellipse));
painter.setRenderHint(QPainter::Antialiasing);
painter.setOpacity(1);
opt.init(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
}
Moreover I have noticed, that the stylesheet is also drawn, even if I remove the style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); line at all.
The stylesheet you apply to your widget overrides the OS-specific style(s) widgets are equipped with by default. This can even cause problems, if you want to have a, say, Windows look, but still want to use a stylesheet. Anyway, you can check what each style does in the Qt source directory: src/gui/styles. For style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);, the code reads:
case PE_Widget:
if (w && !rule.hasDrawable()) {
QWidget *container = containerWidget(w);
if (styleSheetCaches->autoFillDisabledWidgets.contains(container)
&& (container == w || !renderRule(container, opt).hasBackground())) {
//we do not have a background, but we disabled the autofillbackground anyway. so fill the background now.
// (this may happen if we have rules like :focus)
p->fillRect(opt->rect, opt->palette.brush(w->backgroundRole()));
}
break;
}
As you can see clipping is not meddled with in any way, so your idea of setting a clip region should work. Now for the painting mystery. The painting of the background happens in void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int flags) const, which is called from void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, QPainter *sharedPainter, QWidgetBackingStore *backingStore). You can find the code in: /src/gui/kernel/qwidget.cpp. The relevant code reads:
if (q->testAttribute(Qt::WA_StyledBackground)) {
painter->setClipRegion(rgn);
QStyleOption opt;
opt.initFrom(q);
q->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, q);
}
Maybe turning the attribute off would help? The basic lesson you should draw from my answer is to get accustomed to source diving. The idea behind Qt is nice (instantiating controls, without bothering about implementation details), but it rarely works in practice, i.e. you often need to source dive.
To clip widget's children to arbitrary clip regions, you can capture them into a pixmap, example:
QPixmap pixmap(widget->size());
widget->render(&pixmap);
And then draw the pixmap manually. You might also be able to prevent them repainting automatically (via setUpdatesEnabled() or by hiding them) and then calling their render in you paintEvent handler manually.

QT change QImage alpha

I need to change the alpha of a QImage I have so it blends with the other QImages behind and infront of it. This needs to be toggled quickly on and off.
Previously I've had to recreate every single image and give them new colors with just a different alpha value. But now I want to retain that same original image instead of redrawing and painting it.
I'm trying to do it like this now:
QImage image;
unsigned int rgb;
for(int y=0;y<image.height();y++){
for(int x=0;x<image.width();x++){
rgb=image.pixel(x,y);
image.setPixel(x,y,qRgba(qRed(rgb),qGreen(rgb),qRed(rgb),120));
}
}
I'm getting some fairly unpredictable behavior. When I toggle the image sometimes I lose colors or the alpha isn't changed. And if the alpha did get changed when I toggle back (I hardcode the alpha 255 elsewhere instead of 120) it doesn't go back to normal.
This doesn't seem like the right way to do this anyways, it shouldn't be this difficult. It seems like there should be a single function call on an image to change the alpha but I haven't found one yet.
If you are using the QImage in QGraphicsView or in some other QWidget, you should look into this QGraphicsEffect:
http://qt-project.org/doc/qt-4.8/qgraphicsopacityeffect.html
http://doc-snapshot.qt-project.org/4.8/qwidget.html#setGraphicsEffect
http://doc-snapshot.qt-project.org/4.8/qgraphicsitem.html#setGraphicsEffect
If you are using a QLabel, I would try this out:
#include <QLabel>
#include <QPainter>
class TransparentQLabel : public QLabel
{
Q_OBJECT
public:
explicit TransparentQLabel() : QLabel() {}
~TransparentQLabel(){}
void setOpacity(const qreal & val)
{
if (this->pixmap() == null || this->pixmap().isNull())
return;
QPixmap result(this->pixmap()->size());
result.fill(Qt::transparent);
QPainter painter;
painter.begin(&result);
painter.setOpacity(val);
painter.drawPixmap(0, 0, *(this->pixmap()));
painter.end();
QLabel::setPixmap(result);
}
};
This next bit is only slightly related to your question, but it is nice to know. If you are layering outside of your QApplication onto the operating system, you need some things like this:
this->setWindowFlags( Qt::WindowStaysOnTopHint |
Qt::FramelessWindowHint | Qt::Tool);
this->setAttribute(Qt::WA_TranslucentBackground, true);
this->setAttribute (Qt::WA_TransparentForMouseEvents, true);
Here is an example of this stuff in action:
http://qt-project.org/wiki/QSplashScreen-Replacement-for-Semitransparent-Images
Hope that helps.

QLinearGradient doesn't work properly with QBrush

I want to draw 1 digit on the screen by the graphic framework classes. I want the fill approach of '1' to be something like
(source: qt-project.org)
but the brush of my drawn '1' is just like a yellow SolidBrush by the below code (an ugly bold yellow '1'). Can you help me what's wrong with it?
QGraphicsSimpleTextItem digit_1 = new QGraphicsSimpleTextItem;
digit_1->setText(QString::number(1));
digit_1->setPen(QPen(QColor("black")));
QLinearGradient gradient(digit_1->boundingRect().topLeft(),
digit_1->boundingRect().bottomRight());
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, Qt::yellow); // yellow is for example
QBrush brush(gradient);
brush.setStyle(Qt::BrushStyle::LinearGradientPattern);
digit_1->setBrush(brush);
digit_1->setFont(QFont("courier", 35, QFont::Black));
Thanks in advanced.
Your issue most likely comes from the fact that you're basing your gradient's "area" on the bounding rect of your item before you set the font size to something much larger than the default.
The bounding rect you're getting is thus much smaller than your actual bounding rect. Since the default spread method is padding, you're seeing most likely just one color (or not enough of the gradient for it to be actually visible).
So move your setFont call to the top, before you create the gradient. You can drop the setStyle on your brush, that's determined automatically from the gradient. (In fact, you can drop that brush entirely and use the gradient in setBrush.)
With the way you set up the gradient, you'll get a "diagonal" gradient. If you want it from top to bottom, use the top left and bottom left points instead.
Demo
#include <QtGui>
class W: public QGraphicsView
{
Q_OBJECT
public:
W(QWidget *parent = 0)
: QGraphicsView(parent)
{
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem;
item->setText("Stack Overflow");
item->setPen(QPen(Qt::red));
item->setFont(QFont("courier", 60, QFont::Bold));
QLinearGradient lgrad(item->boundingRect().topLeft(),
item->boundingRect().bottomLeft());
lgrad.setColorAt(0.0, Qt::red);
lgrad.setColorAt(1.0, Qt::yellow);
item->setBrush(lgrad);
QGraphicsScene *scene = new QGraphicsScene;
scene->setBackgroundBrush(QBrush(Qt::black));
scene->addItem(item);
setScene(scene);
}
};

Draw a multi-colored line in Qt

What I'm trying to achieve is the following: I have a QGraphicsScene with a QGraphicsPixmapItem shown in it. The pixmap has multiple colors and I need to draw a line across the pixmap that must be visible and recognizable in every single point.
My idea is to draw a line where every pixel has the negative (complementary) color of the pixmap's relative pixel. So I thought about subclassing QGraphicsItem and reimplement the paint() method to draw a multi-colored line.
However I'm stuck because I don't know how I can retrieve the pixel information of the pixmap from the paint function, and even if I found out, I can't think of a way to draw the line in this way.
Could you give me some advice on how to proceed?
You can use QPainter's compositionMode property to do something like this pretty easily, without having to read the source pixel colors.
Simple sample QWidget with a custom paintEvent implementation, which you should be able to adapt to your item's paint method:
#include <QtGui>
class W: public QWidget {
Q_OBJECT
public:
W(QWidget *parent = 0): QWidget(parent) {};
protected:
void paintEvent(QPaintEvent *) {
QPainter p(this);
// Draw boring background
p.setPen(Qt::NoPen);
p.setBrush(QColor(0,255,0));
p.drawRect(0, 0, 30, 90);
p.setBrush(QColor(255,0,0));
p.drawRect(30, 0, 30, 90);
p.setBrush(QColor(0,0,255));
p.drawRect(60, 0, 30, 90);
// This is the important part you'll want to play with
p.setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
QPen inverter(Qt::white);
inverter.setWidth(10);
p.setPen(inverter);
p.drawLine(0, 0, 90, 90);
}
};
This will output something like the following image:
Experiment with the other composition modes to get more interesting effects.

Resources