Qt text with shadow - qt

I see this in other applications, even though the appearance is ever so slightly, the effect is a much high contrast -> better readability.
The tabs on the left in Qt Designer for example, or the tabs at the top in Adobe Photoshop: the text has some sort of shadow, only ever 1 pixel surrounding the text with a contrasting colour.
Is there a simple way to do this with Qt? Or a more complex one?
Thank you.

Maybe QGraphicsDropShadowEffect?

There are a couple ways of achieving this effect, but conceptually you need to look at it as being two text layers with a slight offset.
I have done this before by re-implementing the paintEvent() method of a QWidget and drawing the text layers myself. Or you can reimplement the drawItemText() method of a custom QStyle. But basically that is how it is done.

Here is the way I did text shadow on all buttons with Qt5. I am not sure if this is possible with Qt4.
class MyProxyStyle : public QProxyStyle
{
public:
void drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole /* = QPalette::NoRole */) const
{
if (textRole == QPalette::ButtonText && dynamic_cast<QAbstractButton*>(painter->device()))
{
QPalette palShadow(pal);
palShadow.setColor(QPalette::ButtonText, QColor(0, 0, 0, 100));
QProxyStyle::drawItemText(painter, rect.adjusted(1, 1, 1, 1), flags, palShadow, enabled, text, textRole);
}
QProxyStyle::drawItemText(painter, rect, flags, pal, enabled, text, textRole);
}
};
...somewhere in main()
QApplication a;
a.setStyle(new MyProxyStyle);
If you remove the QAbstractButton dynamic_cast the menu titles will also be shadowed which is not always desirable.

Related

Drawing rectangle line-by-line doesn't have the same result as drawing it directly

I'm trying to customize the borders of the cells in a (custom) QTableView. To do that, I found this:
class MyDelegate : public QItemDelegate {
public:
MyDelegate( QObject *parent ) : QItemDelegate( parent ) { }
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const {
QItemDelegate::paint( painter, option, index );
if( /* some condition */ ) {
painter->setPen( Qt::red );
painter->drawRect( option.rect );
}
}
}
This works well. I added this in my delegate - which already has a custom paint method for other things and it does exactly what I want:
Neat.
Things start to go rogue when I want to decide what borders I actually draw. To do this, each item has a BorderOption property, which describes what borders should be draw. And my grid isn't 1px large anymore.
So what I decided to try out was the following:
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
Which should have the same result, right? Right? Well... no.
It seems like two 1px-width lines are being drawn, one for each cell.
That could make sense, if the cells' rectangles didn't overlap. But we've seen before it is not the case, as the first example worked and produced rectangles of width 1px.
Am I doing something wrong?
Thanks
I think there is a misconception on the meaning of option.rect. It is really the rectangle of the item of the table. There is still a portion, a one pixel wide line, that does not belong to the option.rect of the item, but to the QTreeView itself.
This becomes more visible if you restrict the drawing to the allowed item portion by setting setClipRect and by making the lines distinguishable using different colors.
Try to experiment with the width of the drawn line and by enabling and disabling the clip rect. Maybe you need to draw outside of the allowed option.rec, but be aware that you have to consider also different drawings of QTreeView on different platforms, where the separating lines might be 0px wide or wider than just one pixel.
#pragma once
#include <QStyledItemDelegate>
#include <QItemSelectionModel>
#include <QPainter>
#include <QDebug>
class MyDelegate : public QStyledItemDelegate {
public:
MyDelegate(QObject* parent=nullptr) : QStyledItemDelegate(parent) { }
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
if (true) {
painter->save();
auto color=QColor::fromHsv((30*index.row() + 30 * index.column()) % 255, 255, 127);
painter->setPen(QPen(color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin));
painter->setClipRect(option.rect); // Rectangle of the item
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
painter->restore();
}
}
};
Did you tried to set thickness of the pen manually?
painter->setPen(QPen(QColor(255, 0, 0), 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin));
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
Here, I set the thickness to the 3px for example!

QTableView: align the icon of the item to top left

