QT设置QWidget背景色踩坑记

这里写自定义目录标题

  • 背景
  • 需求
  • 问题
  • 尝试1
  • 尝试2
  • 尝试3
  • 尝试4
  • 总结

背景

因为项目需要兼容Windows XP系统,一番选型后,采用了QT的QWidget+QSS方式实现UI。自然,设置某个QWidget组件的背景色是一个极为常见的需求。但就在这个及其简单的需求里,作者也踩到坑,费了半天时间,故有此文记录。

需求

以实现一个视频的缩略图按钮为例,具体来说是实现如下效果:
QT设置QWidget背景色踩坑记_第1张图片

问题

这还不简单?基础功能,预期半小时解决吧,一顿操作猛如虎,得到如下代码:

VideoThumb::VideoThumb(QString text, QWidget *parent) : QWidget(parent)
{
    auto layout = new QVBoxLayout();
    setFixedSize(318,179);
    setStyleSheet("background-color: #333333;");
    setProperty("type", QVariant("video_thumb"));
    _label = new QLabel();
    _label->setText(text);
    _label->setStyleSheet("color:#FFFFFF;font-size:16px;");
    QPixmap play_img(":/images/black_play.png"); #不要吐槽这种非透明抠图,交互稿没人给原Icon
    auto play_label = new QLabel();
    play_label->setPixmap(play_img);

    layout->addSpacing(62);
    layout->addWidget(_label);
    layout->addSpacing(19);
    layout->addWidget(play_label);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->setAlignment(_label, Qt::AlignHCenter);
    layout->setAlignment(play_label, Qt::AlignHCenter);
    layout->addStretch();
    setLayout(layout);
}

运行一看立刻跪了,只有每个子控件自己占据的地方有背景色。这里为了对比强烈,我整个VideoThumb组件处于一个本身有背景色的组件内(这背景色是不是很熟悉)。
QT设置QWidget背景色踩坑记_第2张图片

尝试1

因为背景的蓝色组件是用QPalette设置的颜色,自然想到是不是QStyle的方式不生效,改用QPalette试试:

VideoThumb::VideoThumb(QString text, QWidget *parent) : QWidget(parent)
{
    // 改用palette設置顔色
    QPalette pal(this->palette());
    pal.setColor(QPalette::Background, QColor("#333333"));
    this->setAutoFillBackground(true);
    this->setPalette(pal);
    
	... 
	// 后续代码同前面
}

结果依旧,仍然只有子组件(QLabel)有背景色,其他通过Layout的addSpacing设置的间隔,背景色被消除了,透视到下一层组件的颜色。

尝试2

考虑到VideoThumb所在的蓝色组件设置的颜色可以透传过来,自然想到那是不是可以再封装一层组件专门用于填充颜色,VideoThumb不设置颜色直接透传用底下的颜色。很快就有了下面的代码:

// 专门提供背景色的一个Widget组件
BgColorWrapper::BgColorWrapper(QString colorStr, QWidget* widget, 
	QWidget* parent):QWidget(parent)
{
    stack_widget = new QStackedWidget();

    // 设置颜色
    QPalette pal(this->palette());
    pal.setColor(QPalette::Background, QColor(colorStr));
    stack_widget->setAutoFillBackground(true);
    stack_widget->setPalette(pal);

	// 添加外部传入的widget
    stack_widget->addWidget(widget);

    auto layout = new QVBoxLayout();
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(stack_widget);
    layout->addStretch();
    setLayout(layout);
}

注意上面这个类因为QStackedWidget 只支持addWidget,不支持addLayout,所以外面必须传入一个widget对象,不能只传递布局。

下面是真正实现组件布局的VideoThumbInner

VideoThumbInner::VideoThumbInner(QString text ,QWidget *parent): QWidget(parent)
{
        auto layout = new QVBoxLayout();
        layout->setMargin(0);
        layout->setSpacing(0);

        this->setFixedSize(318,179);
        setProperty("type", QVariant("video_thumb_inner"));
        _label = new QLabel();
        _label->setText(text);
        _label->setStyleSheet("color:#FFFFFF;font-size:16px;");
        QPixmap play_img(":/images/black_play.png");
        auto play_label = new QLabel();
        play_label->setPixmap(play_img);

        layout->addSpacing(62);
        layout->addWidget(_label);
        layout->addSpacing(19);
        layout->addWidget(play_label);

        layout->setAlignment(_label, Qt::AlignHCenter);
        layout->setAlignment(play_label, Qt::AlignHCenter);

        layout->addStretch();
        setLayout(layout);
}

好了,辅助类都定义好了,下面组合一个专用于背景色的widget和组件布局用的inner对象:

VideoThumb::VideoThumb(QString text, QWidget *parent) : QWidget(parent)
{
    auto inner = new VideoThumbInner(text);
    // 把真正布局的VideoThumbInner传给专门用于提供北京色的BgColorWrapper。
    auto wrapper = new BgColorWrapper("#333333", inner);  
    auto layout = new QVBoxLayout();
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(wrapper);
    setLayout(layout);
}

这一次我们看一下效果:
QT设置QWidget背景色踩坑记_第3张图片
符合预期。

尝试3

前面尝试2里提到,为了套如一层专门用于提供背景色的widget,除了本来就需要定义的VideoThumb,布局本身还需要特意声明成一个inner类VideoThumbInner,能不能在背景色类BgColorWrapper里生成一个临时的QWidget对象,然后就可以接收layout参数。变成如下的代码:

BgColorWrapper::BgColorWrapper(QString colorStr, QLayout* target_layout, 
		QWidget* parent):QWidget(parent)
{
    stack_widget = new QStackedWidget();

    QPalette pal(this->palette());
    pal.setColor(QPalette::Background, QColor(colorStr));
    stack_widget->setAutoFillBackground(true);
    stack_widget->setPalette(pal);

    // StackedWidget只能添加widget不支持直接添加layout,所以我们临时创建一个丢进去
    auto widget = new QWidget();
    widget->setLayout(target_layout);

    stack_widget->addWidget(widget);

    auto layout = new QVBoxLayout();
    layout->setMargin(0);
    layout->setSpacing(0);

    layout->addWidget(stack_widget);
    layout->addStretch();
    setLayout(layout);
}

这样我们在VideoThumb类里就能直接声明真正的布局,无需单独为了背景色定义一个VideoThumbInner类。我们看下效果:
QT设置QWidget背景色踩坑记_第4张图片
符合预期,能得到正常的背景色效果。

尝试4

我们给Widget组件设置WA_StyledBackground属性。则无需专门的背景色用的widget,也无需特意定义一个inner类。

VideoThumb::VideoThumb(QString text, QWidget *parent) : QWidget(parent)
    auto layout = new QVBoxLayout();
    setFixedSize(318,179);
    setAttribute(Qt::WidgetAttribute::WA_StyledBackground);  // 重要
    setStyleSheet("background-color: #333333;");
    setProperty("type", QVariant("video_thumb"));
    _label = new QLabel();
    _label->setText(text);
    _label->setStyleSheet("color:#FFFFFF;font-size:16px;");
    QPixmap play_img(":/images/black_play.png");
    auto play_label = new QLabel();
    play_label->setPixmap(play_img);

    layout->addSpacing(62);
    layout->addWidget(_label);
    layout->addSpacing(19);
    layout->addWidget(play_label);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->setAlignment(_label, Qt::AlignHCenter);
    layout->setAlignment(play_label, Qt::AlignHCenter);
    layout->addStretch();
    setLayout(layout);
}    

效果如下
QT设置QWidget背景色踩坑记_第5张图片

总结

在开发UI过程中遇到需要给自定义widget设置背景色的需求,然而简单实现发觉有layout的addSpacing引起的背景色消除透传问题。本文进行了4种方案的尝试。其中尝试1采用QPalette设置背景色,试验证明跟qss方式等效,无法解决问题。方案2采用背景色wrapper类和UI用的inner widget类实现。方案3在前一个方案基础上优化,不许再额外定义inner widget。方案4是最好的一个方案,在widget设置WA_StyledBackground属性,解决问题。

你可能感兴趣的:(UI开发)