QComboBox文字居中的一种解决办法

QComboBox文字居中的一种解决办法


本文会尽可能解释一些内容,所以会显得有点长。

关于QComboBox文字的居中,网上提供的主流方案是,通过 QComboBox::setEditable 设置为可编辑状态,再获取 QComboBox::lineEdit 设置为只读并居中文字。

该方案的缺点在于改变了不可编辑状态下QComboBox的交互行为,例如,点击文本区域不会弹出列表,原因是QComboBox的一些交互行为依赖于 editable 属性,虽然可以通过自定义鼠标事件等方式完善,但不同平台上的交互又有细微的区别。

对此,这里提出另一种解决方案,尽可能避免影响QComboBox原生的交互,有点复杂,从思路上,也许能解决更多的类似问题。

一些基本原理

Qt虽然提供了 style sheet 方式设置控件样式,但Qt并没有将对应的QStyleSheetStyle公开。Qt的基础控件都是使用了QStyle接口绘制的,在使用 style sheet 时,大部分样式,如颜色、字体、边框等,在绘制时会从样式表中读取并覆盖QStyleOption,用户代码无法感知到该过程,特别是当设置了 hover、pressed 动作下的样式时。所以绘制的过程只能交给QStyle去绘制控件。

不过,QStyle接口并不关心QPainter参数的绘制目标,例如可以通过 QWidget::render 来截取控件内容。Qt也没有限制在A控件上绘制B控件。

另外,尽管Qt文档里说明了不同控件的style sheet应用范围,但实际样式定义了就会存在,即使样式对某些控件本身无效。

解决方案

所幸,QComboBox和QPushButton的绘制事件比较简单,不涉及私有接口,可以借用一些源码里的绘制逻辑,所以重写 QComboBox::paintEvent

	// 该代码是Qt默认实现,直接拷贝一份
    QStylePainter painter(this);
    painter.setPen(palette().color(QPalette::Text));

    // draw the combobox frame, focusrect and selected etc.
    QStyleOptionComboBox opt;
    initStyleOption(&opt);
    painter.drawComplexControl(QStyle::CC_ComboBox, opt);

    if (currentIndex() < 0)
        opt.palette.setBrush(QPalette::ButtonText, opt.palette.brush(QPalette::ButtonText).color().lighter());

    // draw the icon and text
    //painter.drawControl(QStyle::CE_ComboBoxLabel, opt);   //不需要再绘制文本内容
	// 以上代码是Qt默认实现
	
    painter.end();
	
	// 在文本区域绘制按钮文本
    QPainter painter2(this);
    QStyleOptionButton buttonOpt;
    buttonOpt.initFrom(this);	// 主要是一些窗口状态,hover、enable等
    QRect editRect = this->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
    buttonOpt.rect = editRect;	// 设置按钮区域为QComboBox的文本区域
    buttonOpt.text = opt.currentText;	// 设置文本
    // 绘制QStyle::CE_PushButtonLabel
    this->style()->drawControl(QStyle::CE_PushButtonLabel, &buttonOpt, &painter2, this);	

需要注意的是:

  • 最后通过 QStyle::drawControl 仅绘制 QStyle::CE_PushButtonLabel ,是由于 QStyle::CE_PushButton 会绘制边框等样式,如果QComboBox设置了边框,会重复画一遍。
  • QStyle::drawControl 最后一个参数必须传QComboBox对象,样式需要通过窗口来获取。
  • 可以在QComboBox的样式中设置 text-align 来控制文本的对齐,虽然对QComboBox本身无效,但对QPushButton有效
  • 通过设置按钮文本绘制区域来控制位置,但可能会存在某些特殊的样式没有计算在内。

结果截图

hover状态下的样式同样能够保留,由于未增加其他子控件,交互逻辑也没有变化。

在这里插入图片描述

解决过程

一开始考虑使用组合的方式,在QComboBox上覆盖QLabel来实现居中,但QStyle在应用的过程中,针对QComboBox子控件的样式做了过滤,样式并不会生效。Qt有个私有的属性 Qt::WA_StyleSheet 可以跳过过滤,但在重设 style sheet 时可能引起异常。

还有一种比较麻烦的方案是,默认实现里,painter.drawControl(QStyle::CE_ComboBoxLabel, opt) 这段执行前,通过字体信息计算当前文本宽度,修改opt的rect属性,使默认的文本绘制刚好显示在中间。

你可能感兴趣的:(Qt常见问题,Qt技术总结,qt,stylesheet,qcombobox,居中,qstyle)