이 페이지에서

트리 모델 자동 완성 예제

트리 모델 완성기 예제는 마침표를 구분자로 사용하여 자식(Child), 손자(GrandChild), 증손자(GrandGrandChild) 레벨의 객체에 접근함으로써 계층적 모델에 대한 자동 완성 기능을 제공하는 방법을 보여줍니다.

트리 모델의 시각화 및 완성기(completer)의 다양한 옵션

'완성기 예제'와 마찬가지로, 완성 모드 및 대소문자 구분 여부를 선택할 수 있도록 ` QComboBox ` 객체를 제공하고, 줄 바꿈 완성을 위한 ` QCheckBox `도 제공합니다.

리소스 파일

TreeModelCompleter의 내용은 treemodel.txt에서 읽습니다. 이 파일은 treemodelcompleter.qrc 리소스 파일에 포함되어 있으며, 해당 파일에는 다음 내용이 포함되어 있습니다.

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

TreeModelCompleter 클래스 정의

TreeModelCompleter 는 QCompleter 의 하위 클래스로, 두 개의 생성자를 가지고 있습니다. 하나는 parent 를 인수로 받고, 다른 하나는 parent 와 model 를 인수로 받습니다.

class TreeModelCompleter : public QCompleter
{
    Q_OBJECT
    Q_PROPERTY(QString separator READ separator WRITE setSeparator)

public:
    explicit TreeModelCompleter(QObject *parent = nullptr);
    explicit TreeModelCompleter(QAbstractItemModel *model, QObject *parent = nullptr);

    QString separator() const;
public slots:
    void setSeparator(const QString &separator);

protected:
    QStringList splitPath(const QString &path) const override;
    QString pathFromIndex(const QModelIndex &index) const override;

private:
    QString sep;
};

이 클래스는 트리 모델에 적합하도록 보호된 함수 ` splitPath()` 및 ` pathFromIndex()`을 재구현합니다. 트리 모델에 맞게 ` QCompleter `을 사용자 정의하는 방법에 대한 자세한 내용은 ` Handling Tree Models`을 참조하십시오.

TreeModelCompleter 또한 Q_PROPERTY() 매크로를 사용하여 선언된 separator 속성을 가지고 있습니다. separator에는 READ 및 WRITE 속성이 있으며, 이에 대응하는 함수는 separator() 와 setSeparator() 입니다. Q_PROPERTY()에 대한 자세한 내용은 Qt의 속성 시스템을 참조하십시오.

TreeModelCompleter 클래스 구현

첫 번째 생성자는 부모를 갖는 TreeModelCompleter 객체를 생성하는 반면, 두 번째 생성자는 부모와 QAbstractItemModel, model 를 모두 갖는 객체를 생성합니다.

TreeModelCompleter::TreeModelCompleter(QObject *parent)
    : QCompleter(parent)
{
}

TreeModelCompleter::TreeModelCompleter(QAbstractItemModel *model, QObject *parent)
    : QCompleter(model, parent)
{
}

separator() 함수는 구분자 문자열을 반환하는 게터 함수입니다.

QString TreeModelCompleter::separator() const
{
    return sep;
}

앞서 언급했듯이, splitPath() 함수는 기본 구현이 QFileSystemModel 또는 리스트 모델에 더 적합하기 때문에 재구현되었습니다. QCompleter 가 경로를 각 레벨에서 일치하는 문자열 목록으로 분할할 수 있도록, sep 를 구분자로 사용하여 QString::split()를 통해 경로를 분할합니다.

QStringList TreeModelCompleter::splitPath(const QString &path) const
{
    return (sep.isNull() ? QCompleter::splitPath(path) : path.split(sep));
}

pathFromIndex() 함수는 트리 모델에 대한 completionRole() 데이터를 반환합니다. 이 함수는 기본 구현이 리스트 모델에 더 적합하므로 재구현되었습니다. 구분자가 없는 경우 QCompleter 의 기본 구현을 사용하고, 그렇지 않은 경우 prepend() 함수를 사용하여 위쪽으로 탐색하며 데이터를 축적합니다. 그런 다음 이 함수는 구분자를 사용하여 서로 다른 레벨의 객체를 결합함으로써 ` QStringList`, ` dataList`을 반환합니다.

QString TreeModelCompleter::pathFromIndex(const QModelIndex &index) const
{
    if (sep.isNull())
        return QCompleter::pathFromIndex(index);

    // navigate up and accumulate data
    QStringList dataList;
    for (QModelIndex i = index; i.isValid(); i = i.parent())
        dataList.prepend(model()->data(i, completionRole()).toString());

    return dataList.join(sep);
}

MainWindow 클래스 정의

MainWindow 클래스는 QMainWindow 의 하위 클래스이며, about(), changeCase(), changeMode(), highlight(), updateContentsLabel() 등 5개의 사용자 정의 슬롯을 구현합니다.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private slots:
    void about();
    void changeCase(int);
    void changeMode(int);
    void highlight(const QModelIndex &index);
    void updateContentsLabel(const QString &sep);

또한, 이 클래스에는 createMenu() 및 modelFromFile() 라는 두 개의 비공개 함수와, QTreeView, QComboBox, QLabel, TreeModelCompleter, QLineEdit 의 비공개 인스턴스가 있습니다.

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

    QTreeView *treeView = nullptr;
    QComboBox *caseCombo = nullptr;
    QComboBox *modeCombo = nullptr;
    QLabel *contentsLabel = nullptr;
    TreeModelCompleter *completer = nullptr;
    QLineEdit *lineEdit = nullptr;
};

MainWindow 클래스 구현

MainWindow 의 생성자는 부모를 가진 MainWindow 객체를 생성하고, completer 및 lineEdit 를 초기화합니다. createMenu() 함수가 호출되어 "File" 메뉴와 "Help" 메뉴를 설정합니다. completer 의 모델은 modelFromFile() 에서 얻은 QAbstractItemModel 로 설정되며, highlighted() 신호는 MainWindow 의 highlight() 슬롯에 연결됩니다.

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

    completer = new TreeModelCompleter(this);
    completer->setModel(modelFromFile(":/resources/treemodel.txt"));
    completer->setSeparator(QLatin1String("."));
    QObject::connect(completer, QOverload<const QModelIndex &>::of(&TreeModelCompleter::highlighted),
                     this, &MainWindow::highlight);

    QWidget *centralWidget = new QWidget;

    QLabel *modelLabel = new QLabel;
    modelLabel->setText(tr("Tree Model<br>(Double click items to edit)"));

    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);

QLabel 객체인 modelLabel, modeLabel 및 caseLabel 가 인스턴스화됩니다. 또한 QComboBox 객체인 modeCombo 와 caseCombo 도 인스턴스화되고 데이터가 채워집니다. 기본적으로 completer 의 모드는 "Filtered Popup"이며 대소문자를 구분하지 않습니다.

    QLabel *separatorLabel = new QLabel;
    separatorLabel->setText(tr("Tree Separator"));

    QLineEdit *separatorLineEdit = new QLineEdit;
    separatorLineEdit->setText(completer->separator());
    connect(separatorLineEdit, &QLineEdit::textChanged,
            completer, &TreeModelCompleter::setSeparator);

    QCheckBox *wrapCheckBox = new QCheckBox;
    wrapCheckBox->setText(tr("Wrap around completions"));
    wrapCheckBox->setChecked(completer->wrapAround());
    connect(wrapCheckBox, &QAbstractButton::clicked, completer, &QCompleter::setWrapAround);

    contentsLabel = new QLabel;
    contentsLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    connect(separatorLineEdit, &QLineEdit::textChanged,
            this, &MainWindow::updateContentsLabel);

    treeView = new QTreeView;
    treeView->setModel(completer->model());
    treeView->header()->hide();
    treeView->expandAll();

    connect(modeCombo, &QComboBox::activated,
            this, &MainWindow::changeMode);
    connect(caseCombo, &QComboBox::activated,
            this, &MainWindow::changeMode);

    lineEdit = new QLineEdit;
    lineEdit->setCompleter(completer);

QGridLayout 를 사용하여 모든 객체를 MainWindow 에 배치합니다.

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(modelLabel, 0, 0); layout->addWidget(treeView, 0, 1);
    layout->addWidget(modeLabel, 1, 0);  layout->addWidget(modeCombo, 1, 1);
    layout->addWidget(caseLabel, 2, 0);  layout->addWidget(caseCombo, 2, 1);
    layout->addWidget(separatorLabel, 3, 0); layout->addWidget(separatorLineEdit, 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);

    changeCase(caseCombo->currentIndex());
    changeMode(modeCombo->currentIndex());

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

createMenu() 함수는 필요한 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);
}

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);
}

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

void MainWindow::about()
{
    QMessageBox::about(this, tr("About"), tr("This example demonstrates how "
        "to use a QCompleter with a custom tree model."));
}

changeCase() 함수는 cs 의 값에 따라 Case Sensitive 모드와 Case Insensitive 모드를 번갈아 가며 실행합니다.

void MainWindow::changeCase(int cs)
{
    completer->setCaseSensitivity(cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
}

main() 함수

main() 함수는 MainWindow 를 인스턴스화하고 show() 함수를 호출하여 이를 표시합니다.

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

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

© 2026 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.