Qt ウィジェット - テキストビューアプラグインの例

メニュー、ツールバー、ステータスバーを備えたウィジェットの例です。

テキストビューアの例は、汎用のドキュメントビューアのプラグインの形で、QPlainTextEdit を中心に構築されたテキストエディタです。

TxtViewer AbstractViewer AbstractViewer は、ビューワとメインウィンドウ間のインタラクションのフレームワークを提供します。このアプリケーションは、 、 、 のエントリーをメニューバーに提供します。File Edit Help

メイン・ウィンドウの下部にあるステータス・バーには、アプリケーションがユーザーに提供するメッセージが表示される。

最近開いたファイルはFile メニューに表示されます。この例では、一度に1つのファイルしかロードできません。

クラス定義

class TxtViewer : public ViewerInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "txtviewer.json")
    Q_INTERFACES(ViewerInterface)

クラス定義は、シグナルとスロットを処理するQ_OBJECT マクロで始まります。その後に、プラグインの登録に必要なQ_PLUGIN_METADATAQ_INTERFACES マクロが続きます。

このクラスはViewerInterface を継承し、AbstractViewer を継承します。ViewerInterface クラスは、メイン・ウィンドウ・アプリケーションとプラグインの間のインターフェイスを提供するために使用されます。

QPluginLoader また、プラグインのキーを含むtxtviewer.jsonファイルが必要です:

{ "Keys": [ "txtviewer" ] }
public:
    TxtViewer();
    ~TxtViewer() override;
    void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
    QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
    QStringList supportedMimeTypes() const override;
    bool saveDocument() override { return saveFile(m_file.get()); };
    bool saveDocumentAs() override;
    bool hasContent() const override;
    QByteArray saveState() const override { return {}; }
    bool restoreState(QByteArray &) override { return true; }
    bool supportsOverview() const override { return false; }

#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
protected:
    void printDocument(QPrinter *printer) const override;
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT

private slots:
    void setupTxtUi();

private:
    void openFile();
    bool saveFile (QFile *file);

    QPlainTextEdit *m_textEdit;
};

このクラスはコンストラクタを定義していないので、引数のない標準的なコンストラクタしか利用できません。デストラクタを含む他のすべての関数は、ViewerInterface の仮想関数を再実装しています。これらは、メイン・アプリケーションとデータ、情報、命令を交換するために使用されます。

設定を保存したり復元したりする機能は実装されていません。supportsOverview 関数は常にfalse を返し、サムネイル・ナビゲーション用のウィンドウを表示する必要がないことをメイン・アプリケーションに伝えます。

TxtViewerクラスの実装

#include "txtviewer.h"

#include <QFileDialog>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QPlainTextEdit>
#include <QScrollBar>
#include <QToolBar>

#include <QGuiApplication>
#include <QPainter>
#include <QTextDocument>

#include <QDir>

#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrinter>
#include <QPrintDialog>
#endif

using namespace Qt::StringLiterals;

TxtViewer::TxtViewer()
{
    connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi);
}

TxtViewer::~TxtViewer() = default;

void TxtViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
{
    AbstractViewer::init(file, new QPlainTextEdit(parent), mainWindow);
    m_textEdit = qobject_cast<QPlainTextEdit *>(widget());
}

QStringList TxtViewer::supportedMimeTypes() const
{
    return {"text/plain"_L1};
}

void TxtViewer::setupTxtUi()
{
    QMenu *editMenu = addMenu(tr("&Edit"));
    QToolBar *editToolBar = addToolBar(tr("Edit"));
#ifndef QT_NO_CLIPBOARD
    const QIcon cutIcon = QIcon::fromTheme("edit-cut"_L1,
                                           QIcon(":/demos/documentviewer/images/cut.png"_L1));
    QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this);
    cutAct->setShortcuts(QKeySequence::Cut);
    cutAct->setStatusTip(tr("Cut the current selection's contents to the "
                            "clipboard"));
    connect(cutAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::cut);
    editMenu->addAction(cutAct);
    editToolBar->addAction(cutAct);

    const QIcon copyIcon = QIcon::fromTheme("edit-copy"_L1,
                                            QIcon(":/demos/documentviewer/images/copy.png"_L1));
    QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this);
    copyAct->setShortcuts(QKeySequence::Copy);
    copyAct->setStatusTip(tr("Copy the current selection's contents to the "
                             "clipboard"));
    connect(copyAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::copy);
    editMenu->addAction(copyAct);
    editToolBar->addAction(copyAct);

    const QIcon pasteIcon = QIcon::fromTheme("edit-paste"_L1,
                                             QIcon(":/demos/documentviewer/images/paste.png"_L1));
    QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
    pasteAct->setShortcuts(QKeySequence::Paste);
    pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
                              "selection"));
    connect(pasteAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::paste);
    editMenu->addAction(pasteAct);
    editToolBar->addAction(pasteAct);

    menuBar()->addSeparator();

    cutAct->setEnabled(false);
    copyAct->setEnabled(false);
    connect(m_textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
    connect(m_textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled);
#endif // !QT_NO_CLIPBOARD

    openFile();

    connect(m_textEdit, &QPlainTextEdit::textChanged, this, [&](){
        maybeSetPrintingEnabled(hasContent());
    });

    connect(m_uiAssets.back, &QAction::triggered, m_textEdit, [&](){
        auto *bar = m_textEdit->verticalScrollBar();
        if (bar->value() > bar->minimum())
            bar->setValue(bar->value() - 1);
    });

    connect(m_uiAssets.forward, &QAction::triggered, m_textEdit, [&](){
        auto *bar = m_textEdit->verticalScrollBar();
        if (bar->value() < bar->maximum())
            bar->setValue(bar->value() + 1);
    });
}

