Qt Stylesheet for custom widget
Asked Answered
T

6

39

I have several custom widget in my current project. I wish to apply stylesheets to them and when I do so inside Qt Creator, it appears to work. However, when executing the program, no stylesheet is used. The stylesheets for the Qt widgets are working normally.

Does anyone have any advice?

WidgetUnits.h

#ifndef WIDGETUNITS_H
#define WIDGETUNITS_H

#include <QList>

#include <QWidget>
#include <QPainter>

#include <Widgets/JECButton.h>

#include <Unit.h>
#include <Time.h>

namespace Ui
{
    class WidgetUnits;
}

class WidgetUnits : public QWidget
{
    Q_OBJECT

public:
    explicit WidgetUnits(QWidget *parent = 0);
    ~WidgetUnits();

    void setNumTimes(const int& numTimes);

public slots:
    void updatePictures(const Time* time);

protected:
    void paintEvent(QPaintEvent *event);
private:
    void checkNewQueue(const QList<QList<Unit*>*>* units);
    Ui::WidgetUnits *ui;

    const int pictureWidth;                         // The width of the Unit pictures.
    const int pictureHeight;                        // The height of the Unit pictures.

    QList<QList<JECButton*>*> buttonPictures;       // The Units' pictures. The outer QList stores the QList of pictures for a given tick.
                                                    // The inner QList stores the JECButtons for the specific tick.
};

WidgetUnits.cpp

#include "WidgetUnits.h"
#include "ui_WidgetUnits.h"

WidgetUnits::WidgetUnits(QWidget *parent):
    QWidget(parent),
    ui(new Ui::WidgetUnits),
    pictureWidth(36),
    pictureHeight(36)
{
    ui->setupUi(this);
}

WidgetUnits::~WidgetUnits()
{
    delete ui;
}

void WidgetUnits::updatePictures(const Time *time)
{
    // Only showing units that started to get built this turn.
    checkNewQueue(time->getUnits());
    checkNewQueue(time->getBuildings());
    checkNewQueue(time->getUpgrades());

    // Updating the position of the remaining pictures (after some were removed).
    // Checking the maximum number of Units made in one tick.
    int maxNewQueue = 0;
    for (int a = 0; a < buttonPictures.length(); ++a)
    {
        if (buttonPictures.at(a)->length() > maxNewQueue)
        {
            maxNewQueue = buttonPictures.at(a)->length();
        }
    }

    if (buttonPictures.length() > 0)
    {
        this->setGeometry(0, 0, buttonPictures.length() * 130,
                          maxNewQueue * (pictureWidth + 10) + 20);

        QList<JECButton*>* tickButtons = 0;
        for (int a = 0; a < buttonPictures.length(); ++a)
        {
            tickButtons = buttonPictures.at(a);
            for (int b = 0; b < tickButtons->length(); ++b)
            {
                tickButtons->at(b)->move(a * 130, b * (pictureHeight + 10));
            }
        }
    }
    update();
}

void WidgetUnits::checkNewQueue(const QList<QList<Unit *> *> *units)
{
    if (units != 0)
    {
        const Unit* currentUnit = 0;
        JECButton* currentButton = 0;
        for (int a = 0; a < units->length(); ++a)
        {
            buttonPictures.append(new QList<JECButton*>());

            for (int b = 0; b < units->at(a)->length(); ++b)
            {
                currentUnit = units->at(a)->at(b);

                // Verifying that there is an item in the queue and the queue action was started this turn.
                if (currentUnit->getQueue() != 0 && currentUnit->getAction()->getTimeStart() == currentUnit->getAction()->getTimeCurrent()
                        && (currentUnit->getAction()->getType() == Action::BUILD || currentUnit->getAction()->getType() == Action::TRAIN ||
                            currentUnit->getAction()->getType() == Action::UPGRADE))
                {
                    buttonPictures.last()->append(new JECButton(this));
                    currentButton = buttonPictures.last()->last();

                    QImage* image = new QImage(currentUnit->getQueue()->getUnitBase()->getImage().scaled(pictureWidth, pictureHeight));
                    currentButton->setImage(*image);
                    currentButton->setGeometry(0, 0, currentButton->getImage().width(),
                                                       currentButton->getImage().height());
                    currentButton->setColorHover(QColor(0, 0, 225));
                    currentButton->setColorPressed(QColor(120, 120, 120));
                    currentButton->setImageOwner(true);
                    currentButton->setVisible(true);
                }
            }
        }
    }
}

