I've currently got this style for all push buttons:
this->setStyleSheet(".QPushButton { background-color: #416eb6; color: #ddd; }");
This is great because it allows me to keep a constant styling for all the QPushButton's without having to style them individually.
An issue arises when I need to style a QPushButton representing a color chooser. The button should represent the color that was chosen from the color chooser, but instead it just keeps the initial style that I set.
Things I have tried:
Giving an empty style sheet for the item:
this->setStyleSheet(#m_colorChooserButton { });
Setting the style to initial:
this->setStyleSheet(#m_colorChooserButton { background-color: initial});
Using a similar css selector :not:
this->setStyleSheet(".QPushButton:not(#m_colorChooserButton) { background-color: #416eb6; color: #ddd; }");
Is there any way to achieve this result?
I'd like to mimic the :not selector if possible since that's the most straightforward, but at this point, I'd do anything that works.
I'd like to avoid having to manually specify the style for each button I want it to show on, as there are well over 100 buttons and finding the object names of these buttons is very time consuming (large legacy code base).
Thanks
You have 2 choices: apply a stylesheet to just that button and invalidate/reapply it every time you change the selected colour or just subclass that button and reimplement the paintevent:
class ColorButton : public QPushButton{
Q_OBJECT
Q_DISABLE_COPY(ColorButton)
public:
ColorButton(QWidget* parent = Q_NULLPTR)
:QPushButton(parent)
,m_color(Qt::red)
{}
const QColor& color() const {return m_color;}
void setColor(const QColor& val){if(m_color==val) return; m_color=val; update();}
protected:
void paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);
option.palette.setColor(m_color,QPalette::Background);
p.drawControl(QStyle::CE_PushButton, option);
}
private:
QColor m_color;
};
P.S.
The KDE API already has that widget: https://api.kde.org/frameworks-api/frameworks-apidocs/frameworks/kwidgetsaddons/html/classKColorButton.html
Related
I need to make an element like this:
It's a combination of a line edit and a button with the same height and no space between them.
I have tried in Qt Designer but the height of them is not the same and there is always a small space between these 2 elements.
How can I solve this problem?
Maybe you could customize a QComboBox, something like this:
#include <QComboBox>
#include <QLineEdit>
class CustomBox : public QComboBox
{
Q_OBJECT
public:
CustomBox(QWidget * parent = nullptr) : QComboBox(parent)
{
setEditable(true);
QString ss =
"QComboBox::drop-down {border: none;}"
"QComboBox::down-arrow { image: url(/data/whatever.png); }"
"QComboBox::down-arrow:pressed { image: url(/data/whatever-pressed.png); }";
setStyleSheet(ss);
connect(lineEdit(), &QLineEdit::editingFinished, [this](){
QString t = lineEdit()->text();
clear();
lineEdit()->setText(t);
});
}
void setText(const QString & t) { lineEdit()->setText(t); }
QString text() const { return lineEdit()->text(); }
protected:
void showPopup() override
{
emit buttonClicked();
}
signals:
void buttonClicked();
};
The combo box is set as editable, so you can use an underlying QLineEdit, which is, more or less, under full control through the protected lineEdit member function. As you can see, I connected its editingFinished signal to a lambda to avoid adding items to the combo each time (this happens when the user press Enter in an editable combo box).
The styling is quite simple, given that you have a couple of icons for the button.
I exposed the line edit text getter/setter, and added a signal for the button click, which gets emitted from the showPopup protected method (called when the user press the button).
Just an idea.
QPushButton#PBack {
background: #9A9AFF;
font: bold 28pt "Brutal Type";
color: #000000;
border: 3px solid #000000;
border-radius: 25px;
}
QPushButton#PBack:disabled {
background-color: #bdbdbd;
border-color: #787878;
}
How to get border-color inside of paintEvent?
void W_PBackButton::paintEvent(QPaintEvent* event)
{
// Here i use constants
if (isEnabled() == true)
painter.setPen(QPen(QBrush(_enabled), 3));
else
painter.setPen(QPen(QBrush(_disabled), 3));
painter.fillPath(path, palette().base());
painter.drawPath(path);
}
In example i use constants, but how to get color values from palette or styles?
Need to draw triangle button
Now, given the fact question is filled in with more information, I think I can provide some answer. Well, I do not think you need to try getting some "border" property which is declared by Qt. It would be much more difficult. Instead, you can define your own custom property, e.g. "borderColor" (or a couple of them, such for thickness, shape, and so on), and set it like this through stylesheet: "qproperty-borderColor". About definition of custom properties, you can read more information here: https://doc.qt.io/qt-5/properties.html (and an example would be https://doc.qt.io/qt-5/properties.html#a-simple-example ).
The following code illustrates possible way of setting property desired. First of all, you would have to define your own class inheriting the class on which you want property to be set. Let's say it is child of QPushButton:
#include <QPushButton>
class CustomButton
: public QPushButton
{
Q_OBJECT
Q_PROPERTY(QColor borderColor READ GetBorderColor WRITE SetBorderColor)
public:
CustomButton(QWidget *parent = nullptr);
virtual ~CustomButton();
QColor GetBorderColor() const;
void SetBorderColor(const QColor& color);
protected:
virtual void paintEvent(QPaintEvent* event) override;
private:
QColor m_borderColor;
};
#include "CustomButton.h"
#include <QDebug>
CustomButton::CustomButton(QWidget *parent)
: QPushButton(parent)
{
}
CustomButton::~CustomButton()
{
}
QColor CustomButton::GetBorderColor() const
{
return m_borderColor;
}
void CustomButton::SetBorderColor(const QColor& color)
{
m_borderColor = color;
qDebug() << m_borderColor;
}
void CustomButton::paintEvent(QPaintEvent* event)
{
QPushButton::paintEvent(event);
// Do whatever you like with m_borderColor and any other properties.
}
And finally, you would have to promote you QPushButton in the Qt Creator to your own CustomButton. Regarding widgets promotion, you can read about it here: https://doc.qt.io/archives/qt-4.8/designer-using-custom-widgets.html#promoting-widgets
And when you have everything setup, you can easily declare stylesheet for your newly introduced button:
#pushButton {
qproperty-borderColor: #123456;
}
Maybe it is not the way you have intented to achieve your goal initially, but it looks like that declaring custom stylesheet properties and using promotion of widgets would help you achieve your goal without any hacking and in a way Qt was been built to be used. Hopefully, it helps you. :)
I'm trying to use QSS to customize the look of the tool-tip, it works. However, if I try showing the tooltip using QToolTip::showText function, it won't work as intended which means that the QToolTip style sheet doesn't apply to it probably?
My purpose: When changing a slider value I want to show a rectangle somewhere near the slider, so I thought the tooltip was the easiest way to do it?
If you don't understand what I mean, I'm trying to make sliders in Qt feel like Windows 10 UWP so if you want to understand what I mean look for example any slider in Windows like go to Settings->System->Sound and see how their slider works. I've done everything they have except that rectangle which shows when value get changed.
What I'm trying to do:
In the style sheet
QToolTip
{
color: red;
}
In my custom Slider class (inherited from QSlider)
class FSlider : public QSlider
{
Q_OBJECT
public:
FSlider(QWidget *parent = 0) : QSlider(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(notifyValueChanged(int))); }
protected:
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
bool event(QEvent* event);
private slots:
void notifyValueChanged(int Value);
};
void FSlider::notifyValueChanged(int Value)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sliderHandle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle);
QToolTip::setFont(QFont("Segoe UI", 13, 400));
QToolTip::showText(mapToGlobal(QPoint(sliderHandle.x() - sliderHandle.width() - 2, sliderHandle.y() - sliderHandle.height() - 30)), QString::asprintf("%i", Value));
}
Thanks for reading !
I had the same issue at first with Qt 5.12.2. I was missing the third argument to QToolTip::showText. I had to add the parent widget. In that case, the CSS that I had applied to the tooltip in mainwindow.ui applied to the QToolTip . Without the parent, it is a separate window shown on the position specified and has no knowledge of the css.
I use drag and drop in my QTableView (works). However, I do not see any drop indicator. I should see a line where the drop is supposed to be inserted, shouldn't I? At least here they say so.
My init is pretty much standard.
// see model for implementing logic of drag
this->viewport()->setAcceptDrops(allowDrop);
this->setDragEnabled(allowDrag);
this->setDropIndicatorShown(true);
this->m_model->allowDrop(allowDrop);
I have no idea why I do not see the indicator. A style sheet is used with the views, could that be the reason. However, I have disabled the stylesheet and still do not see it.
The view uses entire rows for selection, not sure if this causes an issue. So any hint is appreciated.
-- Edit --
As of the comment below, tried all selection modes: single, multi or extended, no visual effect. Also tried cell instead of row selection, again no improvement.
-- Edit 2 --
Currently evaluating another style proxy example, similar to the one below, originally referenced here
-- Related --
QTreeView draw drop indicator
How to highlight the entire row on mouse hover in QTableWidget: Qt5
https://forum.qt.io/topic/12794/mousehover-entire-row-selection-in-qtableview/7
https://stackoverflow.com/a/23111484/356726
I faced the same problem, I tried two options which both worked for me. IIRC, the help came from an answer on SO.
if you are subclassing QTreeView, you can override its paintEvent() method. It is calling by default the drawTree() method and the paintDropIndicator() one (the latter being part of QAbstractItemView private class).
You can call drawTree() from your paintEvent(), and it should override the default drag and drop indicator as well :
class MyTreeView : public QTreeView
{
public:
explicit MyTreeView(QWidget* parent = 0) : QTreeView(parent) {}
void paintEvent(QPaintEvent * event)
{
QPainter painter(viewport());
drawTree(&painter, event->region());
}
};
the other method is to subclass QProxyStyle and overriding the drawPrimitive() method. When you get the element QStyle::PE_IndicatorItemViewItemDrop as a parameter, you can paint it your own way.
The code will look like this:
class MyOwnStyle : public QProxyStyle
{
public:
MyOwnStyle(QStyle* style = 0) : QProxyStyle(style) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
//custom paint here, you can do nothing as well
QColor c(Qt::white);
QPen pen(c);
pen.setWidth(1);
painter->setPen(pen);
if (!option->rect.isNull())
painter->drawLine(option->rect.topLeft(), option->rect.topRight());
}
else
{
// the default style is applied
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};
I have subclassed QTreeView and made model subclassed from QAbstractTableModeland everything works fine. If something is being changed in QTreeView from code (not by user), then that row's text color becomes red. I have implemented this trough checking Qt::TextColorRole from data() function and returning Qt::red.
But if that particular row is being selected, then text color changes automatically to black (and background color to light green, which is normal). After deselecting that row everything is OK again. In debug mode I've seen that data() function returns true value for selected row (Qt::red).
Now how can I solve this problem, what may cause to this incorrect behaviour?
Thank you in advance!
I've found a way of doing this trough a delegate. Here is the code
class TextColorDelegate: public QItemDelegate
{
public:
explicit TextColorDelegate(QObject* parent = 0) : QItemDelegate(parent)
{ }
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionViewItem ViewOption(option);
QColor itemForegroundColor = index.data(Qt::ForegroundRole).value<QColor>();
if (itemForegroundColor.isValid())
{
if (itemForegroundColor != option.palette.color(QPalette::WindowText))
ViewOption.palette.setColor(QPalette::HighlightedText, itemForegroundColor);
}
QItemDelegate::paint(painter, ViewOption, index);
}
};
And for using delegate you should write something like this
pTable->setItemDelegate(new TextColorDelegate(this));
where pTable's type is QTableView*;
Does the following code keep your text color red?
QPalette p = view->palette();
p.setColor(QPalette::HighlightedText, QColor(Qt::red));
view->setPalette(p);