Wtのファイル選択ダイアログ



仕事のために、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 { //Unix root '/' return 1; } return 0; } WModelIndex FileSystemModel::parent(const WModelIndex& index) const { if (!index.isValid()) { return WModelIndex(); } auto node = static_cast<TreeNode*>(index.internalPointer()); if (node->parent == nullptr) { return WModelIndex(); } if (node->parent->parent == nullptr) { return createIndex(0, 0, m_impl->root.get()); } const auto grand = node->parent->parent; const auto parent = node->parent; const auto res = std::lower_bound(grand->children.cbegin(), grand->children.cend(), parent); const size_t row = std::distance(grand->children.cbegin(), res); return createIndex(row, 0, parent); } boost::any FileSystemModel::data(const WModelIndex &index, int role) const { if (!index.isValid()) { return boost::any(); } auto node = static_cast<TreeNode*>(index.internalPointer()); if (node == nullptr) { return boost::any(); } switch (role) { case DisplayRole: { return node->filename; } case Path: { return node->path; } case DecorationRole: { try { return FILE_SYSTEM_ICONS.at(node->type); } catch (...) { return boost::any(); } } break; default: return boost::any(); } } WModelIndex FileSystemModel::index(int row, int column, const Wt::WModelIndex& parent) const { if (!parent.isValid()) { return createIndex(0, 0, m_impl->root.get()); } TreeNode* pNode = static_cast<TreeNode*>(parent.internalPointer()); if (pNode == nullptr) { return WModelIndex(); } return createIndex(row, column, pNode->children[row]); } boost::any FileSystemModel::headerData(int section, Orientation orientation, int role) const { if (role == DisplayRole && orientation == Horizontal) { return "File name"; } return boost::any(); } 


ファイル選択ダイアログ



ファイル選択ダイアログは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; } 

Source: https://habr.com/ru/post/J275453/


All Articles