void WidgetUnits::setNumTimes(const int &numTimes)
{
    // Appending new button lists for added ticks.
    for (int a = buttonPictures.length(); a < numTimes; ++a)
    {
        buttonPictures.append(new QList<JECButton*>());
    }
}

void WidgetUnits::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
}

The widget is visible- I set a tooltip which it showed me (It's just the same color of the QScrollArea it's sitting in).

Tameshatamez answered 1/9, 2011 at 20:48 Comment(4)
Could you show the corresponding stylesheet ?Eileneeilis
style sheet = background: rgb(170, 0, 255);\nborder: 2px solid black;Tameshatamez
After searching the interwebs for several hours, I found out about this developer.qt.nokia.com/forums/viewthread/7340 The code referenced on that page was required for the stylesheet to work.Tameshatamez
@jecjackal: If you've found a solution, please submit it as an answer to this question for the benefit of any future viewers.Palate
A
67

I had a similar problem and it was solved using jecjackal's comment. As sjwarner said, it would be much more noticeable in the form of an answer. So I'll provide it. For the benefit of any future viewers. Again, it isn't my answer! Appreciate jecjackal for it!

As it is said in the Qt's stylesheets reference, applying CSS styles to custom widgets inherited from QWidget requires reimplementing paintEvent() in that way:

 void CustomWidget::paintEvent(QPaintEvent *)
 {
     QStyleOption opt;
     opt.init(this);
     QPainter p(this);
     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
 }

Without doing it your custom widgets will support only the background, background-clip and background-origin properties.

You can read about it here: Qt Stylesheets reference in the section "List of Stylable Widgets" -> QWidget.

Arithmomancy answered 11/1, 2012 at 10:41 Comment(0)
G
14

There is an answer much easier than writing your own paintEvent: subclass QFrame instead of QWidget and it will work right away:

class WidgetUnits : public QFrame
{
    Q_OBJECT
....
Grunenwald answered 28/2, 2014 at 11:46 Comment(1)
I've found this to be a much less intrusive way of solving the problem. Its easy enough to just find/replace that one line for each custom widget, as well as doing that to the cpp file counter part by replacing QWidget(parent) to QFrame(parent) within the default constructor.Toxic
S
8

For completeness, the same problem is present in PyQt. You can apply a stylesheet to a subclassed QWidget by adding similar code:

def paintEvent(self, pe):
  opt = QtGui.QStyleOption()
  opt.init(self)
  p = QtGui.QPainter(self)
  s = self.style()
  s.drawPrimitive(QtGui.QStyle.PE_Widget, opt, p, self)
Sudatorium answered 10/7, 2013 at 14:35 Comment(1)
Which imports do I need for this? I get AttributeError: module 'PyQt5.QtGui' has no attribute 'QStyleOption' if I import QtGui and from QtWidgets import QStyleOption.Holstein
P
8

I had same problem with pyside. I post my solution just for completeness. It is almost like in PyQt as Pieter-Jan Busschaert proposed. only difference is you need to call initFrom instead of init

def paintEvent(self, evt):
    super(FreeDockWidget,self).paintEvent(evt)
    opt = QtGui.QStyleOption()
    opt.initFrom(self)
    p = QtGui.QPainter(self)
    s = self.style()
    s.drawPrimitive(QtGui.QStyle.PE_Widget, opt, p, self) 

One other thing you need to make sure is that you define your custom widget in your css file the following way:

FreeDockWidget{...}

and not like often recommended

QDockWidget#FreeDockWidget{...}
Purvis answered 29/4, 2014 at 8:34 Comment(0)
I
7

Calling setAttribute(Qt::WA_StyledBackground, true) for the custom widget worked for me.

Isleen answered 8/3, 2018 at 17:58 Comment(1)
How to set style for :hover that way?Villus
C
2

Setting Qt::WA_StyledBackground to true only works if you remember to add Q_OBJECT to your class. With these two changes you don't need to reimplement paintEvent.

Clannish answered 24/9, 2019 at 19:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.