Qt中QComboBox下拉列表(popup)位置与样式的控制
转载请注明来源:
Qt中的QComboBox在不同平台下有所差异(主要是不可编辑的QComboBox),如下样式A和样式B:
左边为样式A为“fusion”样式,在ubuntu下的样式似乎就是这个,它的特点是下拉列表会把⽂字框和箭头盖住。右边样式B
为“windowsvista”样式,它是win10下Qt的默认样式,其特点是下拉列表会显⽰在⽂字框和箭头下⽅,所以不会盖住⽂字框和箭头。
那么如何在不改变全局样式(即指定“fusion”样式或“windowsvista”样式)的情况下对QComboBox的样式进⾏控制,使其显⽰为样
式A或样式B呢?如果全局样式使得QComboBox为样式A,⽽你想使⽤样式B,这种情况是⽐较简单的,使⽤qss即可满⾜需求,对
QComboBox调⽤setStyleSheet(“QComboBox {combobox-popup: 0;}”)后,样式就变成了这样:
虽然和样式B不完全⼀样,但再使⽤qss对⽂字框和箭头样式微调⼀下就可以达到和样式B差不多的效果。
⽽如果全局样式是样式B,想要把QComboBox改成样式A,那就要费⼀番周折了,当然了,如果你看到了我这篇博客,就不⽤费那么多周
折了。
⾸先,类似地,我们对QComboBox对象调⽤setStyleSheet,这⾥将combobox-popup设为1,得到了如下样式:
每⼀项左边空出了⼀⼤块空⽩,被选中的项还多了⼀个对勾图标。如果你对这种样式还算满意的话,就不⽤往下看了。如果不满意的话,我
们还要想办法调整⼀下样式。
我⼀开始想要使⽤qss来进⾏调整,然⽽死活调不动,最多也就能调整⼀下⽂字的颜⾊和背景⾊啥的,调整⼤⼩时要么调不了,要么各种错
位,想要调整图标,更是根本没到⽅法。
那么该如何调整呢,答案是使⽤QStyle类来控制样式,这个类可以精确的控制每个控件该如何绘制。各个平台下默认样式不同,实际上就是
因为不同平台下默认使⽤了不同的QStyle类的派⽣类,如qfusionstyle.cpp、qwindowsstyle.cpp等,不过这些类我们是不能直接使⽤
的,只能使⽤setStyle(“fusion”)这样的⽅式,如果我们⾃⼰实现了⼀个QStyle派⽣类,则可以使⽤setStyle(new MyStyle)这样的⽅式
来直接使⽤我们的类。
我们需要做的就是实现⼀个⾃⼰的QStyle类,这个类相当复杂,就不介绍了,我们需要做的就是重新实现它的drawControl⽅法。⼀般在
实现⾃⼰的样式时,是不会去继承QStyle类的,它是⼀个纯虚类,如果你要继承它的话,需要实现所有的⽅法。⼀般都是继承
QProxyStyle类,这个类的样式⼀般就是当前平台下的默认样式。对于我们想要控制的内容,我们⾃⼰实现,对于不想控制的内容,直接使
⽤⽗类的⽅法(即QProxyStyle类)就可以了。
drawControl⽅法的函数签名如下:
void QProxyStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const
当绘制⼀个元素时,就会调⽤这个⽅法(不是所有元素都会调⽤这个⽅法),绘制选项保存在option中,绘制所⽤QPainter为painter,最
后,这个元素所在控件为widget。
那么,我们要控制的下拉列表是什么元素呢,答案是CE_MenuItem。这⾥还要提到另⼀个⽅法styleHint,这个⽅法也是⽤来控制样式的:int QProxyStyle::styleHint(StyleHint hint, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR, QStyleHintReturn *returnData =
在qss中使⽤“combobox-popup: 1”时,和调⽤styleHint⽅法hint参数为SH_ComboBox_Popup时返回true效果是⼀样的(因此你可以不使⽤qss将样式B修改为样式A,⽽是重新实现styleHint⽅法),SH_ComboBox_Popup在⽂档中的描述为:
Allows popups as a combobox drop-down menu.
也就是将下拉列表变成⼀个menu。所以此时下拉列表的绘制和⼀个菜单的绘制是⼏乎相同的。
不多说了,代码如下,注释解释得⽐较清楚了:
void MyStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
const QWidget *widget) const
{
qDebug() << element;
switch (element)
{
case CE_MenuItem:
// 进⾏常规的检查,确定element与opt的类别是相符的
if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt))
{
borderbox// 判断控件是否为QComboBox,如果是,则使⽤我们指定的实现,如果不是(可能只是绘制了⼀个普通的菜单),则使⽤默认的实现
if (qobject_cast<const QComboBox*>(widget) ||
(opt->styleObject && opt->styleObject->property("_q_isComboBoxPopupItem").toBool()))
{
// 我的实现做了简化,只处理了每⼀项的⽂本和背景,对于border、margin和padding等,有需要的可以⾃⼰去实现相应的功能
// 是否为被选择状态,如果被选择,则需要使⽤不同的背景⾊和⽂本⾊
bool act = menuitem->state & State_Selected;
// 绘制背景
QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
p->fillRect(menuitem->rect.adjusted(0, 0, -1, 0), fill);
// 如果⽂本⾮空,则进⾏绘制
if (!menuitem->text.isEmpty()) {
p->save();
// 设置⽂本⾊
p->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color());
int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
QFont font = menuitem->font;
p->setFont(font);
// 绘制在参数指定的矩形中
p->drawText(menuitem->rect, text_flags, menuitem->text);
p->restore();
}
}
else
{
QProxyStyle::drawControl(element,opt,p,widget);
}
}
break;
default:
QProxyStyle::drawControl(element,opt,p,widget);
}
}
最终得到的效果如下:
左侧的空⽩和对勾图标都不见了,和样式A已经⾮常像了,如果还有什么不满意的,⾃⼰在drawControl⽅法中实现吧。
本⽂采⽤ CC-BY 协议进⾏授权
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论