Customized color on progressbar delegate - c++

Been trying to do this for quite a while and took advice from every forum post i could find but i still cant solve it. This is my current code and I would really like to change the color of the chunk on the progress bar. Every other setting is working, except the colors.
In my workspace object that fills up one subview on the MainWindow.
Workspace::Workspace( QWidget* parent) : QWidget( parent )
{
QTableView* tableView = new QTableView();
// ...
tableView->setItemDelegate(new ProgressBarDelegate);
}
The delegate.cpp looks like this:
ProgressBarDelegate::ProgressBarDelegate( QObject* parent )
: QStyledItemDelegate(parent)
{
}
void ProgressBarDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == 2)
{
int progressPercentage = index.model()->data(index, Qt::DisplayRole).toInt();
QStyleOptionProgressBarV2 progressBarOption;
progressBarOption.rect = QRect(option.rect.x(), option.rect.y() + 5 , option.rect.width(), option.rect.height() / 1.5);
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progressPercentage;
QPalette pal = progressBarOption.palette;
QColor col = QColor(35, 35,25);
pal.setColor(QPalette::Highlight, col); // or QPalette::Window doesnt matter
progressBarOption.palette = pal;
if(option.state & QStyle::State_Selected)
{
}
QApplication::style()->drawControl( QStyle::CE_ProgressBar,
&progressBarOption,
painter);
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
Currently, no matter what I do the color doesnt change from OSX standard light-gray.
Running OSX 10.6.7 and Qt 4.8.1 if that matters. thank you!
Edit:
I was able to do the following:
app.setStyleSheet("QScrollBar:horizontal { border: 2px solid green;background: cyan;height: 15px;margin: 0px 20px 0 20px;}");
But when I do this:
app.setStyleSheet("QProgressBar:horizontal { border: 1px solid gray; border-radius: 3px; background: white; padding: 1px; }");
NOTHING changes on the progressbar. I am in theory not creating any progressbar objects, im just settings a style how I'm viewing my data in my delegate. But surely, I cant be the first person who would want to do this right?
Also, if this doesnt work, how can I do this (having a styled progressbar) in a tableview?

You should use Qt Style Sheet, which allows us to customize UI of many controls to give unique look and feel across platforms. Check this.
Create a new simple Qt Gui project, open UI Form editor and add a Progress Bar control from under 'Display Widgets' in tool window. Now write following code in constructor of MainWindow..
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Customize progress-bar's style..
QString style = "QProgressBar {border: 2px solid grey; border-radius: 5px; text-align: center;}";
style += "QProgressBar::chunk {background-color: #CD96CD; width: 10px; margin: 0.5px;}";
// Assuming objectName is 'progressBar'..
ui->progressBar->setStyleSheet(style);
}
Compile and run.
If you just want to change that single QProgressBar control, then above method is sufficient, but if you want to apply styles at application level (say all QProgressBar controls and some other controls), then proper way is to create a *.css file, write styles using Qt Style Sheet Reference and then read that file in Qt and call
QApplication::setStyleSheet(QString style).
Besides, style sheet uses the same syntax as CSS and also supports various selectors.
Edit:
I agree that above method works only with controls and not delegates. I found something for delegates also. Try following paint function.
void ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == 2)
{
QProgressBar renderer;
int progressPercentage = index.model()->data(index, Qt::DisplayRole).toInt();
// Customize style using style-sheet..
QString style = "QProgressBar { border: 2px solid grey; border-radius: 5px; }";
style += "QProgressBar::chunk { background-color: #05B8CC; width: 20px; }";
renderer.resize(option.rect.size());
renderer.setMinimum(0);
renderer.setMaximum(100);
renderer.setValue(progressPercentage);
renderer.setStyleSheet(style);
painter->save();
painter->translate(option.rect.topLeft());
renderer.render(painter);
painter->restore();
}
else
QStyledItemDelegate::paint(painter, option, index);
}
So here the point is that instead of using QStyleOption, we can use directly the control itself as a renderer. Hope this helps..

