완성기 예제

컴플리터 예제는 모델에서 제공하는 데이터를 기반으로 입력 위젯에 문자열 완성 기능을 제공하는 방법을 보여줍니다.

이 예에서는 사용자 정의 항목 모델인 FileSystemModelQCompleter 객체를 사용합니다. QCompleter 은 항목 모델을 기반으로 완성 기능을 제공하는 클래스입니다. 콤보 상자를 사용하여 모델 유형, 완성 모드 및 대소문자 구분을 선택할 수 있습니다.

리소스 파일

완성기 예제에는 countries.txtwords.txt를 저장하기 위한 리소스 파일이 필요합니다. 리소스 파일에는 다음 코드가 포함되어 있습니다:

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
   <file>resources/countries.txt</file>
   <file>resources/wordlist.txt</file>
</qresource>
</RCC>

파일시스템모델 클래스 정의

FileSystemModel 클래스는 로컬 파일시스템에 대한 데이터 모델을 제공하는 QFileSystemModel 의 서브클래스입니다.

class FileSystemModel : public QFileSystemModel
{
public:
    FileSystemModel(QObject *parent = nullptr);
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};

이 클래스에는 생성자와 data() 함수만 있는데, 이는 드라이브 레이블이 아닌 폴더만 반환하는 QFileSystemModeldata() 함수와 달리 data() 이 표시 역할에 대한 전체 파일 경로를 반환할 수 있도록 하기 위해서만 만들어졌기 때문입니다. 이에 대한 자세한 설명은 FileSystemModel 의 구현에서 확인할 수 있습니다.

FileSystemModel 클래스 구현

FileSystemModel 클래스의 생성자는 parentQFileSystemModel 로 전달하는 데 사용됩니다.

FileSystemModel::FileSystemModel(QObject *parent)
    : QFileSystemModel(parent)
{
}

앞서 언급했듯이 data() 함수를 다시 구현하여 표시 역할에 대한 전체 파일 경로를 반환하도록 합니다. 예를 들어 QFileSystemModel 를 사용하면 뷰에 "Program Files"가 표시됩니다. 그러나 FileSystemModel 을 사용하면 "C:\Program Files"가 표시됩니다.

QVariant FileSystemModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole && index.column() == 0) {
        QString path  = QDir::toNativeSeparators(filePath(index));
        if (path.endsWith(QDir::separator()))
            path.chop(1);
        return path;
    }

    return QFileSystemModel::data(index, role);
}

QCompleter 에서 일치하는 항목을 찾는 데 사용하는 Qt::EditRole 은 변경되지 않습니다.

MainWindow 클래스 정의

MainWindow 클래스는 QMainWindow 의 하위 클래스이며 about(), changeCase(), changeMode(), changeModel(), changeMaxVisible() 등 5개의 프라이빗 슬롯을 구현합니다.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private slots:
    void about();
    void changeCase(int);
    void changeMode(int);
    void changeModel();
    void changeMaxVisible(int);

MainWindow 클래스 내에는 createMenu()modelFromFile() 라는 두 개의 비공개 함수가 있습니다. 또한 세 개의 QComboBox 객체, QCheckBox, QCompleter, QLabel, QLineEdit 등 필요한 개인 위젯을 선언합니다.

private:
    void createMenu();
    QAbstractItemModel *modelFromFile(const QString &fileName);

    QComboBox *caseCombo = nullptr;
    QComboBox *modeCombo = nullptr;
    QComboBox *modelCombo = nullptr;
    QSpinBox *maxVisibleSpinBox = nullptr;
    QCheckBox *wrapCheckBox = nullptr;
    QCompleter *completer = nullptr;
    QLabel *contentsLabel = nullptr;
    QLineEdit *lineEdit = nullptr;
};

메인윈도우 클래스 구현

MainWindow 의 생성자는 부모 위젯이 있는 MainWindow 을 생성하고 프라이빗 멤버를 초기화합니다. 그런 다음 createMenu() 함수가 호출됩니다.

modelComb, modeCombo, caseCombo 세 개의 QComboBox 객체를 설정합니다. 기본적으로 modelComboQFileSystemModel, modeCombo 은 "필터링된 팝업", caseCombo 은 "대소문자 구분 안 함"으로 설정되어 있습니다.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    createMenu();

    QWidget *centralWidget = new QWidget;

    QLabel *modelLabel = new QLabel;
    modelLabel->setText(tr("Model"));

    modelCombo = new QComboBox;
    modelCombo->addItem(tr("QFileSystemModel"));
    modelCombo->addItem(tr("QFileSystemModel that shows full path"));
    modelCombo->addItem(tr("Country list"));
    modelCombo->addItem(tr("Word list"));
    modelCombo->setCurrentIndex(0);

    QLabel *modeLabel = new QLabel;
    modeLabel->setText(tr("Completion Mode"));
    modeCombo = new QComboBox;
    modeCombo->addItem(tr("Inline"));
    modeCombo->addItem(tr("Filtered Popup"));
    modeCombo->addItem(tr("Unfiltered Popup"));
    modeCombo->setCurrentIndex(1);

    QLabel *caseLabel = new QLabel;
    caseLabel->setText(tr("Case Sensitivity"));
    caseCombo = new QComboBox;
    caseCombo->addItem(tr("Case Insensitive"));
    caseCombo->addItem(tr("Case Sensitive"));
    caseCombo->setCurrentIndex(0);

maxVisibleSpinBox 이 생성되고 작성기에 표시되는 항목의 수가 결정됩니다.

그런 다음 wrapCheckBox 이 설정됩니다. 이 checkBoxcompletersetWrapAround() 속성의 활성화 또는 비활성화 여부를 결정합니다.

    QLabel *maxVisibleLabel = new QLabel;
    maxVisibleLabel->setText(tr("Max Visible Items"));
    maxVisibleSpinBox = new QSpinBox;
    maxVisibleSpinBox->setRange(3,25);
    maxVisibleSpinBox->setValue(10);

    wrapCheckBox = new QCheckBox;
    wrapCheckBox->setText(tr("Wrap around completions"));
    wrapCheckBox->setChecked(true);

contentsLabel 을 인스턴스화하고 크기 정책을 fixed 으로 설정합니다. 그러면 콤보 박스의 activated() 신호가 각 슬롯에 연결됩니다.

    contentsLabel = new QLabel;
    contentsLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    connect(modelCombo, &QComboBox::activated,
            this, &MainWindow::changeModel);
    connect(modeCombo, &QComboBox::activated,
            this, &MainWindow::changeMode);
    connect(caseCombo, &QComboBox::activated,
            this, &MainWindow::changeCase);
    connect(maxVisibleSpinBox, &QSpinBox::valueChanged,
            this, &MainWindow::changeMaxVisible);

lineEdit 을 설정한 다음 QGridLayout 을 사용하여 모든 위젯을 정렬합니다. changeModel() 함수를 호출하여 completer 을 초기화합니다.

    lineEdit = new QLineEdit;

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(modelLabel, 0, 0); layout->addWidget(modelCombo, 0, 1);
    layout->addWidget(modeLabel, 1, 0);  layout->addWidget(modeCombo, 1, 1);
    layout->addWidget(caseLabel, 2, 0);  layout->addWidget(caseCombo, 2, 1);
    layout->addWidget(maxVisibleLabel, 3, 0); layout->addWidget(maxVisibleSpinBox, 3, 1);
    layout->addWidget(wrapCheckBox, 4, 0);
    layout->addWidget(contentsLabel, 5, 0, 1, 2);
    layout->addWidget(lineEdit, 6, 0, 1, 2);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

    changeModel();

    setWindowTitle(tr("Completer"));
    lineEdit->setFocus();
}

createMenu() 함수는 fileMenuhelpMenu 를 채우는 데 필요한 QAction 객체를 인스턴스화하는 데 사용됩니다. 액션의 triggered() 신호는 각각의 슬롯에 연결됩니다.

void MainWindow::createMenu()
{
    QAction *exitAction = new QAction(tr("Exit"), this);
    QAction *aboutAct = new QAction(tr("About"), this);
    QAction *aboutQtAct = new QAction(tr("About Qt"), this);

    connect(exitAction, &QAction::triggered, qApp, &QApplication::quit);
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
    connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt);

    QMenu *fileMenu = menuBar()->addMenu(tr("File"));
    fileMenu->addAction(exitAction);

    QMenu *helpMenu = menuBar()->addMenu(tr("About"));
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);
}

modelFromFile() 함수는 파일의 fileName 을 수락하고 그 내용에 따라 처리합니다.

먼저 file 의 유효성을 검사하여 QFile::읽기 전용 모드에서 열 수 있는지 확인합니다. 실패하면 이 함수는 빈 QStringListModel 을 반환합니다.

QAbstractItemModel *MainWindow::modelFromFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly))
        return new QStringListModel(completer);

그런 다음 마우스 커서를 Qt::WaitCursor 로 덮어쓴 다음 QStringList 객체 wordsfile 의 콘텐츠로 채웁니다. 이 작업이 완료되면 마우스 커서를 복원합니다.

#ifndef QT_NO_CURSOR
    QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
    QStringList words;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        if (!line.isEmpty())
            words << QString::fromUtf8(line.trimmed());
    }

#ifndef QT_NO_CURSOR
    QGuiApplication::restoreOverrideCursor();
#endif

앞서 언급했듯이 리소스 파일에는 countries.txt와 words.txt라는 두 개의 파일이 있습니다. file 읽은 내용이 words.txt인 경우 wordsQStringList 으로, completer 을 부모로 하는 QStringListModel 을 반환합니다.

    if (!fileName.contains(QLatin1String("countries.txt")))
        return new QStringListModel(words, completer);

file 읽은 내용이 countries.txt인 경우 words.count() 행, 열 2개, completer 을 부모로 하는 QStandardItemModel 이 필요합니다.

    QStandardItemModel *m = new QStandardItemModel(words.count(), 2, completer);

countries.txt의 표준 줄은 다음과 같습니다:

