서론
최근에 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는 현재 입력된 텍스트를 반영합니다. 편집 불가 상태에서는 현재 선택된 항목이나 빈 문자열이 됩니다.
따라서 편집 불가 상태에서 항목을 추가하면 현재 선택 항목이 변경되어 시그널이 발생한다고 추측했습니다.
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
이는 addItem 호출 직후 슬롯이 트리거됨을 보여줍니다. 그러나 addItems는 currentTextChanged를 발생시키지 않았는데, 그 이유를 알아보기 위해 소스 코드를 분석했습니다.
소스 코드 분석
// 호출 구조
1. QComboBox::addItem(int , const QIcon &, const QString &, const QVariant &)
----> QStandardItem::setData(const QVariant &, int )
----> QStandardItemModelPrivate::itemChanged(QStandardItem *, const QVector<int> &)
----> 시그널: QStandardItemModel::dataChanged(QModelIndex,QModelIndex)
슬롯: QComboBox::_q_dataChanged(QModelIndex,QModelIndex)
----> if (lineEdit) lineEdit->setText();
else emit currentTextChanged(QString);
2. QComboBox::addItems(QStringList)
----> QComboBox::insertItems(int, QStringList)
----> QStandardItem::insertRows(int, QList<QStandardItem*>)
----> QStandardItemPrivate::insertRows(int, QList<QStandardItem*>)
----> rowsAboutToBeInserted(QStandardItem *, int , int)
----> QAbstractItemModel::beginInsertRows(const QModelIndex &, int , int)
----> 시그널: rowsAboutToBeInserted(...)
슬롯: QAbstractItemModelPrivate::rowsAboutToBeInserted(...)
----> QStandardItemModelPrivate::rowsInserted(QStandardItem *, int , int)
----> QAbstractItemModel::endInsertRows()
----> QAbstractItemModelPrivate::rowsInserted(...)
----> 시그널: QAbstractItemModel::rowsInserted(...)
슬롯: QComboBox::_q_rowsInserted(...)
-
addItem 동작
addItem은 내부적으로insertItem을 호출합니다.insertItem은 모델이 기본QStandardItemModel인 경우 데이터를 설정합니다:- setData 경로: 데이터 설정 과정에서
dataChanged시그널이 발생합니다. QComboBox는 이 시그널을_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(); }여기서
currentTextChanged시그널이 발생하지만, 조건은 다음과 같습니다: - 현재 인덱스가 데이터 변경 범위 내에 있어야 함. - 편집 불가 상태(lineEdit가 없음)인 경우에만 시그널을 방출함.
따라서setEditable(true)로 설정하면lineEdit가 존재하므로else분기가 실행되지 않아 시그널이 발생하지 않습니다. - setData 경로: 데이터 설정 과정에서
-
addItems 동작
addItems는insertItems를 통해insertRows를 호출합니다. 이 함수는rowsInserted시그널을 방출하며, QComboBox는 이를_q_rowsInserted()슬롯에 연결합니다. 해당 슬롯에서 첫 번째 항목이 추가될 때 현재 인덱스를 0으로 설정하여currentIndexChanged시그널을 발생시킵니다. 그러나addItems는 여러 항목을 한 번에 추가하므로, 첫 번째 항목 이후에는 인덱스가 변경되지 않아currentTextChanged가 발생하지 않습니다.
이 분석을 통해 addItem이 currentTextChanged를 트리거하는 이유와 편집 가능 상태에서 시그널이 발생하지 않는 이유를 명확히 이해할 수 있었습니다. 또한 addItems가 시그널을 발생시키지 않는 이유도 설명됩니다.