您的位置:首页 > Web前端 > CSS

Qt中QComboBox下拉列表(popup)位置与样式的控制

2017-10-04 15:18 701 查看
转载请注明来源:http://blog.csdn.net/imred

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 = Q_NULLPTR) const


在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))
{
// 判断控件是否为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 协议进行授权
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息