I need to draw an icon on the items of a QTableView.
What I'm getting now is the following (each big rectangle is an item):
with the following code in the method
QVariant myClass::data(const QModelIndex& index, int role) const
...
case Qt::DecorationRole:
{
QPixmap pix(m_cellSize, m_cellSize);
QPainter painter( &pix );
painter.setFont( QFont("Arial", m_cellSize / 2) );
painter.setPen(Qt::black);
painter.drawRect(0, 0, m_cellSize - 1, m_cellSize - 1);
painter.drawText( QRect(0, 0, m_cellSize, m_cellSize), Qt::AlignTop, QString::number(m_letters[index.row()][index.column()].number) );
QIcon icon(pix);
return icon;
}
...
I do a drawRect only to see how the pixmap is and where the text is inside it. I can align the text in the pixmap but I cannot align the icon inside the item.
In few words, I need to draw this icon on the top left corner of the table view item but I don't know how to. It's always vertically aligned.
Any help would be appreciated.
You can investigate if changing Qt::TextAlignmentRole makes a difference, but my guess is it won't. The usual way to solve this is to implement a custom delegate, i.e subclass QStyledItemDelegate, and override the paint method. I suspect this might also give better performance, since you could do your custom drawing there, and return you model data in Qt::DisplayRole as a QString instead.
I'm making a few assumptions in this, but if you look at the examples of custom item delegates I think you will see something very close to what you need.

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.

Qt Color Picker Widget?

I have a QDialog subclass that presents some options to the user for their selecting. One of these options is a color. I have seen the QColorDialog, and I need something much simpler, that is also a regular widget so I can add to my layout as part of my dialog. Does Qt offer anything like this or will I have to make my own? If the latter, what is the best strategy?
Have you looked at the QtColorPicker, part of Qt Solutions?
QtColorPicker provides a small widget in the form of a QComboBox with a customizable set of predefined colors for easy and fast access. Clicking the ... button will open the QColorDialog. It's licensed under LGPL so with dynamic linking and proper attribution it can be used in commercial software. Search for QtColorPicker and you'll find it. Here's a link to one site that hosts many of the Qt Solutions components:
https://github.com/pothosware/PothosFlow/tree/master/qtcolorpicker
There's a very easy way to implement that using a QPushButton to display the current color and pickup one when it is clicked:
Definition:
#include <QPushButton>
#include <QColor>
class SelectColorButton : public QPushButton
{
Q_OBJECT
public:
SelectColorButton( QWidget* parent );
void setColor( const QColor& color );
const QColor& getColor() const;
public slots:
void updateColor();
void changeColor();
private:
QColor color;
};
Implementation:
#include <QColorDialog>
SelectColorButton::SelectColorButton( QWidget* parent )
: QPushButton(parent)
{
connect( this, SIGNAL(clicked()), this, SLOT(changeColor()) );
}
void SelectColorButton::updateColor()
{
setStyleSheet( "background-color: " + color.name() );
}
void SelectColorButton::changeColor()
{
QColor newColor = QColorDialog::getColor(color, parentWidget());
if ( newColor != color )
{
setColor( newColor );
}
}
void SelectColorButton::setColor( const QColor& color )
{
this->color = color;
updateColor();
}
const QColor& SelectColorButton::getColor() const
{
return color;
}
Qt doesn't offer anything simpler than QColorDialog natively, but there are several color picking widgets as part of wwWidgets, a user made set of widgets for Qt (note that this is "wwWidgets" with a "w" and not "wxWidgets" with an "x").
I think QColorDialog is best suited for your application. If you want to go for something simpler, it will come with reduced functionality. I'm not aware of any standard widget in Qt offering such an option but you can try out the following:
QCombobox with each entry corresponding to a different color. You can maybe even have the colors of the names in their actual color.
One or more slider bars to adjust the hue, saturation, val or R,G,B components.
QLineEdit fields for individual R,G,B components. You can also have a signal / slot mechanism wherein once the user changes a color, the color shown to the user gets changed accordingly.
You can have '+' and '-' signs to increase / decrease the above color component values.
I hope the above gives you some ideas.

Resources