Instead of pal.setColor(QPalette::Window, col);
Use pal.setColor(QPalette::Highlight, col);
This should work.
I used the code in Paintevent
void Dialog::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(ShowPrintDialog()));
QStyleOptionProgressBarV2 progressBarOption;
progressBarOption.rect = QRect(20,20,30,30);
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = 75;
QPalette pal = progressBarOption.palette;
QColor col = QColor(0,255,0);
pal.setColor(QPalette::Highlight, col);
progressBarOption.palette = pal;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,&progressBarOption, &painter);
}

Related

Changing Background color of QCheckBox

I want to have a checkbox with the following style:
For that purpose I have created the follow stylesheet:
QCheckBox { background-color : cyan; }
QCheckBox::indicator { width : 20px; height : 20px; }
sadly the result was not was I expected, here is what I get:
But I want to have all background with the color cyan.
I have checked the Qt Documentation About StyleSheet and have also also checked some old questions, but none of the has served me. When I added QCheckBox::indicator{ background-color : cyan; } the button became all cyan (like on the first image on the state unchecked) on checked and unchecked states. Neither adding other proprieties like border helped me.
I also tried to use QPalette instead, but never changed.
Here is my code:
QWidget* w = new QWidget();
QCheckBox* cb = new QCheckBox(w);
cb->setFixedSize(20, 20);
cb->setStyleSheet(QString("QCheckBox { background-color : cyan; }") + '\n' +
"QCheckBox::indicator { width : 20px; height : 20px; border : }");
w->show();
EDIT 28/09/2020
I have recently figured out that the style of the application has influence on it. I was using the Breeze style, changing to Fusion style made it work as expected. The only problem is that it requires change the theme of the application itself. Anyway, here is the code sample in case it helps anyone:
#include <QApplication>
#include <QWidget>
#include <QCheckBox>
int main(int argc, char** argv)
{
QApplication a(argc, argv);
qApp->setStyle(QStyleFactory::create("Fusion"));
QWidget* w = new QWidget();
QCheckBox* cb = new QCheckBox(w);
cb->setFixedSize(20, 20);
cb->setStyleSheet(QString("QCheckBox { background-color : cyan; }") + '\n' +
"QCheckBox::indicator { width : 20px; height : 20px; border : }");
w->show();
}
I did not solve my problem using the stylesheet commands, I actually think that it is not possible SOURCE.
Anyway, I was I able to achieve my goals using one image for the checked state and another one for the unchecked state as follow.
QWidget* w = new QWidget();
QCheckBox* cb = new QCheckBox(w);
cb->setFixedSize(20, 20);
cb->setStyleSheet("QCheckBox::indicator { width : 20; height : 20; }\n"
"QCheckBox::indicator::unchecked { image : url(/path/**/unchecked.png); }\n"
"QCheckBox::indicator::checked { image : url(/path/**/checked.png); }");
w->show();
Probably
QCheckBox::indicator:unchecked {background-color : cyan;});
is what you wanted to see.
Edit: Too late

QLineEdit change text weight on activation

I want to make QLineEdit looks like QLabel with bold text if it's inactive and like QLineEdit with normal weight if active. In my understanding "active" = when text cursor shown and user can enter a text.
I tried to apply this css:
QLineEdit {
font-weight: normal;
border: 1px solid black;
}
QLineEdit:!focus {
font-weight: bold;
border: none;
}
Border works as expected, but font-weight is always bold.
I was thinking of creating my class to handle activation event, but can't found anything related with it.
I will be very thankful if somebody can help me.
You need to get your custom slot for the focusChanged event. There you can change the font like this:
QFont font = ui->lineEdit_search->font();
font.setWeight(QFont::Bold); // or
font.setWeight(QFont::Normal);
ui->lineEdit_search->setFont(font);
An example where I handled a LineEdit search box is here, but you have to customize it with the bold text.
void MainWindow::focusChanged(QWidget* old, QWidget* now)
{
if(now != NULL &&
now->objectName() == "lineEdit_search")
{
if(ui->lineEdit_search->text() == tr("Search..."))
{
ui->lineEdit_search->clear();
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::black);
ui->lineEdit_search->setPalette(*palette);
delete palette;
}
}
else if(old != NULL &&
old->objectName() == "lineEdit_search")
{
if(ui->lineEdit_search->text().isEmpty())
{
ui->lineEdit_search->setText(tr("Search..."));
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::gray);
ui->lineEdit_search->setPalette(*palette);
delete palette;
}
}
}
When you turn your line edit to inactive,
//Set your line edit to read only
yourLineEdit->setReadOnly(true);
//Get your widget background color
const QColor clr = this->palette().color(QWidget::backgroundRole());
//Set the widget background color to both the line edit background and its border
QString backGroundstyle = "background-color: rgb(%1, %2, %3);";
QString borderStyle = "border: 1px solid rgb(%1, %2, %3);";
yourLineEdit->setStyleSheet(backGroundstyle.arg(clr.red()).arg(clr.green()).arg(clr.blue()) + borderStyle.arg(clr.red()).arg(clr.green()).arg(clr.blue()));
When you turn line edit to active
//Make your line edit read-write
yourLineEdit->setReadOnly(false);
//Bring back your styles.
QString backGroundstyle_active = "background-color: white;";
QString borderStyle_active = "border: 1px solid black;";
yourLineEdit->setStyleSheet(backGroundstyle_active + borderStyle_active);

