Qt5再入門: FilePageサブクラスの作成
前回の記事で、FilePageというページクラスの親となるTabPageクラスを定義しました。今回は、これを元にFilePageサブクラスを作成していきます。
まずはヘッダーファイルです。
// filepage.h
#ifndef FILEPAGE_H
#define FILEPAGE_H
#include "tabpage.h"
#include <nxpp/nxpp_database.hpp>
/**
* @brief ファイルページクラス(DB、ディレクトリ用)
*/
class FilePage : public TabPage
{
Q_OBJECT
nxpp::Lmbcs path_;
nxpp::Lmbcs server_;
public:
/**
* @brief コンストラクタ
* @param path パス
* @param server サーバ
* @param parent 親ウィジェット
*/
FilePage(
const nxpp::Lmbcs &path,
const nxpp::Lmbcs &server,
QWidget *parent = nullptr
);
/**
* @brief 同じファイルかどうか
* @param path パス
* @param server サーバ
* @return 同じファイルパスなら真
*/
bool equalsTo(
const nxpp::Lmbcs &path,
const nxpp::Lmbcs &server
) const;
/**
* @brief ページの書き込み
*/
virtual void loadMainModel() override;
/**
* @brief ローダー関数オブジェクト用抽象クラス
*/
class Loader {
public:
/**
* @param pPage ページへのポインタ
* @param dbPtr データベースポインタ
*/
virtual void operator ()(FilePage *pPage, nxpp::DbPtr dbPtr) = 0;
/**
* @brief データベース/ディレクトリ共通のパス行追加
* @param pPage ページへのポインタ
* @param dbPtr データベースポインタ
*/
void addPaths(FilePage *pPage, nxpp::DbPtr dbPtr);
};
/**
* @brief アップデーター関数オブジェクト用抽象クラス
*/
class Updater {
public:
/**
* @param pPage ページへのポインタ
* @param dbPtr データベースポインタ
* @param item 更新されたアイテム(セル)
*/
virtual void operator ()(
FilePage *pPage,
nxpp::DbPtr dbPtr,
QStandardItem *item
) = 0;
};
public slots:
/**
* @brief アイテム更新スロット
* @param item 更新されたアイテム(セル)
*/
void updateItem(QStandardItem *item);
};
#endif // FILEPAGE_H
FilePageクラスは、ブックマークに対応するページクラスなので、パスとサーバー名を受け取って内部に保持します。
また、一度開いたFilePageクラスは、同じ内容であれば別のオブジェクトとせず、すでに開いたページを再表示させたいので、同値性を確認できるようにequalsToというメソッドを用意しています。
loadMainModelメソッドは、TabPageクラスからのサブクラスとして実装必須の仮想関数です。
次に、FilePage::Loaderクラスはローダーとして機能する関数オブジェクトの抽象クラスです。ローダーとは、FilePageが持っているデータ(パスとサーバー名)を元に、メインモデルに対して行データを追加します。FilePage::loadMainModelが呼び出します。
さらにもう一つ、FilePage::Updaterクラスも同じく関数オブジェクト用抽象クラスですが、こちらはテーブル上でデータを更新した時に呼び出します。直後に定義されているスロットメソッド、updateItemから呼び出します。
次にソースファイルです。
// filepage.cpp
#include "filepage/directorypage.h"
#include "filepage/databasepage.h"
#include <nxpp/qt/nxpp_qt_lmbcs.hpp>
FilePage::FilePage(
const nxpp::Lmbcs &path,
const nxpp::Lmbcs &server,
QWidget *parent
)
: TabPage(parent)
, path_(path)
, server_(server)
{
// 更新シグナルをメインモデルのロードに接続
connect(this, &TabPage::refresh,
this, &FilePage::loadMainModel
);
// モデルの更新シグナルをファイルページのアイテム更新スロットに接続
connect(
mainModel(), &QStandardItemModel::itemChanged,
this, &FilePage::updateItem
);
}
bool FilePage::equalsTo(
const nxpp::Lmbcs &path,
const nxpp::Lmbcs &server
) const {
return path == path_ && server == server_;
}
void FilePage::loadMainModel() {
try {
// データベース/ディレクトリを開く
auto dbPtr = nxpp::Db::open(path_, server_);
// ローダーポインタを作成
QScopedPointer<Loader> loader;
// 開いたファイルに応じてローダーを設定
if (dbPtr->isDirectory()) {
loader.reset(new DirectoryLoader());
} else {
loader.reset(new DatabaseLoader());
}
// メインモデルを初期化
initMainModel();
// ローダーを実行
(*loader)(this, dbPtr);
// ステータスを表示
emit showStatus(tr("Loaded FilePage"));
// コンソールに表示
emit consoleLog(
tr("Loaded file (%1:%2)")
.arg(fromLmbcsQ(path_))
.arg(fromLmbcsQ(server_))
);
}
// Notesステータスがスローされたらそのエラーメッセージに変換して表示
catch (nxpp::Status &status) {
emit consoleLog(nxpp::qt::fromStatus(status.error()));
}
// それ以外の例外ならそのメッセージを表示
catch (std::exception &ex) {
std::string what(ex.what());
emit consoleLog(QString::fromStdString(what));
}
}
void FilePage::updateItem(QStandardItem *item) {
try {
// データベース/ディレクトリをオープン
auto dbPtr = nxpp::Db::open(path_, server_);
// アップデーターを作成
QScopedPointer<Updater> updater;
if (!dbPtr->isDirectory()) {
updater.reset(new DatabaseUpdater());
}
if (updater.isNull()) return;
// アップデータを実行
(*updater)(this, dbPtr, item);
// ステータスを表示
emit showStatus(tr("Updated FilePage"));
}
// Notesステータスがスローされたらそのエラーメッセージに変換して表示
catch (nxpp::Status &status) {
emit consoleLog(nxpp::qt::fromStatus(status.error()));
}
// それ以外の例外ならそのメッセージを表示
catch (std::exception &ex) {
std::string what(ex.what());
emit consoleLog(QString::fromStdString(what));
}
}
void FilePage::Loader::addPaths(FilePage *pPage, nxpp::DbPtr dbPtr) {
// データベース/ディレクトリのパスを取得する。
auto paths = dbPtr->getPaths();
// テーブルに追加するデータリストを作成する。
QList<QPair<QString, QString>> list {
{ pPage->tr("Path/Canonical"), fromLmbcsQ(std::get<0>(paths)) },
{ pPage->tr("Path/Extended"), fromLmbcsQ(std::get<1>(paths)) }
};
// リストに沿ってテーブルに行を追加する。
for (auto pair : list) {
auto items = pPage->addRowToMainModel(pair.first, pair.second);
items.first->setEditable(false);
items.second->setEditable(false);
}
}
最初に、コンストラクタではrefreshシグナルをFilePage::loadMainModelに接続しています。また、メインモデルでアイテムが更新された時は、自身のupdateItemスロットメソッドを呼び出すようにしています。
次のloadMainModelでは、まずFilePageが保持するパス、サーバー名という2つのデータを元にデータベースを開きます。開いた情報を元に、これはディレクトリか、データベースかが区別され、実装するローダーを切り替えています(DirectoryLoaderとDatabaseLoaderは次回紹介する予定です)。あとは、メインモデルを初期化し、ローダーを呼び出します。ローダーが正常に実行されればテーブルも更新されます。
次のupdateItemは、テーブル内のデータ(=モデルアイテム)が変更された時に呼び出されます。ここでもディレクトリか、データベースかがチェックされ、データベースの時はアップデータを作成して、これを実行します。
最後のLoader::addPathsは、データベース/ディレクトリ共通の、パス情報をテーブルに追加するメソッドになります。
まとめ
今回は、Notes C APIの要素も少し含まれていましたが、ページウィジェットとしてシグナル/スロットの取り回しやモデル/ビューなどがメインかなと思い、「Qt5再入門」として掲載しました。
次回は、データベース/ディレクトリに特化した情報を扱って、テーブルに情報を追加したり、変更した内容をデータベースに書き込んだりする作業をしていきます。
この記事が気に入ったらサポートをしてみませんか?