自定义小部件的Qt样式表

39

我在当前项目中有几个自定义小部件。 我希望将样式表应用于它们,当我在Qt Creator中这样做时,它似乎有效。 但是,当执行程序时,没有使用样式表。 Qt小部件的样式表正常工作。

有人有什么建议吗?

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);
}
widget 可见 - 我设置了一个工具提示,它向我显示了(它只是与其所在的 QScrollArea 相同的颜色)。

你能展示相应的样式表吗? - alexisdm
1
样式表 = 背景: rgb(170, 0, 255);\n边框: 2px 实线黑色; - jecjackal
9
经过数小时在互联网上的搜索,我发现了这个链接:http://developer.qt.nokia.com/forums/viewthread/7340 该页面引用的代码是样式表正常工作所必需的。 - jecjackal
1
@jecjackal:如果你已经找到了解决方案,请将其提交为答案,以造福未来的观众。 - sam-w
6个回答

67

我遇到了类似的问题,而且使用jecjackal的评论解决了它。正如sjwarner所说,将其作为答案呈现会更加明显。所以我提供它,以便于未来的观看者受益。再次强调,这不是我的答案!感谢jecjackal提供的帮助!

根据Qt的样式表参考文档所述,将CSS样式应用于从QWidget继承的自定义小部件需要在其中重新实现paintEvent()方法:

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

如果不进行此操作,您的自定义小部件将只支持背景、background-clip和background-origin属性。

您可以在这里阅读:Qt样式表参考中的“可设置样式的小部件”章节 - QWidget。(链接)


14

有一个比编写自己的paintEvent更简单的答案:不要继承QWidget,而是继承QFrame,它将立即起作用:

class WidgetUnits : public QFrame
{
    Q_OBJECT
....

1
我发现这是一种更少侵入性的解决问题的方法。 对于每个自定义小部件,只需找到/替换那一行,同时通过在默认构造函数中将QWidget(parent)替换为QFrame(parent)来对cpp文件进行计数。 - Yattabyte

8

为了完整性,PyQt 中也存在同样的问题。您可以通过添加类似代码来将样式表应用于子类化的 QWidget:

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)

我需要导入哪些模块?如果我导入了QtGui和从QtWidgets导入QStyleOption,我会得到“AttributeError:module 'PyQt5.QtGui' has no attribute 'QStyleOption'”错误。 - ewi

8

我曾经遇到过与PySide相关的同样问题。我在这里分享我的解决方案,以供完整性考虑。 它几乎与Pieter-Jan Busschaert提出的PyQt相同,唯一的区别是你需要调用initFrom而不是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) 

另外,您需要确保按以下方式在css文件中定义自定义小部件:

FreeDockWidget{...}

并且不像通常建议的那样

QDockWidget#FreeDockWidget{...}

7

对于自定义小部件,调用 setAttribute(Qt::WA_StyledBackground, true) 对我起了作用。


如何设置 :hover 的样式呢? - Danon

2
Qt::WA_StyledBackground 设置为 true 只有在您记得将 Q_OBJECT 添加到您的类中时才起作用。通过这两个更改,您无需重新实现 paintEvent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接