How to draw line behind buttons in Qt?

Question
In a custom widget, I'd like to draw lines (using QPainter) that connect buttons in a QGridLayout. The lines shall be behind the buttons, in order to
a) not interfere with buttons in between
b) allow starting the lines in the center of the buttons, not the edges
Considering ideas from this question, I could almost realize a simple, basic version running in my GUI application (source code below).
As long as I use QPushButton with the standard Qt style, it works like a charm (left), but, as I want to use a custom style, the lines overlap (right):
What property or mechanism is causing this behavior?
Code
MyFrame.h:
#include <QFrame>
class MyFrame : public QFrame
{
public:
MyFrame();
virtual ~MyFrame() = default;
};
MyFrame.cpp:
#include "MyFrame.h"
#include "LineDrawWidget.h"
#include <QVBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QButtonGroup>
MyFrame::MyFrame()
{
auto* mainLayout = new QVBoxLayout(this);
auto* buttonLayout = new QGridLayout();
QPushButton* button;
auto* buttons = new QButtonGroup();
for (int i = 0; i < 3; ++i) {
button = new QPushButton();
button->setText(QString::number(i+1));
button->setFixedHeight(40);
button->setFixedWidth(40);
button->setStyleSheet("QPushButton { color : black; background-color : white; }");
button->setStyleSheet("QPushButton { border-style : outset; border-color: black; border-width: 2px; border-radius: 6px; }");
buttonLayout->addWidget(button);
buttons->addButton(button, i);
}
auto* lineDraw = new LineDrawWidget(
buttons->button(0),
buttons->button(2));
lineDraw->setLayout(buttonLayout);
mainLayout->addWidget(lineDraw);
}
LineDrawWidget.h:
#include <QWidget>
class LineDrawWidget : public QWidget
{
public:
LineDrawWidget(
QWidget* from,
QWidget* to,
QWidget* parent = nullptr);
virtual ~LineDrawWidget() = default;
protected:
virtual void paintEvent(QPaintEvent* e) final override;
private:
QWidget* _from;
QWidget* _to;
};
LineDrawWidget.cpp:
#include "LineDrawWidget.h"
#include <QPainter>
LineDrawWidget::LineDrawWidget(
QWidget* from,
QWidget* to,
QWidget* parent) :
QWidget(parent),
_from(from),
_to(to)
{
}
void LineDrawWidget::paintEvent(QPaintEvent* e)
{
(void)e;
QPainter painter(this);
QPoint start = _from->mapToGlobal(_from->rect().bottomLeft());
QPoint end = _to->mapToGlobal(_to->rect().topRight());
painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));
}
I think the problem is the two separate calls to setStyle for a single QPushButton -- the second call appears to reset all properties not present in it. Try putting it all in a single call...
button->setStyleSheet("color : black; background-color : white; border-style : outset; border-color: black; border-width: 2px; border-radius: 6px;");
Seems to work for me.

Qt Widget with QLineEdit-like background

