2

Qt QComboBox之setEditable和currentTextChanged及其源码分析 - 师从名剑山

 2 years ago
source link: https://www.cnblogs.com/codegb/p/16103788.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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";
        ...
    }

输出的结果为:

这也就表明了,我是在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的原因。
    至此,根据源码的分析,所有发生的事情,都能够正常的解释通了。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK