更复杂的数据(More Complex Data)

实际工作中使用的模型数据通常比较复杂。所以需要自定义一些角色枚举方便视图通过属性查找数据。例如模型提供颜色数据不仅只是16进制字符串,在QML中也可以是来自HSV颜色模型的色调,饱和度和亮度,以“model.hue”,“model.saturation”和“model.brightness”作为参数。

  1. #ifndef ROLEENTRYMODEL_H
  2. #define ROLEENTRYMODEL_H
  3. #include <QtCore>
  4. #include <QtGui>
  5. class RoleEntryModel : public QAbstractListModel
  6. {
  7. Q_OBJECT
  8. public:
  9. // Define the role names to be used
  10. enum RoleNames {
  11. NameRole = Qt::UserRole,
  12. HueRole = Qt::UserRole+2,
  13. SaturationRole = Qt::UserRole+3,
  14. BrightnessRole = Qt::UserRole+4
  15. };
  16. explicit RoleEntryModel(QObject *parent = 0);
  17. ~RoleEntryModel();
  18. // QAbstractItemModel interface
  19. public:
  20. virtual int rowCount(const QModelIndex &parent) const override;
  21. virtual QVariant data(const QModelIndex &index, int role) const override;
  22. protected:
  23. // return the roles mapping to be used by QML
  24. virtual QHash<int, QByteArray> roleNames() const override;
  25. private:
  26. QList<QColor> m_data;
  27. QHash<int, QByteArray> m_roleNames;
  28. };
  29. #endif // ROLEENTRYMODEL_H

在头文件中,我们为QML添加了数据角色枚举的映射。当QML尝试访问一个模型中的属性时(例如“model.name”),链表视图将会在映射中查询“name”然后向模型申请使用NameRole角色枚举的数据。用户在定义角色枚举时应该从Qt::UserRole开始,并且对于每个模型需要保证唯一。

  1. #include "roleentrymodel.h"
  2. RoleEntryModel::RoleEntryModel(QObject *parent)
  3. : QAbstractListModel(parent)
  4. {
  5. // Set names to the role name hash container (QHash<int, QByteArray>)
  6. // model.name, model.hue, model.saturation, model.brightness
  7. m_roleNames[NameRole] = "name";
  8. m_roleNames[HueRole] = "hue";
  9. m_roleNames[SaturationRole] = "saturation";
  10. m_roleNames[BrightnessRole] = "brightness";
  11. // Append the color names as QColor to the data list (QList<QColor>)
  12. for(const QString& name : QColor::colorNames()) {
  13. m_data.append(QColor(name));
  14. }
  15. }
  16. RoleEntryModel::~RoleEntryModel()
  17. {
  18. }
  19. int RoleEntryModel::rowCount(const QModelIndex &parent) const
  20. {
  21. Q_UNUSED(parent);
  22. return m_data.count();
  23. }
  24. QVariant RoleEntryModel::data(const QModelIndex &index, int role) const
  25. {
  26. int row = index.row();
  27. if(row < 0 || row >= m_data.count()) {
  28. return QVariant();
  29. }
  30. const QColor& color = m_data.at(row);
  31. qDebug() << row << role << color;
  32. switch(role) {
  33. case NameRole:
  34. // return the color name as hex string (model.name)
  35. return color.name();
  36. case HueRole:
  37. // return the hue of the color (model.hue)
  38. return color.hueF();
  39. case SaturationRole:
  40. // return the saturation of the color (model.saturation)
  41. return color.saturationF();
  42. case BrightnessRole:
  43. // return the brightness of the color (model.brightness)
  44. return color.lightnessF();
  45. }
  46. return QVariant();
  47. }
  48. QHash<int, QByteArray> RoleEntryModel::roleNames() const
  49. {
  50. return m_roleNames;
  51. }

现在实现只是改变了两个地方。首先是初始化。我们使用QColor数据类型初始化数据链表。此外我们还定义了我们自己的角色名称映射实现QML的访问。这个映射将在后面的::roleNames函数中返回。

第二个变化是在::data函数中。我们确保能够覆盖到其它的角色枚举(例如色调,饱和度,亮度)。没有可以从颜色中获取SVG名称的方法,由于一个颜色可以替代任何颜色,但SVG名称是受限的。所以我们忽略掉这点。我们需要创建一个结构体{ QColor, QString }来存储名称,这样可以鉴别已被命名的颜色。

在注册类型完成后,我们可以使用模型了,可以将它的条目显示在我们的用户界面中。

  1. ListView {
  2. id: view
  3. anchors.fill: parent
  4. model: RoleEntryModel {}
  5. focus: true
  6. delegate: ListDelegate {
  7. text: 'hsv(' +
  8. Number(model.hue).toFixed(2) + ',' +
  9. Number(model.saturation).toFixed() + ',' +
  10. Number(model.brightness).toFixed() + ')'
  11. color: model.name
  12. }
  13. highlight: ListHighlight { }
  14. }

我们将返回的类型转换为JS数字类型,这样可以使用定点标记来格式化数字。代码中应当避免直接调用数字(例如model.saturation.toFixed(2))。选择哪种格式取决于你的输入数据。