I'm looking to create a custom widget where part of it is a "background" that looks like a QLineEdit (or QProgressBar),
e.g. but without the text.
I've come up with a couple of hacky ways to do this, but neither of them seem like a good solution:
1.
QPainter painter(this);
int penwidth = painter.pen().width();
int width = this->width();
int height = this->height() - 20;
QPoint tl(penwidth / 2, penwidth / 2 + 10);
QPoint bl(penwidth / 2, height - penwidth);
QPoint tr(width - penwidth, penwidth / 2);
QPoint br(width - penwidth, height - penwidth);
QRect rect(tl, br);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().color(QPalette::Button);
option.rect = rect;
this->style()->drawControl(QStyle::CE_ProgressBarGroove, &option, &painter, this);
This has the disadvantage of not being fully controllable, especially when inside a layout as I intend it to be
2.
Using a QLineEdit widget but setting it to NoFocus and ReadOnly.
This seems like overkill to me, as I'll never want any of the text functionality
What is the best solution to this?
Use QLabel with special stylesheet:
ui->label->setText("");
ui->label->setStyleSheet("QLabel{ border: 1px solid gray; background-color:white; border-radius:2px}");
Stylesheet:
QLabel
{
border: 1px solid gray;
background-color:white;
border-radius:2px
}
QLabel has no any other unnecessary things so it is better than QLineEdit or QProgressBar.
Result:

How to render a red push button using QStyle.drawControl()?

With the following code I tried to render a red push button using QStyle.drawControl():
#include <QtCore/QtCore>
#include <QtGui/QtGui>
class Widget : public QWidget
{
virtual void paintEvent(QPaintEvent* event)
{
QStyleOptionButton opt;
opt.palette = QPalette(Qt::red);
opt.state = QStyle::State_Active | QStyle::State_Enabled;
opt.rect = QRect(50, 25, 100, 50);
QPainter painter(this);
style()->drawControl(QStyle::CE_PushButton, &opt, &painter);
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Widget w;
w.resize(200, 100);
w.show();
return app.exec();
}
However I get the following result:
How do I render a red push button using QStyle.drawControl()?
I'm using Qt 4.8.1 and Visal Studio 2010 on Windows XP.
The buttons are drawn by the native style engine, so the palette might not be used at all (see that question from the FAQ).
You can use an actual button with a stylesheet that you pass as the last parameter to the own button's style drawControl function.
class Widget : public QWidget
{
// To allow the automatic deletion without parenting it
QScopedPointer<QPushButton> button;
public:
Widget() : button(new QPushButton) {
button->setStyleSheet("background-color: red");
}
virtual void paintEvent(QPaintEvent* event)
{
QStyleOptionButton opt;
opt.state = QStyle::State_Active | QStyle::State_Enabled;
opt.rect = QRect(50, 25, 100, 50);
QPainter painter(this);
button->style()->drawControl(QStyle::CE_PushButton, &opt, &painter,
button.data());
}
};
But you will loose the native style, so you'll have to fake it (bali182's answer might be useful for that part).
Or you can use the same button with colorize effect and call its render() function to paint it:
class Widget : public QWidget {
QScopedPointer<QPushButton> button;
public:
Widget() : button(new QPushButton) {
QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect(button.data());
effect->setColor(Qt::red);
button->setGraphicsEffect(effect);
}
virtual void paintEvent(QPaintEvent* event) {
button->setFixedSize(100, 50);
button->render(this, QPoint(50, 25));
}
};
What you are trying to do, seems overly complicated. If you just want a red button, why not use the setStyleSheet() method of the QPushButton? It takes a QString, and you can define your button similar to CSS. Here i created you a red button, similar to the XP ui buttons:
QPushButton
{
background: qlineargradient(x1:0,y1:0,x2:0,y2:1, stop:0 #f4a3a3,stop: 1 #cc1212);
border-width: 1px;
border-color: #d91414;
border-style: solid;
padding: 5px;
padding-left:10px;
padding-right:10px;
border-radius: 3px;
color:#000;
}
QPushButton:hover
{
border-color: #e36666;
}
QPushButton:pressed
{
background:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop: 0 #de8383, stop: 1 #ad0C0C);
border-color: #d91414;
}
Now you just need to pass the code above as a string to your buttons setStyleSheet() method. If you want to create a button widget, what is red by default, then extend the QPushButton class, create a static QString field with the content above, and set the button as stylesheet in the constructor.
More easy to understand examples on stylesheets:
Stylesheet Examples