カスタムコンプリートの例

カスタム・コンプリターの例は、モデルによって提供されたデータに基づいて、入力ウィジェットに文字列補完機能を提供する方法を示しています。このコンプリターは、ユーザーが入力した最初の3文字に基づいて、候補となる単語をポップアップ表示し、ユーザーが選択した単語はQTextCursor を使ってTextEdit に挿入されます。

リソースファイルのセットアップ

カスタム・コンプリターの例では、QCompleter 単語を補完するための単語リストがあるリソース・ファイル、wordlist.txt が必要です。このファイルには以下が含まれています:

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

TextEdit クラス定義

TextEdit クラスはQTextEdit のサブクラスで、insertCompletion() スロットがあり、keyPressEvent() とfocusInEvent() 関数を再実装しています。TextEdit には、textUnderCursor() というプライベート関数と、QCompleterc というプライベートインスタンスもあります。

class TextEdit : public QTextEdit
{
    Q_OBJECT

public:
    TextEdit(QWidget *parent = nullptr);
    ~TextEdit();

    void setCompleter(QCompleter *c);
    QCompleter *completer() const;

protected:
    void keyPressEvent(QKeyEvent *e) override;
    void focusInEvent(QFocusEvent *e) override;

private slots:
    void insertCompletion(const QString &completion);

private:
    QString textUnderCursor() const;

private:
    QCompleter *c = nullptr;
};

TextEdit クラスの実装

TextEdit のコンストラクタは、親を持つTextEdit を構築し、c を初期化します。setPlainText ()関数を使って、TextEdit オブジェクトにコンプリターの使用方法を表示します。

TextEdit::TextEdit(QWidget *parent)
    : QTextEdit(parent)
{
    setPlainText(tr("This TextEdit provides autocompletions for words that have more than"
                    " 3 characters. You can trigger autocompletion using ") +
                    QKeySequence("Ctrl+E").toString(QKeySequence::NativeText));
}

さらに、TextEdit にはデフォルトのデストラクタもあります:

TextEdit::~TextEdit()
{
}

setCompleter() 関数はcompleter を受け取り、設定します。if (c) を使って、c が初期化されているかどうかをチェックします。初期化されていれば、QObject::disconnect ()関数が呼び出され、スロットからシグナルが切断される。これは、以前のコンプリーターオブジェクトがまだスロットに接続されていないことを確認するためです。

void TextEdit::setCompleter(QCompleter *completer)
{
    if (c)
        c->disconnect(this);

    c = completer;

    if (!c)
        return;

    c->setWidget(this);
    c->setCompletionMode(QCompleter::PopupCompletion);
    c->setCaseSensitivity(Qt::CaseInsensitive);
    QObject::connect(c, QOverload<const QString &>::of(&QCompleter::activated),
                     this, &TextEdit::insertCompletion);
}

次に、completerc をインスタンス化し、TextEdit's widget として設定する。補完モードと大文字小文字の区別も設定し、activated() シグナルをinsertCompletion() スロットに接続します。

completer() 関数はゲッター関数で、c を返します。

QCompleter *TextEdit::completer() const
{
    return c;
}

補完機能は、wordlist.txtの内容に基づいて、利用可能なオプションをポップアップ表示しますが、テキストカーソルは、ユーザーが選択した単語に従って、不足している文字を埋める役割を果たします。

ユーザーが "ACT "と入力し、コンプリーターの "ACTUAL "という提案を受け入れたとする。すると、completion の文字列が、コンプリーターのactivated() 信号によってinsertCompletion() に送られる。

insertCompletion() 関数は、QTextCursor オブジェクト、tc を使用して単語を補完する。この関数は、tc を使用して単語を完成させるために余分な文字を挿入する前に、完了者のウィジェットがTextEdit であることを確認します。

void TextEdit::insertCompletion(const QString &completion)
{
    if (c->widget() != this)
        return;
    QTextCursor tc = textCursor();
    int extra = completion.length() - c->completionPrefix().length();
    tc.movePosition(QTextCursor::Left);
    tc.movePosition(QTextCursor::EndOfWord);
    tc.insertText(completion.right(extra));
    setTextCursor(tc);
}

下図はこのプロセスを示している:

completion.length() = 6

c->completionPrefix().length()=3

この2つの値の差はextra 、3である。これは、右から最後の3文字、「U」、「A」、「L」が、tc によって挿入されることを意味する。

