• QComboBox文字居中的几种实现方式


    不知道你有没有遇到过这样的需求,将一个QComboBox文字居中显示。我最近遇到了这样的需要,主要是要在表头中增加可以下拉的列表来进行过滤的功能,这也就要求我们必须将下拉列表的文字居中显示。

    这种需求可能实现的方式会有好多种,但我一直坚信,能用原生的就用原生的,或者在原生的控件之上进行一些特例化的改动,让其满足我们的需求是最简单的方式,并且不会过多的影响样式表设置。

    所以这儿我还是选择了QComboBox,而我们要做的事情就是怎样将他的文字居中显示。

    下拉列表的居中我们都知道是比较好解决的事情,只要进行简单的设置就能满足。

    dynamic_cast<QStandardItemModel*>(this->view()->model())->item(index)->setTextAlignment(Qt::AlignVCenter);
    
    • 1

    顶多,我们再加个循环就能搞定了。

    void ComboBox::setTextAlignment(Qt::Alignment alignment) const
    {
    	auto* model = dynamic_cast<QStandardItemModel*>(this->view()->model());
    	if (Q_NULLPTR == model)
    	{
    		return;
    	}
    
    	for (int index = 0, size = model->rowCount(); index < size; ++index)
    	{
    		if (Q_NULLPTR != model->item(index))
    		{
    			model->item(index)->setTextAlignment(alignment);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    比较难的是怎么让没有下拉的时候它的文字居中显示。

    我下面的所有例子都是建立在新建类 ComboBox 继承自 QComboBox 之上的,也就是这样。

    class ComboBox : public QComboBox
    {
    
    }
    
    • 1
    • 2
    • 3
    • 4

    1、 借用其本身的QLineEdit

    可能我们首先想到的会是这个方式。大家都知道,QLineEdit 是可以将文字设置为居中显示的。而QComboBox 中,其实是有一个子控件就是 QLineEdit 的。但是这个子控件被构造的时机是我们必须设置 QComboBox 为可编辑状态。

    this->setEditable(true);
    this->lineEdit()->setReadOnly(true);
    this->lineEdit()->setAlignment(Qt::AlignCenter);
    
    • 1
    • 2
    • 3

    上面的这几行代码其实已经可以实现文字居中显示了。问题是:

    • 太丑了,这样设置之后的样式可能跟我们的预期会有点差距,但如果你只是自己玩玩,倒也不是不行。
    • 下拉列表的触发方式被改变了,这样就只能点击 QComboBox 后面的那个箭头触发下来列表的 popup 了,不管你是否设置这个QLineEdit是只读,而正常的结果是 QComboBox 的任何一个点都能触发。造成了操作上的不便。

    当然,第二个问题是好解决的,因为我们在很多的地方可能都接触过事件的过滤,那我们就可以过滤触发在 QLineEdit 上的鼠标单点的事件来进行手动的 popup 。过滤的事件首先应该是鼠标事件,其次是鼠标的左键被 release 的状态,这样才能正常的模拟我们的鼠标单击。

    bool ComboBox::eventFilter(QObject* obj, QEvent *event)
    {
        if (event->type() == QEvent::MouseButtonRelease &&  dynamic_cast<QMouseEvent*>(event)->button() == Qt::LeftButton)
        {
            showPopup();
        }
    
        return QObject::eventFilter(obj, event);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    this->lineEdit()->installEventFilter(this);
    
    • 1

    就算进行了鼠标事件的过滤,模拟了鼠标单击,但我们还有一个问题,就是第一个的样式,这是我们用这种方式解决不了的。要解决这个问题,也就引出了我们下面的第二种方式。
    看下实现的效果:
    在这里插入图片描述

    2、 借用外来的 QLineEdit

    这种方式其实跟第一种是很像的,区别只是我们用了一个外来的 QLineEdit 代替了 QComboBox 本身存在的 QLineEdit。当然,这种方式下不用将QComboBox 设置为可编辑状态,因为我们不会用到他自身的 QLineEdit。

    auto lineEdit = new QLineEdit();
    lineEdit->setReadOnly(true);
    lineEdit->installEventFilter(this);
    
    this->setQLineEdit(lineEdit);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样的效果跟我们第一种的效果是完全不一样的。也基本上满足了我们的需求。如果只是浅尝辄止一下,那到这儿也就差不多了。

    第二种的效果:
    在这里插入图片描述

    3、 重新绘制

    为什么我说如果只是简单的进行一下测试,到第二种方法的时候就可以结束了,因为这种方式,会稍微有点难度,但难度也不大。

    如果你经常会时不时地去瞄一眼Qt的源码,或许你就会发现很多你本来不知道,但非常好用的东西。比如下面这个QComboBoxpaintEvent 函数.

    void QComboBox::paintEvent(QPaintEvent *)
    {
         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);
    
         // draw the icon and text
         painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    看看这简单的几行代码,外加必要的注释,基本上已经让你将这个函数吃透了。

    最后一行代码。

    // draw the icon and text
    painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
    
    • 1
    • 2

    他的作用是绘制 icon 和 text。这特么不就是我们需要的吗?也就是说,我们只需要修改这一行代码,就能满足我们绘制需求了。所以就有了下面的这个重写函数。

    void ComboBox::paintEvent(QPaintEvent* e)
    {
        QStylePainter painter(this);
        painter.setPen(palette().color(QPalette::Text));
    
        QStyleOptionComboBox opt;
        initStyleOption(&opt);
        painter.drawComplexControl(QStyle::CC_ComboBox, opt);
    
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个函数上面的部分我们保持不变,并且还需要保持一种方式就是当QComboBox可编辑的时候需要跟以前一样。而我们通过对 QStyleOptionComboBox 类的详细了解发现,这个类里面是有一个 editable 成员变量的。

    class Q_WIDGETS_EXPORT QStyleOptionComboBox : public QStyleOptionComplex
    {
    public:
        enum StyleOptionType { Type = SO_ComboBox };
        enum StyleOptionVersion { Version = 1 };
    
        bool editable;
        QRect popupRect;
        bool frame;
        QString currentText;
        QIcon currentIcon;
        QSize iconSize;
    
        QStyleOptionComboBox();
        QStyleOptionComboBox(const QStyleOptionComboBox &other) : QStyleOptionComplex(Version, Type) { *this = other; }
    
    protected:
        QStyleOptionComboBox(int version);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    偷下懒,如果 QComboBox 是可编辑状态,就还是按照原先的那种方式绘制去吧。

    if(opt.editable)
        painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
    
    • 1
    • 2

    如果不是可编辑状态:

    首先按照QComboBox的大小,获取到文字显示的矩形框,这个矩形框获取的过程中是不包含QComboBox最后面的那个下来箭头的。

    QRect editRect = this->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
    
    • 1

    然后绘制一个ButtonLabel,我们都知道,QPushbutton默认是文字居中显示的,所以不用设置其他的。最后进行绘制。

    QStyleOptionButton buttonOpt;
    buttonOpt.initFrom(this);
    buttonOpt.rect = editRect;
    buttonOpt.text = opt.currentText;
    buttonOpt.icon = opt.currentIcon;
    buttonOpt.iconSize = opt.iconSize;
    painter.drawControl(QStyle::CE_PushButtonLabel, buttonOpt);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面是我们用了同一个绘制 QStylePainter 的对象 painter 实现了绘制过程,但让,还有另外一种绘制方式,就是利用 QStyle 。

    painter.end();
    
    QPainter pt(this);
    QStyleOptionButton buttonOpt;
    buttonOpt.initFrom(this);
    QRect editRect = this->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
    buttonOpt.rect = editRect;
    buttonOpt.text = opt.currentText;
      buttonOpt.icon = opt.currentIcon;
      buttonOpt.iconSize = opt.iconSize;
    this->style()->drawControl(QStyle::CE_PushButtonLabel, &buttonOpt, &pt, this);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这种方式现实的效果:
    在这里插入图片描述

    完整的函数是这样的:

    void ComboBox::paintEvent(QPaintEvent* e)
    {
        QStylePainter painter(this);
        painter.setPen(palette().color(QPalette::Text));
    
        QStyleOptionComboBox opt;
        initStyleOption(&opt);
        painter.drawComplexControl(QStyle::CC_ComboBox, opt);
    
        if(opt.editable)
        {
            painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
            return;
        }
    
        QRect editRect = this->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
    
        QStyleOptionButton buttonOpt;
        buttonOpt.initFrom(this);
        buttonOpt.rect = editRect;
        buttonOpt.text = opt.currentText;
        buttonOpt.icon = opt.currentIcon;
        buttonOpt.iconSize = opt.iconSize;
    
    #if 1
        painter.drawControl(QStyle::CE_PushButtonLabel, buttonOpt);
    #else
        painter.end();
        QPainter pt(this);
        this->style()->drawControl(QStyle::CE_PushButtonLabel, &buttonOpt, &pt, this);
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    很多时候,我们总会选择一些比较方便,能够轻松解决的方式进行处理,会忽略一些比较耗能的过程,而那些路径比较长的实现方式,大多时候是我们能够学到东西的最好路径。就像选择 QStylePainter 的绘制,会比前两种方式更能让我们了解一个控件或者类的内部。

    QStylePainter 提供了很多的封装的控件绘制方法,为我们提供了便利。也不会影响其他的基础功能。

  • 相关阅读:
    C++二分算法:黑名单中的随机数
    信息学奥赛一本通:2031:【例4.17】四位完全平方数
    【3D建模全流程教学】在 ZBrush、Maya 和 Marvelous Designer 中制作一位逼真的女孩
    3分钟告诉你如何成为一名黑客|零基础到黑客入门指南,你只需要掌握这五点能力
    羧基功能化咪唑基离子液体[CEBIM][PF6]改性普鲁士兰多糖的反应合成
    容联七陌入选沙利文2023中国AI技术变革典型企业
    Delphi Enterprise具有强大视觉设计功能
    SQL注入漏洞(Mysql与MSSQL特性)
    关于log4net的详细使用教程
    这可能是最全的SpringBoot3新版本变化了!
  • 原文地址:https://blog.csdn.net/tax10240809163com/article/details/126539726