Qt QComboBox之setEditable和currentTextChanged及其源码分析

Qt QComboBox之setEditable和currentTextChanged以及其源码分析

前言

最近做了一个QComboBox里有选项,然后选中选项之后就会自动触发条件搜索。然后我发现,在我初始化comboBox时,由于信号连接的原因会触发这个currentTextChanged信号。代码大致如下:

connect(ui->comboBox, &QComboBox::currentTextChanged,
        this,         &CountryType::slot_pageSearch);


void Country::setComboBox()
{
    QStringList content;
    int maxLen = 0;
    QFont font;
    font.setFamily("Microsoft YaHei");
    font.setPixelSize(16);
    QFontMetrics fontMetrics(font);

    QString command = jointQueryComboBoxTextCommand();
    QList<QStringList> texts = m_oracle->runSelectCommand(command);
    foreach (QStringList text, texts) {
        QString item = text.value(0)+"-"+text.value(1);
        content.push_back(item);
        // 计算最大宽度
        maxLen = maxLen > fontMetrics.boundingRect(item).width() ?
                    maxLen :
                    fontMetrics.boundingRect(item).width();
    }

    // comboBox的宽度为 文字的最大宽度 + 下拉箭头的宽度 + 文字两边的间距
    ui->comboBox->setMinimumWidth(maxLen + 38 + 8);
    ui->comboBox->clear();
    // 填充一个空选项作为筛选所有
    ui->comboBox->addItem("");
    ui->comboBox->addItems(content);
}

void Country::search()
{
    setComboBox();
}

问题的出现

在我每一次对页面进行切换的时候,我发现这个search都会触发这个slot_pageSearch槽函数,然后执行条件搜索。
但是我今天突发奇想,我是不是应该让使用者能够手动的输入这个条件呢,于是我setEditable(true);,将编辑打开了。
也就是:

ui->comboBox->setEditable(true);

在设置了这个之后,我惊奇的发现,并没有像之前一样会触发slot_pageSearch这个槽函数。

问题分析

因为我只修改了ui->comboBox->setEditable(true);,所以我肯定,问题就是发生在这个地方,于是我在网上搜索与这个问题有关联的答案。

最后,我还是在QT的官方文档中对于currentText这个部分的介绍中,找到了问题的原因。
在这里插入图片描述大概意思就是说,当你将QComboBox设置成可编辑的状态时(setEditable(true)),currentText就是当前的框内显示的文字。当不为可编辑的状态时,currentText就是当前的选项或者是一个空的字符串。

所以我猜想,设置成不可编辑状态时,由于我进行了一个条目的添加,所以就将当前的选项改变了。

currentTextChanged信号触发

于是我在正常的流程下,添加了一些打印语句,用于证实我的猜想。

    void Country::setComboBox() {
        ...
        // 填充一个空选项作为筛选所有

        qDebug() << "1";
        ui->comboBox->addItem("");
        qDebug() << "2";
        ui->comboBox->addItems(content);
        qDebug() << "3";
        ...
    }

    void CountryType::slot_pageSearch()
    {
        ...
        qDebug() << "111";
        ...
    }

输出的结果为:

    1
    111
    2
    3

这也就表明了,我是在setItem之后,就会触发槽函数。但是具体为啥是这样的,为啥addItems不会触发currentTextChanged呢?
所以我带着问题,决定去源码里找答案

源码分析

// 代码调用结构
1. QComboBox::addItem(int , const QIcon &, const QString &, const QVariant &)
----> QStandardItem::setData(const QVariant &, int )
	  ----> QStandardItemModelPrivate::itemChanged(QStandardItem *, const QVector<int> &)
			----> signal: QStandardItemModel::dataChanged(QModelIndex,QModelIndex) slot: QComboBox::_q_dataChanged(QModelIndex,QModelIndex)
				  ----> if (lineEdit) lineEdit->setText(); else emit currentTextChanged(QString);
  
2. QComboxBox::addItems(QStringList)
----> QComboxBox::insertItems(int, QStringList)
	  ----> QStandardItem::insertRows(int, QList<QStandardItem*>)
			----> QStandardItemPrivate::insertRows(int, QList<QStandardItem*>)
				  ----> rowsAboutToBeInserted(QStandardItem *, int , int)
						----> QAbstractItemModel::beginInsertRows(const QModelIndex &, int , int )
							  ----> signal: rowsAboutToBeInserted(const QModelIndex &, int , int ) slot: 
							  ----> QAbstractItemModelPrivate::rowsAboutToBeInserted(const QModelIndex &, int , int )
				  ----> QStandardItemModelPrivate::rowsInserted(QStandardItem *, int , int )
						----> QAbstractItemModel::endInsertRows()
							  ----> void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &, int , int )
							  ----> signal: QAbstractItemModel::rowsInserted(QModelIndex,int,int) slot: QComboBox::_q_rowsInserted(QModelIndex,int,int)
  1. 首先,我从最简单的来入手——addItem
    请添加图片描述
    在上面这张图里可以知道,addItem调用的是insertItem这个函数,这个是用来插入条目的一个函数;

然后就是insertItem这个函数,我们可以看到,这个函数会根据你的是不是原始的QStandardItemModel,是的话,就会去设置数据;
请添加图片描述
这里有两个分支,

  • setData
    在这里插入图片描述
    在这里插入图片描述
    随着函数的调用过程,信号dataChanged被发射了,同时,在qcombobox.cpp中有对这个信号的连接,
    在这里插入图片描述在这里插入图片描述
    我们进到这个_q_dataChanged()函数里面,
    在这里插入图片描述
    这里有一段代码:
 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
        const QString text = q->itemText(currentIndex.row());
        if (lineEdit) {
            lineEdit->setText(text);
            updateLineEditGeometry();
        } else {
            emit q->currentTextChanged(text);
        }
        q->update();
#ifndef QT_NO_ACCESSIBILITY
        QAccessibleValueChangeEvent event(q, text);
        QAccessible::updateAccessibility(&event);
#endif
    }

在这里,我们就找到了我们的目标currentTextChanged这个信号。但是发射这个信号的前提条件是:

  • currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()也就是说,当前的下标的值需要在范围内
  • 当前的状态必须是不可编辑状态才会发射信号

所以这里就是设置成可编辑状态后,不会触发信号的原因;

  • insertRow
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    现在关键的函数要来了,这个函数bool QStandardItemPrivate::insertRows,在待会addItems这个函数分析时也会用到。
    在这里插入图片描述
    在这里插入图片描述
    在这里发射了这个rowInserted()信号,这个信号,又在QComboBox中进行了槽函数的连接
    在这里插入图片描述
    在这里插入图片描述
    所以在这个函数里面,如果是插入的第一个条目,就会把当前的下表设置成0,这时候就会触发另外一个信号currentIndexChanged
    在这里插入图片描述
    至此,我们就能明白,为什么addItem会触发currentTextChanged的信号。同时,如果设置成可编辑状态,又是为何不会触发currentTextChanged
  1. 其次,我们从第二个函数,也就是addItems
    在这里插入图片描述
    在这里插入图片描述
    到这里,就能发现,这个部分调用的还是这个bool QStandardItemPrivate::insertRows,同样根据条件判断,currentIndex = 0而其他两个分别为1和添加条目的数量,很显然不符合要求。
    所以这也就是为什么addItems不会触发currentIndexChange的原因。
    至此,根据源码的分析,所有发生的事情,都能够正常的解释通了。