textUnderCursor() 関数は、QTextCursortc を使用して、カーソル下の単語を選択し、それを返します。

QString TextEdit::textUnderCursor() const
{
    QTextCursor tc = textCursor();
    tc.select(QTextCursor::WordUnderCursor);
    return tc.selectedText();
}

TextEdit クラスは、ウィジェットのキーボード・フォーカス・イベントを受け取るためのイベント・ハンドラであるfocusInEvent() 関数を再実装しています。

void TextEdit::focusInEvent(QFocusEvent *e)
{
    if (c)
        c->setWidget(this);
    QTextEdit::focusInEvent(e);
}

keyPressEvent() は、Qt::Key_EnterQt::Key_ReturnQt::Key_EscapeQt::Key_TabQt::Key_Backtab のようなキー・イベントを無視するように再実装されているので、コンプリターがそれらを処理することができます。

アクティブなコンプリーターがある場合、Ctrl+Eというショートカットを処理することはできません。

void TextEdit::keyPressEvent(QKeyEvent *e)
{
    if (c && c->popup()->isVisible()) {
        // The following keys are forwarded by the completer to the widget
       switch (e->key()) {
       case Qt::Key_Enter:
       case Qt::Key_Return:
       case Qt::Key_Escape:
       case Qt::Key_Tab:
       case Qt::Key_Backtab:
            e->ignore();
            return; // let the completer do default behavior
       default:
           break;
       }
    }

    const bool isShortcut = (e->modifiers().testFlag(Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
    if (!c || !isShortcut) // do not process the shortcut when we have a completer
        QTextEdit::keyPressEvent(e);

また、その他の修飾子やショートカットのうち、コンプリーターに反応してほしくないものも処理します。

    const bool ctrlOrShift = e->modifiers().testFlag(Qt::ControlModifier) ||
                             e->modifiers().testFlag(Qt::ShiftModifier);
    if (!c || (ctrlOrShift && e->text().isEmpty()))
        return;

    static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
    const bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
    QString completionPrefix = textUnderCursor();

    if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
                      || eow.contains(e->text().right(1)))) {
        c->popup()->hide();
        return;
    }

    if (completionPrefix != c->completionPrefix()) {
        c->setCompletionPrefix(completionPrefix);
        c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
    }
    QRect cr = cursorRect();
    cr.setWidth(c->popup()->sizeHintForColumn(0)
                + c->popup()->verticalScrollBar()->sizeHint().width());
    c->complete(cr); // popup it up!
}

最後に、コンプレッサーをポップアップします。

MainWindowクラスの定義

MainWindow クラスはQMainWindow のサブクラスで、about() というプライベート・スロットを実装しています。このクラスはまた、createMenu()modelFromFile() という2つのプライベート関数と、QCompleterTextEdit というプライベート・インスタンスを持っています。

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private slots:
    void about();

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

    QCompleter *completer = nullptr;
    TextEdit *completingTextEdit;
};

MainWindow クラスの実装

コンストラクタは、親を持つMainWindow を構築し、completer を初期化します。また、TextEdit をインスタンス化し、そのコンプリタを設定します。modelFromFile() から取得したQStringListModel は、completer に入力するために使用されます。MainWindow の中心ウィジェットはTextEdit に設定され、そのサイズは 500 x 300 に設定されます。

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

    completingTextEdit = new TextEdit;
    completer = new QCompleter(this);
    completer->setModel(modelFromFile(":/resources/wordlist.txt"));
    completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
    completer->setCaseSensitivity(Qt::CaseInsensitive);
    completer->setWrapAround(false);
    completingTextEdit->setCompleter(completer);

    setCentralWidget(completingTextEdit);
    resize(500, 300);
    setWindowTitle(tr("Completer"));
}

createMenu() 関数は、"File" と "Help" メニューに必要なQAction オブジェクトを作成し、それらのtriggered() シグナルはそれぞれquit()about()aboutQt() スロットに接続される。

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 を受け取り、このファイルの内容をQStringListModel に展開しようとします。QStringListwords に入力するときにQt::WaitCursor を表示し、終了したらマウスカーソルを元に戻します。

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

#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
    return new QStringListModel(words, completer);
}

about() 関数は、カスタム・コンプリターの例に関する簡単な説明を提供します。

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

main() 関数

main() 関数はMainWindow をインスタンス化し、show() 関数を呼び出す。

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

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。