仕事のために、Wt:ウィザード(デバイス上のディレクトリとファイルを選択するためのダイアログ)でいくつかのコンポーネントを作成する必要がありました。 私はそれを
GitHubに置くことにしました。おそらく誰かがそれを必要とするでしょう。 猫の下には、Wt + Boostの簡単なファイル選択ダイアログがあります。
Wtはウィジェット指向のフレームワークであり、APIによってQtに似ています。 WtはQtと「類似」しているが、Qtではないという事実。
データモデル
抽象モデルの中心
Wt :: WAbstractItemModel (ほぼ
QAbstractItemModelに似てい
ます ):
class FileSystemModel: public Wt::WAbstractItemModel { public: enum Roles { Name = Wt::UserRole + 1, Path = Wt::UserRole }; FileSystemModel(Wt::WObject* parent = nullptr); bool isFile(const Wt::WModelIndex& index) const; bool isDir(const Wt::WModelIndex& index) const; virtual int columnCount(const Wt::WModelIndex& parent = Wt::WModelIndex()) const; virtual int rowCount(const Wt::WModelIndex& parent = Wt::WModelIndex()) const; virtual Wt::WModelIndex parent(const Wt::WModelIndex& index) const; virtual boost::any data(const Wt::WModelIndex& index, int role = Wt::DisplayRole) const; virtual Wt::WModelIndex index(int row, int column, const Wt::WModelIndex& parent = Wt::WModelIndex()) const; virtual boost::any headerData(int section, Orientation orientation = Horizontal, int role = DisplayRole) const; private: std::unique_ptr<FileSystemModelPrivate> m_impl; };
ツリーモデルを実装するときは、ツリー自体を用意すると便利です。 ツリー要素は、
TreeNode構造によって表されます。
struct TreeNode { enum Type { File, Directory }; std::string filename; std::string path; TreeNode* parent; std::vector<TreeNode*> children; Type type; bool childrenLoaded; TreeNode(TreeNode* prnt = nullptr) : parent(prnt), type(Directory), childrenLoaded(false) { if (parent) { parent->children.push_back(this); } } ~TreeNode() { parent = nullptr; for (TreeNode* child : children) { delete child; } children.clear(); } size_t loadChildren() { if (childrenLoaded) { return children.size(); } boost::filesystem::path p(path); childrenLoaded = true; size_t count = 0; try { for (directory_iterator iter(p), end; iter != end; ++iter) { auto itm = new TreeNode(this); itm->filename = iter->path().filename().string(); itm->path = iter->path().string(); itm->type = is_directory(iter->path()) ? TreeNode::Directory : TreeNode::File; ++count; } std::sort(children.begin(), children.end(), [](const TreeNode* a, const TreeNode* b) { return a->filename<b->filename; }); return count; } catch (const filesystem_error&) { return 0; } } };
loadChildrenメソッドは、ファイルシステムを読み取り、ノードをロードします。 ノードはオンデマンドで、つまり
rowCountがモデルから
要求されたときにロードされます。 ツリーのルートは
FileSystemModelPrivateコンストラクターで作成され
ます 。
struct FileSystemModelPrivate { FileSystemModelPrivate() : root(new TreeNode) { root->filename = "/"; root->path = "/"; } std::unique_ptr<TreeNode> root; };
Wtには、Qtと同様、モデルインデックス(WModelIndex)
を作成し、ポインターを
TreeNodeに渡すことができる
createIndexメソッド
があります。
残りのコードは非常に簡単です。
FileSystemModel::FileSystemModel(WObject* parent) : WAbstractItemModel(parent), m_impl(new FileSystemModelPrivate) { } int FileSystemModel::columnCount(const WModelIndex& parent) const { return 1; } int FileSystemModel::rowCount(const WModelIndex& parent) const { if (parent.isValid()) { TreeNode* node = static_cast<TreeNode*>(parent.internalPointer()); if (node == nullptr || node->type == TreeNode::File) { return 0; } return node->childrenLoaded ? node->children.size() : node->loadChildren(); } else {
ファイル選択ダイアログ
ファイル選択ダイアログは
Wt :: WDialogから継承され、インターフェースがあります:
class FileDialog: public Wt::WDialog { public: FileDialog(WObject* parent = nullptr); virtual void accept(); Wt::WStringList selected() const; private: Wt::WTreeView* m_view; FileSystemModel* m_fs; };
FileDialogクラスには、モデルと
Wt :: WTreeViewのツリービューが含まれています。
コンストラクターについて考えます。
FileDialog::FileDialog(WObject* parent) : WDialog(parent), m_view(new WTreeView()), m_fs(new FileSystemModel(this)) { setWindowTitle("Selecting files and directories"); auto cancel = new WPushButton("Cancel", footer()); cancel->clicked().connect(this, &WDialog::reject); m_view->setModel(m_fs); m_view->setSelectionBehavior(SelectItems); m_view->setSelectionMode(ExtendedSelection); auto select = new WPushButton("Select", footer()); select->clicked().connect(this, &FileDialog::accept); m_view->setSortingEnabled(false); m_view->setHeaderHeight(0); m_view->expandToDepth(1); auto layout = new WVBoxLayout; layout->addWidget(m_view); contents()->setLayout(layout); resize(600, 500); }
コンストラクターの本体内には、フッターに配置される2つのボタンが作成されます。フッターは、コンテンツの代わりに配置される垂直レイアウトです。
WTreeViewの寸法を設定するには垂直レイアウトが必要です。そうしないと、ビューがダイアログボックスを超える場合があります。
建設業
cancel->clicked().connect(this, &WDialog::reject);
これは、
Boost.Signals2 (またはBoostのバージョンに応じて、Boost.Signals)に基づくシグナル/スロットメカニズムです。 残りの2つの方法は簡単です
void FileDialog::accept() { const auto indxs = m_view->selectedIndexes(); if (indxs.size() > 0) { WDialog::accept(); } } Wt::WStringList FileDialog::selected() const { WStringList list; const auto indxs = m_view->selectedIndexes(); for (auto indx : indxs) { const WString pt = boost::any_cast<std::string>( indx.data(FileSystemModel::Path)); list << pt; } return list; }