TxtViewer で使用されるすべてのクラスにアクセスするために必要なヘッダーファイルから始めます。また、txtviewer.h もインクルードします。

QPrinter と は、コンパイル・システムで印刷サポートが有効になっている場合にのみインクルードされます。QPrintDialog

なぜこれらのヘッダーをmainwindow.h に含めないのか不思議に思うかもしれません。その理由は、別のヘッダーファイルから複数の大きなヘッダーをインクルードすると、パフォーマンスが急速に低下する可能性があるからです。しかし、他のヘッダー・ファイルから厳密に必要なヘッダー・ファイルだけをインクルードするのは、一般的に良い考えです。

実装は空のデストラクタで始まる。完全に省略することもできる。コード読者に、デストラクタでは何もする必要がないことを示すために、空で実装するのはよい習慣です。

デストラクタの後には、3つの引数をとる初期化関数が続く:

  • fileオープンして表示するファイルへのポインタ。
  • parentエディターが配置されるQWidget
  • mainWindowメニューとメニュー・バーが処理されるアプリケーションのメイン・ウィンドウへのポインタ。

この関数は、AbstractViwer の基本 init 関数を呼び出します。新しいQPlainTextEdit ウィジェットが作成され、ファイルの内容が表示されます。そして、TxtViewer のセットアップ関数がベース・クラスのuiInitializedシグナルに接続されます。

次の関数は、テキスト・ビューワがサポートする MIME タイプのリストを返します。プレーンテキストのみがサポートされる。

最後の初期化関数は、メニュー、アイコン、ボタン、ツールチップのようなビューア固有のUIコンポーネントを追加します。AbstractViewer 、これらのコンポーネントがアプリケーションのメインウィンドウから削除されるようにします。

void TxtViewer::openFile()
{
    const QString type = tr("open");
    if (!m_file->open(QFile::ReadOnly | QFile::Text)) {
        statusMessage(tr("Cannot read file %1:\n%2.")
                      .arg(QDir::toNativeSeparators(m_file->fileName()),
                           m_file->errorString()), type);
        return;
    }

    QTextStream in(m_file.get());
#ifndef QT_NO_CURSOR
    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
#endif
    if (!m_textEdit->toPlainText().isEmpty()) {
        m_textEdit->clear();
        disablePrinting();
    }
    m_textEdit->setPlainText(in.readAll());
#ifndef QT_NO_CURSOR
    QGuiApplication::restoreOverrideCursor();
#endif

    statusMessage(tr("File %1 loaded.")
                  .arg(QDir::toNativeSeparators(m_file->fileName())), type);
    maybeEnablePrinting();
}

openFile ファイルをオープンし、その内容を に転送し、オープンが成功したかどうかに応じて、ユーザーにステータス・メッセージを表示します。QPlainTextEdit

bool TxtViewer::hasContent() const
{
    return (!m_textEdit->toPlainText().isEmpty());
}

#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
void TxtViewer::printDocument(QPrinter *printer) const
{
    if (!hasContent())
        return;

    m_textEdit->print(printer);
}
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT

bool TxtViewer::saveFile(QFile *file)
{
    QString errorMessage;

    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
    if (file->open(QFile::WriteOnly | QFile::Text)) {
        QTextStream out(file);
        out << m_textEdit->toPlainText();
    } else {
        errorMessage = tr("Cannot open file %1 for writing:\n%2.")
                       .arg(QDir::toNativeSeparators(file->fileName())),
                            file->errorString();
    }
    QGuiApplication::restoreOverrideCursor();

    if (!errorMessage.isEmpty()) {
        statusMessage(errorMessage);
        return false;
    }

    statusMessage(tr("File %1 saved")
                  .arg(QDir::toNativeSeparators(file->fileName())));
    return true;
}

bool TxtViewer::saveDocumentAs()
{
    QFileDialog dialog(mainWindow());
    dialog.setWindowModality(Qt::WindowModal);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    if (dialog.exec() != QDialog::Accepted)
        return false;

    const QStringList &files = dialog.selectedFiles();
    if (files.isEmpty())
        return false;

    //newFile();
    m_file->setFileName(files.first());
    return saveDocument();
}

次に再実装された関数は、ビューア・プラグインが実際にコンテンツを表示しているかどうかをメイン・アプリケーションに伝えます。

コンパイルシステムで印刷がサポートされている場合、次のセクションでそれを実装する。

最後の2つの再実装は、現在のファイルを保存したり、新しい名前で保存したりする機能を提供する。

プロジェクト例 @ code.qt.io

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