ドキュメント・ビューア
JSON、テキスト、PDFファイルを表示、印刷するためのウィジェットアプリケーションです。

Document Viewerは、静的および動的なツールバー、メニュー、アクションを備えたQMainWindow 。さらに、ウィジェット・ベースのアプリケーションにおける以下の機能も紹介します:
- QSettings を使用して、ユーザー設定を照会および保存し、以前に開いたファイルの履歴を管理する。
- ウィジェットにカーソルを置いたときのカーソル動作の制御。
- 動的にロードされるプラグインの作成。
- 異なる言語へのUIのローカライズ。
例の実行
からサンプルを実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Qt Creator:Tutorialを参照してください:ビルドと実行。
アプリケーションとメイン・ウィンドウの作成
アプリケーションとそのメイン・ウィンドウはmain.cpp で構築されます。main()関数は、コマンドライン引数(help、version、オプションの位置引数file)を処理するためにQCommandLineParser 。ユーザーがアプリケーションの起動時にファイルへのパスを指定した場合、メイン・ウィンドウはそのファイルを開きます:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QCoreApplication::setOrganizationName("QtProject"_L1); QCoreApplication::setApplicationName("DocumentViewer"_L1); QCoreApplication::setApplicationVersion("1.0"_L1); Translator mainTranslator; mainTranslator.setBaseName("docviewer"_L1); mainTranslator.install(); QCommandLineParser parser; parser.setApplicationDescription(Tr::tr("A viewer for JSON, PDF and text files")); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("File"_L1, Tr::tr("JSON, PDF or text file to open")); parser.process(app); const QStringList &positionalArguments = parser.positionalArguments(); const QString &fileName = (positionalArguments.count() > 0) ? positionalArguments.at(0) : QString(); MainWindow w(mainTranslator); // Start application only if plugins are available if (!w.hasPlugins()) { QMessageBox::critical(nullptr, Tr::tr("No viewer plugins found"), Tr::tr("Unable to load viewer plugins. Exiting application.")); return 1; } w.show(); if (!fileName.isEmpty()) w.openFile(fileName); return app.exec(); }
MainWindowクラス
MainWindow クラスは、メニュー、アクション、ツールバーを備えたアプリケーション画面を提供します。ファイルを開くことができ、そのコンテンツ・タイプを自動的に検出します。また、QSettings を使用して、以前に開いたファイルのリストを保持し、起動時に設定を保存して再読み込みします。MainWindowは、コンテンツ・タイプに基づいて、開かれたファイルに適したビューアを作成し、ドキュメントの印刷をサポートします。
MainWindowのコンストラクタは、Qt Designer で作成されたユーザー・インターフェイスを初期化します。mainwindow.ui ファイルは左側にQTabWidget を提供し、ブックマークとサムネイルを表示します。右側には、ファイルの内容を表示するためのQScrollArea があります。
ViewerFactoryクラス
ViewerFactory クラスは、既知のファイルタイプのビューアを管理します。これらのビューアはプラグインとして実装されます。ViewerFactoryのインスタンスが作成されると、ビューエリアとメインウィンドウへのポインタがコンストラクタに渡されます:
m_factory.reset(new ViewerFactory(ui->viewArea, this));
ViewerFactory は、構築時に利用可能なすべてのプラグインをロードします。ViewerFactory は、ロードされたプラグイン、その名前、およびサポートされる MIME タイプを照会するためのパブリック API を提供します:
using ViewerList = QList<AbstractViewer *>; QStringList viewerNames(bool showDefault = false) const; ViewerList viewers() const; AbstractViewer *findViewer(const QString &viewerName) const; AbstractViewer *defaultViewer() const; QStringList supportedMimeTypes() const;
viewer() 関数は、引数として渡されたQFile を開くのに適したプラグインへのポインタを返します:
m_viewer = m_factory->viewer(file);
アプリケーション設定にビューア用のセクションが含まれている場合、それはビューアの仮想restoreState() 関数に渡されます:
void MainWindow::restoreViewerSettings() { if (!m_viewer) return; QSettings settings; settings.beginGroup(settingsViewers); QByteArray viewerSettings = settings.value(m_viewer->viewerName(), QByteArray()).toByteArray(); settings.endGroup(); if (!viewerSettings.isEmpty()) m_viewer->restoreState(viewerSettings); }
次に、標準のUIアセットがビューアに渡され、ビューアの表示ウィジェットが表示されるようにメインスクロールエリアが設定されます:
m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget); restoreViewerSettings(); ui->scrollArea->setWidget(m_viewer->widget()); return true; }
AbstractViewerクラス
AbstractViewer は、ドキュメントを表示、保存、印刷するための一般化されたAPIを提供します。ドキュメントとビューアの両方のプロパティを問い合わせることができます:
- 文書に内容はあるか?
- 文書に内容はあるか?
- 概要(サムネイルやブックマーク)はサポートされていますか?
AbstractViewerは、派生クラスがメインウィンドウ上にアクションやメニューを作成するためのプロテクトメソッドを提供します。これらのアセットをメインウィンドウに表示するために、アセットがメインウィンドウの親になります。AbstractViewerは、作成したUIアセットの削除と破棄を行います。シグナルとスロットを実装するために、QObject を継承しています。
シグナル
void uiInitialized();
このシグナルは、ビューアがメインウィンドウ上のUIアセットに関するすべての必要な情報を受け取った後に発行されます。
void printingEnabledChanged(bool enabled);
このシグナルは、ドキュメントの印刷が有効または無効になったときに発行されます。これは、新しいドキュメントが正常にロードされた後、または、例えば、すべてのコンテンツが削除された後に発生します。
void showMessage(const QString &message, int timeout = 8000);
このシグナルは、ユーザーにステータス・メッセージを表示するために発行されます。
void documentLoaded(const QString &fileName);
このシグナルは、ドキュメントが正常に読み込まれたことをアプリケーションに通知します。
TxtViewerクラス
TxtViewer は、AbstractViewerを継承したシンプルなテキストビューアです。テキストファイルの編集、コピー/カット&ペースト、印刷、変更の保存をサポートしています。
クラス定義
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_METADATA とQ_INTERFACES マクロが続きます。
このクラスはViewerInterface を継承し、AbstractViewer を継承します。ViewerInterface クラスは、メイン・ウィンドウ・アプリケーションとプラグインの間のインターフェイスを提供するために使用されます。
QPluginLoader また、プラグインのキーを含むtxtviewer.jsonファイルが必要です:
{ "Keys": [ "txtviewer" ] }このクラスはコンストラクタを定義していないので、引数のない標準コンストラクタしか利用できません。デストラクタを含む他のすべての関数は、ViewerInterface の仮想関数を再実装しています。これらは、メインアプリケーションとデータ、情報、および命令を交換するために使用されます。
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; } void retranslate() override; #ifdef DOCUMENTVIEWER_PRINTSUPPORT protected: void printDocument(QPrinter *printer) const override; #endif // DOCUMENTVIEWER_PRINTSUPPORT private slots: void setupTxtUi(); private: void openFile(); bool saveFile (QFile *file); QPlainTextEdit *m_textEdit; QMenu *m_editMenu = nullptr; QToolBar *m_editToolBar = nullptr; QAction *m_cutAct = nullptr; QAction *m_copyAct = nullptr; QAction *m_pasteAct = nullptr; };
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 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()); setTranslationBaseName("txtviewer"_L1); } QStringList TxtViewer::supportedMimeTypes() const { return {"text/plain"_L1}; } void TxtViewer::setupTxtUi() { m_editMenu = addMenu(tr("&Edit")); m_editToolBar = addToolBar(tr("Edit")); #if QT_CONFIG(clipboard) const QIcon cutIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCut, QIcon(":/demos/documentviewer/images/cut.png"_L1)); m_cutAct = new QAction(cutIcon, tr("Cu&t"), this); m_cutAct->setShortcuts(QKeySequence::Cut); m_cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); connect(m_cutAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::cut); m_editMenu->addAction(m_cutAct); m_editToolBar->addAction(m_cutAct); const QIcon copyIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCopy, QIcon(":/demos/documentviewer/images/copy.png"_L1)); m_copyAct = new QAction(copyIcon, tr("&Copy"), this); m_copyAct->setShortcuts(QKeySequence::Copy); m_copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); connect(m_copyAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::copy); m_editMenu->addAction(m_copyAct); m_editToolBar->addAction(m_copyAct); const QIcon pasteIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditPaste, QIcon(":/demos/documentviewer/images/paste.png"_L1)); m_pasteAct = new QAction(pasteIcon, tr("&Paste"), this); m_pasteAct->setShortcuts(QKeySequence::Paste); m_pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); connect(m_pasteAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::paste); m_editMenu->addAction(m_pasteAct); m_editToolBar->addAction(m_pasteAct); menuBar()->addSeparator(); m_cutAct->setEnabled(false); m_copyAct->setEnabled(false); connect(m_textEdit, &QPlainTextEdit::copyAvailable, m_cutAct, &QAction::setEnabled); connect(m_textEdit, &QPlainTextEdit::copyAvailable, m_copyAct, &QAction::setEnabled); #endif // QT_CONFIG(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メニューとメニュー・バーが処理されるアプリケーションのメイン・ウィンドウへのポインタ。
この関数は、AbstractViewer の基本 init 関数を呼び出します。新しいQPlainTextEdit ウィジェットが作成され、ファイルの内容が表示されます。そして、TxtViewer のsetup関数がベース・クラスの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()); #if QT_CONFIG(cursor) QGuiApplication::setOverrideCursor(Qt::WaitCursor); #endif if (!m_textEdit->toPlainText().isEmpty()) { m_textEdit->clear(); disablePrinting(); } m_textEdit->setPlainText(in.readAll()); #if QT_CONFIG(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 DOCUMENTVIEWER_PRINTSUPPORT void TxtViewer::printDocument(QPrinter *printer) const { if (!hasContent()) return; m_textEdit->print(printer); } #endif // 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(); } void TxtViewer::retranslate() { if (m_editMenu) m_editMenu->setTitle(tr("&Edit")); if (m_editToolBar) m_editToolBar->setWindowTitle(tr("Edit")); if (m_cutAct) { m_cutAct->setText(tr("Cu&t")); m_cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); } if (m_copyAct) { m_copyAct->setText(tr("&Copy")); m_copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); } if (m_pasteAct) { m_pasteAct->setText(tr("&Paste")); m_pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); } }
次に再実装された関数は、ビューア・プラグインが実際にコンテンツを表示しているかどうかをメイン・アプリケーションに伝えます。
コンパイルシステムで印刷がサポートされている場合、次のセクションでそれを実装する。
最後の2つの再実装は、現在のファイルを保存したり、新しい名前で保存したりする機能を提供する。
ImageViewerクラス
ImageViewer は、QImageReader でサポートされているように、QLabel を使って画像を表示します。
コンストラクタでは、QImageReader の割り当て制限を増やして、より大きな写真を表示できるようにしています:
ImageViewer::ImageViewer() : m_formats(imageFormats()) { connect(this, &AbstractViewer::uiInitialized, this, &ImageViewer::setupImageUi); QImageReader::setAllocationLimit(1024); // MB }
openFile()関数では、画像をロードし、そのサイズを決定します。画像が画面より大きい場合は、アスペクト比を維持したまま画面サイズに縮小します。この計算はネイティブ・ピクセルで行う必要があり、結果のピクセルマップを鮮明に表示するには、デバイスのピクセル比を設定する必要があります:
void ImageViewer::openFile() { #if QT_CONFIG(cursor) QGuiApplication::setOverrideCursor(Qt::WaitCursor); #endif const QString name = m_file->fileName(); QImageReader reader(name); const QImage origImage = reader.read(); if (origImage.isNull()) { statusMessage(tr("Cannot read file %1:\n%2.") .arg(QDir::toNativeSeparators(name), reader.errorString()), tr("open")); disablePrinting(); #if QT_CONFIG(cursor) QGuiApplication::restoreOverrideCursor(); #endif return; } clear(); QImage image = origImage.colorSpace().isValid() ? origImage.convertedToColorSpace(QColorSpace::SRgb) : origImage; const auto devicePixelRatio = m_imageLabel->devicePixelRatioF(); m_imageSize = QSizeF(image.size()) / devicePixelRatio; QPixmap pixmap = QPixmap::fromImage(image); pixmap.setDevicePixelRatio(devicePixelRatio); m_imageLabel->setPixmap(pixmap); const QSizeF targetSize = m_imageLabel->parentWidget()->size(); if (m_imageSize.width() > targetSize.width() || m_imageSize.height() > targetSize.height()) { m_initialScaleFactor = qMin(targetSize.width() / m_imageSize.width(), targetSize.height() / m_imageSize.height()); } m_maxScaleFactor = 3 * m_initialScaleFactor; m_minScaleFactor = m_initialScaleFactor / 3; doSetScaleFactor(m_initialScaleFactor); statusMessage(msgOpen(name, origImage)); #if QT_CONFIG(cursor) QGuiApplication::restoreOverrideCursor(); #endif maybeEnablePrinting(); }
JsonViewerクラス
JsonViewer は、JSON ファイルをQTreeView に表示します。内部的には、ファイルのコンテンツをQJsonDocument にロードし、それを使用してカスタム ツリー モデルにJsonItemModel を入力します。
JSONビューアプラグインは、QAbstractItemModel から継承されたカスタムアイテムモデルを実装する方法を示しています。JsonTreeItem クラスは、JSONデータを操作し、それを基礎となるQJsonDocument に伝搬するための基本的なAPIを提供します。
JsonViewer は、ドキュメントの最上位オブジェクトをナビゲーションのブックマークとして使用します。他のノード(キーと値)は、追加のブックマークとして追加したり、ブックマークリストから削除したりすることができます。
PdfViewerクラス
PdfViewer クラス(とプラグイン)はPDF Viewer Widget Example のフォークです。これは、ドキュメントをスムーズにフリックするためのQScroller の使い方を示しています。
その他の関連クラス
HoverWatcherクラス
HoverWatcher クラスは、マウスをウィジェットの上に置くとオーバーライドカーソルを設定し、離れると元に戻します。同じウィジェットに複数のHoverWatcherインスタンスが生成されるのを防ぐため、ウィジェットごとにシングルトンとして実装されています。
HoverWatcher はQObject を継承し、ウォッチするQWidget をインスタンスの親とします。ホバーイベントを消費せずにインターセプトするイベントフィルタをインストールします:
HoverWatcher::HoverWatcher(QWidget *watched) : QObject(watched), m_watched(watched) { Q_ASSERT(watched); m_cursorShapes[Entered].emplace(Qt::OpenHandCursor); m_cursorShapes[MousePress].emplace(Qt::ClosedHandCursor); m_cursorShapes[MouseRelease].emplace(Qt::OpenHandCursor); // no default for Left => restore override cursor m_watched->installEventFilter(this); }
HoverAction enumは、HoverWatcherが反応するアクションをリストしています:
enum HoverAction { Entered, MousePress, MouseRelease, Left, Ignore };
静的関数は、ウォッチャーを作成したり、特定のQWidget に対してウォッチャーの存在をチェックしたり、ウォッチャーを解除したりします:
static HoverWatcher *watcher(QWidget *watched); static const HoverWatcher *watcher(const QWidget *watched); static bool hasWatcher(QWidget *widget); static void dismiss(QWidget *watched);
カーソル形状は、各HoverActionに対して設定または解除することができます。関連するカーソル形状がない場合、アクションがトリガーされると、アプリケーションのオーバーライドカーソルが復元されます。
public slots: void setCursorShape(HoverAction type, Qt::CursorShape shape); void unSetCursorShape(HoverAction type);
mouseButtons プロパティは、MousePress アクションを考慮するマウスボタンを保持します:
void setMouseButtons(Qt::MouseButtons buttons); void setMouseButton(Qt::MouseButton button, bool enable);
アクション固有のシグナルは、アクションを処理した後に発せられる:
signals: void entered(); void mousePressed(); void mouseReleased(); void left();
一般的なシグナルは、処理されたアクションを引数として渡します:
void hoverAction(HoverAction action);RecentFilesクラス
RecentFiles は、最近開いたファイルのリストを管理することに特化したQStringList 。
RecentFilesは、単一のファイルまたは複数のファイルを一度に追加するスロットを持っています。パスが存在し、開くことができるファイルを指している場合、エントリが最近開いたファイルのリストに追加されます。ファイルがすでにリストにある場合、そのファイルは元の位置から取り除かれ、一番上に追加される。
public slots: void addFile(const QString &fileName) { addFile(fileName, EmitPolicy::EmitWhenChanged); } void addFiles(const QStringList &fileNames);
ファイルは、名前またはインデックスによってリストから削除される:
void removeFile(const QString &fileName) { removeFile(m_files.indexOf(fileName)); } void removeFile(qsizetype index) {removeFile(index, RemoveReason::Other); }
QSettings からの保存と復元を実行するスロット:
void saveSettings(QSettings &settings, const QString &key) const; bool restoreFromSettings(QSettings &settings, const QString &key);
設定をリストアする場合、存在しないファイルは無視されます。maxFiles プロパティは、保存する最近のファイルの最大量を保持します(デフォルトは10)。
qsizetype maxFiles();
void setMaxFiles(qsizetype maxFiles);RecentFiles は、ファイルを受け入れる前に、そのファイルが読み込めるかどうかを確認します。
RecentFileMenu クラス
RecentFileMenu は、RecentFilesオブジェクトをサブメニューとして表示することに特化したQMenu 。
コンストラクタは、親クラス(QObject )へのポインタと、表示するRecentFilesオブジェクトへのポインタを受け取ります。fileOpened() シグナルは、ユーザが最近のファイルを選択したときに発生し、引数としてファイルの絶対パスを渡します。
注: RecentFileMenu は、親ウィジェットか、コンストラクタに渡されたRecentFiles オブジェクトによって破棄されます。
class RecentFileMenu : public QMenu { Q_OBJECT public: explicit RecentFileMenu(QWidget *parent, RecentFiles *recent); signals: void fileOpened(const QString &fileName); ... };
翻訳
アプリケーションのユーザー・インターフェースは、英語とドイツ語で利用できます。デフォルトの言語は Qt によって自動選択されます:システム言語がドイツ語であればドイツ語、そうでなければ英語です。また、Help >Language メニューで言語を切り替えることができます。各プラグインは、メインアプリケーションと同様に、ランタイム中に独自の翻訳を読み込む責任を個別に負います。
CMakeの統合
トップレベルのCMakeLists.txtは出荷された言語を宣言します。
qt_standard_project_setup(REQUIRES 6.8
I18N_SOURCE_LANGUAGE en
I18N_TRANSLATED_LANGUAGES de
)documentviewer ターゲットはメインアプリケーションを定義します。ターゲット用にローカライズされた文字列をdocviewer_de.tsとdocviewer_en.tsファイルに格納し、ロードします。さらに、印刷ダイアログのようなQtダイアログも適切に翻訳されるように、Qtが提供するそれぞれのqtbase translations を生成された翻訳ファイルにマージします:
qt_add_translations(documentviewer
SOURCE_TARGETS documentviewer abstractviewer
TS_FILE_BASE docviewer
MERGE_QT_TRANSLATIONS
QT_TRANSLATION_CATALOGS qtbase
)各プラグインのレベルCMakeLists.txt は、そのプラグインのソースファイル (SOURCE_TARGETS) に対してのみqt_add_translations を呼び出します。翻訳ファイルをプラグインターゲットにスコープすることで、メインアプリケーションや他のプラグインのソースを再スキャンして再翻訳することを防ぎます:
qt_add_translations(txtviewer
SOURCE_TARGETS txtviewer
TS_FILE_BASE txtviewer
)翻訳者クラス
Translator クラスは Qt のQTranslator のラッパーで、メインアプリケーションと各プラグインの国際化を管理します。各コンポーネント(メインアプリケーションとプラグイン)は独自の Translator インスタンスを持っており、アプリケーション全体の言語切り替えを調整することができます。起動時またはユーザーが新しい言語を選択すると、Translator::install() が呼び出されます。このメソッドでは、QTranslator::load() を使用して、QLocale::uiLanguages() と Qt リソースシステムの basename に基づいて翻訳ファイルを読み込みます。一致する翻訳が見つからない場合は、英語にフォールバックします。
voidTranslator::install() {if(m_baseName.isEmpty()) { 翻訳ファイルをロードします。 qWarning() << "The basename of the translation is not set. Ignoring."; return; }if(!m_translator.isEmpty()) qApp->removeTranslator(&m_translator); if(m_translator.load(m_trLocale,m_baseName, "_"_L1, ":/i18n/"_L1)&& qApp->installTranslator(&m_translator)) {. qInfo() << "Loaded translation" << m_translator.filePath(); }else{if(m_trLocale.language()!= ::英語) {if(m_trLocale.language()!= ::英語) QLocale::英語) {。 qWarning() << "Failed to load translation" << m_baseName << "ロケール用"<<m_trLocale.name()<< ".英語翻訳にフォールバック"; setLanguage(QLocale::English); } }
プラグイン対応
AbstractViewer 基本クラスは3つのメソッドを提供し、各プラグインが独自の翻訳を管理できるようにしています:
AbstractViewer::setTranslationBaseName():Translatorオブジェクトを初期化し、そのベース名を設定し、デフォルトの翻訳を読み込むためにインストールします。void AbstractViewer::setTranslationBaseName(const QString &baseName) { m_translator.reset(new Translator); m_translator->setBaseName(baseName); m_translator->install(); }
AbstractViewer::updateTranslation(): 既存の Translator でinstall()を呼び出して新しい翻訳をインストールし、retranslate()を呼び出してすべてのテキストをリフレッシュします。void AbstractViewer::updateTranslation(QLocale::Language lang) { if (m_translator) { m_translator->setLanguage(lang); m_translator->install(); retranslate(); } }
AbstractViewer::retranslate(): 各プラグインが自身のUIテキストを再翻訳するために実装する仮想メソッドです。例えば、ImageViewerで再実装されています:void ImageViewer::retranslate() { m_toolBar->setWindowTitle(tr("Images")); m_zoomInAct->setText(tr("Zoom &In")); m_zoomOutAct->setText(tr("Zoom &Out")); m_resetZoomAct->setText(tr("Reset Zoom")); }
アプリケーション起動
- メインアプリケーション:
main.cppでは、ウィンドウを表示する前にアプリケーションの翻訳をロードします:Translator mainTranslator; mainTranslator.setBaseName("docviewer"_L1); mainTranslator.install(); - プラグイン: 各プラグインは
init()関数の中でAbstractViewer::setTranslationBaseName()を呼び出し、翻訳ファイル名でTranslatorを初期化し、現在の言語の翻訳をインストールします。void ImageViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow) ... setTranslationBaseName("imgviewer"_L1); ...
ランタイムの言語切り替え
ランタイム言語の切り替えは、2つの方法で行うことができます:
- メニューHelp >Language:
QMenuアイテムをクリックするとMainWindow::onActionSwitchLanguage()がトリガーされ、新しい言語がインストールされ、メインアプリケーションとプラグインが再翻訳されます:void MainWindow::onActionSwitchLanguage(QLocale::Language lang) { m_translator.setLanguage(lang); m_translator.install(); ui->retranslateUi(this); const auto viewerList = m_factory->viewers(); for (AbstractViewer *viewer : viewerList) viewer->updateTranslation(lang); statusBar()->clearMessage(); }
- 実行時にシステム全体の言語を切り替えます:アプリケーションはイベントをリッスンし、
QEvent::LocaleChangeイベントでMainWindow::onActionSwitchLanguage()を呼び出すことで、これに反応することができます。void MainWindow::changeEvent(QEvent *event) { if (event->type() == QEvent::LocaleChange) onActionSwitchLanguage(QLocale::system().language()); QMainWindow::changeEvent(event); }
ソース・ファイル
すべてのQtサンプルも参照してください 。
© 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.