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
Related
I am using Qt 5.11.1 and Qt Creator to create a project. My code draws several ellipses in the paintEvent function that I have overriden. But because of the paintEvent function's working style the buttons that I have under the ellipses are being erased. I want to have a window that has ellipses at the top and buttons at the bottom of the window. It will roughly seem like this:
Is there any way to do this. Right now, the buttons are being erased and I only have the ellipses. I would be really glad if someone could guide me.
Thanks in advance.
Note: My ellipses are green and my background is black but I have tried by changing the background to white or changing the stylesheet of the buttons, it didn't work.
This is my .h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
protected:
void paintEvent(QPaintEvent *e);
void setBackGroundColorToBlack();
};
#endif // MAINWINDOW_H
This is my .cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtGui>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setBackGroundColorToBlack();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *event) {
setUpdatesEnabled(false);
QPainter painterObj;
painterObj.begin(this);
painterObj.setPen(QPen(Qt::green, 2, Qt::SolidLine, Qt::RoundCap));
painterObj.drawEllipse(0, 0, 318, 390);//456
painterObj.drawEllipse(53, 65, 212, 260);//304
painterObj.drawEllipse(106, 130, 106, 130);//152
painterObj.end();
}
void MainWindow::setBackGroundColorToBlack() {
QPalette pal = palette();
// set black background
pal.setColor(QPalette::Background, Qt::black);
this->setAutoFillBackground(true); // This enables the qt to fill the background before the paint event.
this->setPalette(pal);
//update();
}
This is what I get:
My ui file is like this:
In general - don't use a QMainWindows class. It's for "big" desktop applications that have a menu, a status bar, etc. All you need is to derive from QDialog or even just QWidget. And you can paint the background yourself, so no need to mess with the pallete. The minimal example that should work is below. If the Bar button doesn't show up, then your Qt for the target is broken: the default platform style doesn't work.
// https://github.com/KubaO/stackoverflown/tree/master/questions/painted-with-children-51498155
// This project is compatible with Qt 4 and Qt 5
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
QRectF scaled(const QRectF &rect, qreal scale) {
auto const center = rect.center();
auto const w = rect.width()*scale;
auto const h = rect.height()*scale;
return {center.x() - w/2.0, center.y() - h/2.0, w, h};
}
QRectF marginAdded(const QRectF &rect, qreal margin) {
return rect.adjusted(margin, margin, -margin, -margin);
}
const char buttonQSS[] =
"* { background-color: white; border-width: 2px; border-style:solid; border-color: red;"
" border-radius: 3px; padding: 3px; }"
"*:pressed { padding-left: 5px; padding-top: 5px; background-color: lightGray; }";
class MyWindow : public QWidget {
Q_OBJECT
QPushButton m_restoreButton{"Restore Size"};
QPushButton m_otherButton{"Bar"};
QGridLayout m_layout{this};
Q_SLOT void onRestore() { resize(sizeHint()); }
public:
explicit MyWindow(QWidget *parent = {}) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
m_layout.addItem(new QSpacerItem(0, 100, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 0, 0);
m_layout.addWidget(&m_restoreButton, 1, 0);
m_layout.addWidget(&m_otherButton, 1, 1);
m_restoreButton.setStyleSheet(buttonQSS);
connect(&m_restoreButton, SIGNAL(clicked(bool)), SLOT(onRestore()));
}
protected:
void paintEvent(QPaintEvent *) override {
qreal const penWidth = 2.0;
// Cover the area above all the children
QRectF const area = marginAdded(QRect(0, 0, width(), childrenRect().top() - 10), penWidth);
QPainter p(this);
p.fillRect(rect(), Qt::black);
p.setPen({Qt::green, penWidth});
p.drawEllipse(scaled(area, 3./3.));
p.drawEllipse(scaled(area, 2./3.));
p.drawEllipse(scaled(area, 1./3.));
}
QSize sizeHint() const override { return {320, 568}; /* iPhone 5 */ }
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWindow w;
w.show();
return a.exec();
}
#include "main.moc"
I'm working on a Qt application (in C++). Without appyling any styles, my menu looks like this:
I'd like it to look like this:
How do I achieve this? Either using qss, or programmatically?
I already tried this, without success:
menu->addAction(tr("Add"), this, SLOT(CreateNewWaypoint()))->setIconVisibleInMenu(false);
Answers for both Qt4.8 and Qt5 are needed to get the full bounty!
One way to solve the problem is to use QProxyStyle:
customstyle.h
#ifndef CUSTOMSTYLE_H
#define CUSTOMSTYLE_H
#include <QProxyStyle>
#include <QStyleOptionMenuItem>
class CustomStyle : public QProxyStyle{
public:
using QProxyStyle::QProxyStyle;
void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const override
{
if(element == QStyle::CE_MenuItem){
QStyleOptionMenuItem myMenuOption;
const QStyleOptionMenuItem *menuOption =
qstyleoption_cast<const QStyleOptionMenuItem *>(opt);
if (menuOption) {
const int width = pixelMetric(PM_SmallIconSize)+6;
myMenuOption = *menuOption;
QRect r(myMenuOption.rect);
r.setLeft(-width);
myMenuOption.rect = r;
}
QProxyStyle::drawControl(element, &myMenuOption, p, w);
return;
}
QProxyStyle::drawControl(element, opt, p, w);
}
};
#endif // CUSTOMSTYLE_H
then you install it in the QApplication:
QApplication a(argc, argv);
QApplication::setStyle(new CustomStyle);
You can influence on how your menu appears by playing with its style sheet. With you example code you can do the following:
menu.setStyleSheet("QMenu::item {"
"padding: 2px 5px 2px 2px;"
"}"
"QMenu::item:selected {"
"background-color: rgb(0, 85, 127);"
"color: rgb(255, 255, 255);"
"}");
Note the padding property, which sets the offsets of your menu item rectangles.
I wanted to get rid of the icons in the standard context menu for a QPlainTextEdit.
Just using setIconVisibleInMenu(false) for all the actions in the QMenu still left the icon space as shown in the question.
I was only able to get rid of the icon space when I set the icon for the menu actions to a null icon.
Full example:
void CustomPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
foreach (QAction *action, menu->actions()) {
action->setIcon(QIcon());
}
menu->exec(event->globalPos());
delete menu;
}
I am trying to create a QPushButton in my project such that the text shows on top of the custom button image or icon.
I tried the below methods:
imagePath = path;
QPixmap pixmap(imagePath);
QIcon ButtonIcon(pixmap);
button->setIcon(ButtonIcon);
button->setIconSize(pixmap.rect().size());
button->setGeometry(0,0,height,width);
button->setStyleSheet(
"background-color: gray;"
"border: 1px solid black;"
"border-radius: "+QString::number(radius)+"px;"
"color: lightGray; "
"font-size: 25px;"
);
When I try to use the setText here, it shows the icon first and text on its right. I want the text to appear on top of the icon.
I also tried the below method I found online:
imagePath = path;
button->setGeometry(0,0,height,width);
button->setStyleSheet("background-image: url(:/images/images/2adjacentTracksButton.png));"
"background-position: center center");
This one is not accepting my url path, hence is not displaying the image I need on the button.
How can I solve this?
When it comes to manipulate button, you may want to do your own class, which will implement QAbstractButton. Something like this:
class MyButton : public QAbstractButton
{
Q_OBJECT
public:
static MyButton* createButton(QIcon icon, QWidget *parent);
~MyButton();
void setText(QString);
void setIcon(eIcon);
void setOrientation(Qt::Orientation);
protected :
MyButton(QWidget *parent);
// here, you can reimplement event like mousePressEvent or paintEvent
private :
QBoxLayout* m_ButtonLayout;
QLabel* m_IconLabel;
QIcon m_Icon;
QLabel* m_TextLabel;
}
In the .cpp :
MyButton::MyButton(QWidget *parent)
: QAbstractButton(parent)
{
m_ButtonLayout = new QBoxLayout(QBoxLayout::LeftToRight, this);
m_ButtonLayout->setAlignment(Qt::AlignCenter);
m_ButtonLayout->setContentsMargins(0, 0, 0, 0);
m_ButtonLayout->setSpacing(1);
m_IconLabel = new QLabel(this);
m_IconLabel->setAlignment(Qt::AlignCenter);
m_ButtonLayout->addWidget(m_IconLabel);
m_TextLabel = new QLabel(this);
m_TextLabel->setAlignment(Qt::AlignCenter);
m_ButtonLayout->addWidget(m_TextLabel);
//m_TextLabel->hide();
}
MyButton* MyButton::createButton(QIcon icon, QWidget *parent)
{
MyButton* pButton = new MyButton(parent);
pButton->setIcon(icon);
return pButton;
}
void MyButton::setText(QString text)
{
m_TextLabel->setVisible(!text.isEmpty());
m_TextLabel->setText(text);
QAbstractButton::setText(text);
}
void MyButton::setIcon(QIcon icon)
{
m_Icon = icon;
m_IconLabel->setVisible(true);
}
void MyButton::setOrientation(Qt::Orientation orientation)
{
if (orientation == Qt::Horizontal)
m_ButtonLayout->setDirection(QBoxLayout::LeftToRight);
else
m_ButtonLayout->setDirection(QBoxLayout::TopToBottom);
}
And now you can create your button with your icon by calling the static method:
MyButton* button = MyButton::createButton(myButtonIcon, this);
It is just a basic example I gave you, and I am not sure it will work (this is a thing I did some time ago) but you can give it a shot. Hope that helps !
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);
}
I have this little test-case which is supposed to show two widgets, with one overlapping the other completely. The one is translucent so the other widget should shine through it.
For that purpose, I set a style sheet on the one widget using a type-selector Menu (which is its class name). But instead of making the widget opaque by a factor of 200/255, it makes it completely translucent as if the type-selector doesn't apply at all to the menu object, so that I see no shine of blue anymore.
If I instead use the * selector, it works as expected. I tested the value of metaObject()->className(), which correctly reports Menu. Can anyone hint me to the error I have made please? This is a reduced testcase of a real program which shows a much more weird behavior, and I first want to make this reduced testcase work.
#include <QtGui/QApplication>
#include <QtGui/QWidget>
#include <QtGui/QLayout>
#include <QtGui/QVBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QResizeEvent>
class Menu: public QWidget {
Q_OBJECT
public:
Menu(bool translucent, QWidget *p):QWidget(p) {
if(translucent) {
setStyleSheet("Menu { background-color: rgba(0, 0, 150, 200) }");
}
QLabel *label = new QLabel(
translucent ? "\n\nHello I'm translucent" : "I'm not translucent");
label->setStyleSheet("color: white; font-size: 20pt");
QLayout *mylayout = new QVBoxLayout;
setLayout(mylayout);
mylayout->addWidget(label);
}
};
class MyWindow : public QWidget {
public:
MyWindow() {
Menu *m1 = new Menu(false, this);
Menu *m2 = new Menu(true, this);
m1->lower();
m2->raise();
}
protected:
void resizeEvent(QResizeEvent *event) {
foreach(QWidget *w, findChildren<QWidget*>()) {
w->setGeometry(0, 0, width(), height());
}
}
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
MyWindow w;
w.show();
app.exec();
}
When using stylesheets with QWidget subclasses, you are supposed to override paintEvent this way:
void Menu::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
See the stylesheet reference from Qt documentation.