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).

Dharman
  • 30,962
  • 25
  • 85
  • 135
jecjackal
  • 1,407
  • 2
  • 20
  • 35
  • Could you show the corresponding stylesheet ? – alexisdm Sep 01 '11 at 22:36
  • 1
    style sheet = background: rgb(170, 0, 255);\nborder: 2px solid black; – jecjackal Sep 02 '11 at 00:03
  • 9
    After searching the interwebs for several hours, I found out about this http://developer.qt.nokia.com/forums/viewthread/7340 The code referenced on that page was required for the stylesheet to work. – jecjackal Sep 02 '11 at 00:04
  • 1
    @jecjackal: If you've found a solution, please submit it as an answer to this question for the benefit of any future viewers. – sam-w Nov 11 '11 at 22:50

6 Answers6

66

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.

Nimrod Gileadi
  • 410
  • 1
  • 3
  • 12
Roman Kruglov
  • 3,375
  • 2
  • 40
  • 46
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
....
RAS
  • 8,100
  • 16
  • 64
  • 86
Marco
  • 1,952
  • 1
  • 17
  • 23
  • 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. – Yattabyte Apr 12 '15 at 19:40
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)
  • 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. – ewi May 12 '21 at 14:28
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{...}
Stefan Reinhardt
  • 622
  • 8
  • 17
7

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

mentalmushroom
  • 2,261
  • 1
  • 26
  • 34
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.

Dejan Stankovic
  • 101
  • 2
  • 3