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)
- 首先,我從最簡單的來入手——
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
- 其次,我們從第二個函式,也就是
addItems
到這裡,就能發現,這個部分呼叫的還是這個bool QStandardItemPrivate::insertRows
,同樣根據條件判斷,currentIndex = 0
而其他兩個分別為1和新增條目的數量,很顯然不符合要求。
所以這也就是為什麼addItems
不會觸發currentIndexChange
的原因。
至此,根據原始碼的分析,所有發生的事情,都能夠正常的解釋通了。