因为项目需要兼容Windows XP系统,一番选型后,采用了QT的QWidget+QSS方式实现UI。自然,设置某个QWidget组件的背景色是一个极为常见的需求。但就在这个及其简单的需求里,作者也踩到坑,费了半天时间,故有此文记录。
这还不简单?基础功能,预期半小时解决吧,一顿操作猛如虎,得到如下代码:
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组件处于一个本身有背景色的组件内(这背景色是不是很熟悉)。
因为背景的蓝色组件是用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设置的间隔,背景色被消除了,透视到下一层组件的颜色。
考虑到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);
}
前面尝试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
类。我们看下效果:
符合预期,能得到正常的背景色效果。
我们给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);
}
在开发UI过程中遇到需要给自定义widget设置背景色的需求,然而简单实现发觉有layout的addSpacing引起的背景色消除透传问题。本文进行了4种方案的尝试。其中尝试1采用QPalette设置背景色,试验证明跟qss方式等效,无法解决问题。方案2采用背景色wrapper类和UI用的inner widget类实现。方案3在前一个方案基础上优化,不许再额外定义inner widget。方案4是最好的一个方案,在widget设置WA_StyledBackground
属性,解决问题。