通过bool变量扩展QFileSystemModel

时间:2018-05-02 10:20:59

标签: qt qml

我想扩展QFileSystemModel。我想有一个自定义角色IsSelectedRole,我可以存储一个bool值,它存储文件是否在QML TreeView中被选中。更确切地说,我不知道如何设置setData派生类的dataQFileSystemModel函数,比如存储数据的位置。我想让我自己的bool变量树应该可行,但我希望有一种更简单的方法。

1 个答案:

答案 0 :(得分:0)

Qt已经有一个存储项目选择的模型:QItemSelectionModel。小部件视图以及QML视图使用此模型。剩下的工作就是在视图模型中将来自该模型的数据叠加在QFileSystemModel的数据之上。

你绝对不应该从QFileSystemModel派生出来。 viewmodel仅维护单个视图的状态,并且应该是将您的角色覆盖在基础模型之上的代理。

代理不必假设有关底层模型的任何内容,并且可以在任何模型上工作,而不仅仅是QFileSystemModel

例如:

// https://github.com/KubaO/stackoverflown/tree/master/questions/filesystem-model-select-50132273
#include <QtWidgets>
#include <algorithm>

class SelectionProxy : public QIdentityProxyModel {
   Q_OBJECT
   Q_PROPERTY(QItemSelectionModel* selectionModel
              READ selectionModel WRITE setSelectionModel NOTIFY selectionModelChanged)
   Q_PROPERTY(QVector<int> roles READ roles WRITE setRoles)
   using self_t = SelectionProxy;
   using base_t = QIdentityProxyModel;
   using model_t = QItemSelectionModel;
   QPointer<QItemSelectionModel> m_model;
   QVector<QMetaObject::Connection> m_modelConnections;
   QVector<int> m_roles{IsSelectedRole};
   QModelIndex topLeft() const {
      return sourceModel()->index(0, 0);
   }
   QModelIndex bottomRight() const {
      return sourceModel()->index(sourceModel()->rowCount()-1, sourceModel()->columnCount()-1);
   }
   void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
      auto sel = selected;
      sel.merge(deselected, QItemSelectionModel::Select);
      for (auto &range : qAsConst(sel)) {
         auto topLeft = mapFromSource(range.topLeft());
         auto bottomRight = mapFromSource(range.bottomRight());
         emit dataChanged(topLeft, bottomRight, m_roles);
      }
   }
   void onModelChanged(QAbstractItemModel *model) {
      setSourceModel(model);
   }
   bool check(const QModelIndex &index, int role) const {
      return index.isValid() && m_model && m_roles.contains(role);
   }
public:
   static constexpr int IsSelectedRole = Qt::UserRole + 44;
   SelectionProxy(QObject *parent = {}) : QIdentityProxyModel(parent) {}
   QItemSelectionModel *selectionModel() const { return m_model; }
   virtual void setSelectionModel(QItemSelectionModel *model) {
      if (model == m_model) return;
      for (auto &conn : m_modelConnections)
         disconnect(conn);
      m_model = model;
      m_modelConnections = {
         connect(m_model, &model_t::selectionChanged, this, &self_t::onSelectionChanged),
         connect(m_model, &model_t::modelChanged, this, &self_t::onModelChanged) };
      setSourceModel(model->model());
      emit selectionModelChanged(m_model);
   }
   Q_SIGNAL void selectionModelChanged(QItemSelectionModel *);
   QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
      if (!check(index, role))
         return base_t::data(index, role);
      return m_model->isSelected(mapToSource(index));
   }
   bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override {
      if (!check(index, role))
         return base_t::setData(index, value, role);
      using Sel = QItemSelectionModel;
      m_model->select(mapToSource(index), value.toBool() ? Sel::SelectCurrent : (Sel::Deselect | Sel::Current));
      return true;
   }
   QVector<int> roles() const { return m_roles; }
   void setRoles(QVector<int> roles) {
      std::sort(roles.begin(), roles.end());
      if (roles == m_roles)
         return;
      std::swap(roles, m_roles);
      if (!m_model)
         return;
      QVector<int> allRoles;
      std::merge(roles.begin(), roles.end(), m_roles.begin(), m_roles.end(), std::back_inserter(allRoles));
      emit dataChanged(topLeft(), bottomRight(), allRoles);
   }
   void setRole(int role) {
      setRoles({role});
   }
};

#include "main.moc"

一个简单的演示器设置文件系统模型的两个视图;底部视图显示true / false,表示第一个视图的选择状态。

int main(int argc, char **argv) {
   QApplication app{argc, argv};
   QWidget win;
   QVBoxLayout layout{&win};
   QTreeView left, right;
   layout.addWidget(&left);
   layout.addWidget(&right);

   QFileSystemModel model;
   SelectionProxy selProxy;
   model.setRootPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
   auto rootPathIndex = model.index(model.rootPath());
   left.setModel(&model);
   left.setSelectionMode(QAbstractItemView::MultiSelection);
   left.setRootIndex(rootPathIndex);
   selProxy.setRole(Qt::DisplayRole);
   selProxy.setSelectionModel(left.selectionModel());
   right.setModel(&selProxy);
   right.setRootIndex(selProxy.mapFromSource(rootPathIndex));
   for (int col : {1,2,3})
      right.hideColumn(col);

   win.show();
   return app.exec();
}