노르웨이 NO

따라서 QStandardItemModel 객체, m 을 채우려면 국가 이름과 해당 기호를 분할해야 합니다. 이 작업이 완료되면 m 을 반환합니다.

    for (int i = 0; i < words.count(); ++i) {
        QModelIndex countryIdx = m->index(i, 0);
        QModelIndex symbolIdx = m->index(i, 1);
        QString country = words.at(i).mid(0, words[i].length() - 2).trimmed();
        QString symbol = words.at(i).right(2);
        m->setData(countryIdx, country);
        m->setData(symbolIdx, symbol);
    }

    return m;
}

changeMode() 함수는 index 의 값에 따라 completer 의 모드를 설정합니다.

void MainWindow::changeMode(int index)
{
    QCompleter::CompletionMode mode;
    if (index == 0)
        mode = QCompleter::InlineCompletion;
    else if (index == 1)
        mode = QCompleter::PopupCompletion;
    else
        mode = QCompleter::UnfilteredPopupCompletion;

    completer->setCompletionMode(mode);
}

changeModel() 함수는 사용자가 선택한 모델에 따라 사용되는 항목 모델을 변경합니다.

switch 문은 modelCombo 의 인덱스에 따라 항목 모델을 변경하는 데 사용됩니다. case 가 0이면 정렬되지 않은 QFileSystemModel 을 사용하여 드라이브 레이블을 제외한 파일 경로를 제공합니다.

void MainWindow::changeModel()
{
    delete completer;
    completer = new QCompleter(this);
    completer->setMaxVisibleItems(maxVisibleSpinBox->value());

    switch (modelCombo->currentIndex()) {
    default:
    case 0:
        { // Unsorted QFileSystemModel
            QFileSystemModel *fsModel = new QFileSystemModel(completer);
            fsModel->setRootPath(QString());
            completer->setModel(fsModel);
            contentsLabel->setText(tr("Enter file path"));
        }
        break;

completer 을 부모로 하여 모델을 만들면 모델을 새 모델로 교체할 수 있습니다. completer 은 새 모델이 할당되는 순간 이전 모델이 삭제되도록 합니다.

case 이 1이면 앞서 정의한 DirModel 을 사용하여 파일에 대한 전체 경로를 생성합니다.

    case 1:
        {   // FileSystemModel that shows full paths
            FileSystemModel *fsModel = new FileSystemModel(completer);
            completer->setModel(fsModel);
            fsModel->setRootPath(QString());
            contentsLabel->setText(tr("Enter file path"));
        }
        break;

case 이 2이면 국가 이름을 완성하려고 시도합니다. 이를 위해서는 QTreeView 객체( treeView)가 필요합니다. 국가 이름은 countries.txt에서 추출하고 완료를 표시하는 데 사용되는 팝업을 treeView 로 설정합니다.

    case 2:
        { // Country List
            completer->setModel(modelFromFile(":/resources/countries.txt"));
            QTreeView *treeView = new QTreeView;
            completer->setPopup(treeView);
            treeView->setRootIsDecorated(false);
            treeView->header()->hide();
            treeView->header()->setStretchLastSection(false);
            treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
            treeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
            contentsLabel->setText(tr("Enter name of your country"));
        }
        break;

아래 스크린샷은 국가 목록 모델을 사용한 완성기를 보여줍니다.

case 이 3이면 단어를 완성하려고 시도합니다. 이 작업은 words.txt에서 추출한 데이터가 포함된 QStringListModel 을 사용하여 수행됩니다. 모델은 case insensitively 으로 정렬됩니다.

아래 스크린샷은 단어 목록 모델을 사용한 컴플리터를 보여줍니다.

모델 유형이 선택되면 changeMode() 함수와 changeCase() 함수를 호출하고 그에 따라 줄 바꿈 옵션을 설정합니다. wrapCheckBoxclicked() 신호는 completersetWrapAround() 슬롯에 연결됩니다.

    case 3:
        { // Word list
            completer->setModel(modelFromFile(":/resources/wordlist.txt"));
            completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
            contentsLabel->setText(tr("Enter a word"));
        }
        break;
    }

    changeMode(modeCombo->currentIndex());
    changeCase(caseCombo->currentIndex());
    completer->setWrapAround(wrapCheckBox->isChecked());
    lineEdit->setCompleter(completer);
    connect(wrapCheckBox, &QAbstractButton::clicked, completer, &QCompleter::setWrapAround);
}

changeMaxVisible() 함수는 작성기에 표시되는 최대 항목 수를 업데이트합니다.

void MainWindow::changeMaxVisible(int max)
{
    completer->setMaxVisibleItems(max);
}

about() 함수는 예제에 대한 간략한 설명을 제공합니다.

void MainWindow::about()
{
    QMessageBox::about(this, tr("About"), tr("This example demonstrates the "
        "different features of the QCompleter class."));
}

main() 함수

main() 함수는 QApplicationMainWindow 를 인스턴스화하고 show() 함수를 호출합니다.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.show();
    return app.exec();
}

예제 프로젝트 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.