maxpayne
maxpayne

Reputation: 1111

How to sort file sizes that are in human readable format?

How to sort the following numbers that are in the column of QTableView? I use QSortFilterProxyModel for sorting.

123 B
1 KB
131 KB
3 GB
322 GB
337 KB
3 MB
555 KB
52 TB

Upvotes: 2

Views: 439

Answers (2)

cbuchart
cbuchart

Reputation: 11555

Basically subclass QSortFilterProxyModel and implement lessThan.

Also, it would be better if you use a separate model's role to store the actual size in bytes instead of converting the displayed value. In this case, the comparison method would be something like

class MySortFilterProxyModel : public QSortFilterProxyModel
{
public:
  MySortFilterProxyModel(QObject *parent = 0);

protected:
  bool lessThan(const QModelIndex &left, const QModelIndex &right) const override {
    const auto left_bytes = left.data(SIZE_IN_BYTES_ROLE).toULongLong();
    const auto right_bytes = right.data(SIZE_IN_BYTES_ROLE).toULongLong();

    return left_bytes < right_bytes;
  }
};

If, for any reason, you still use the display role, you should get the text from the indices and convert the string to bytes before comparing. For the conversion function, you have several options, like splitting the string into the number and the unit, and then using a look-up table to get the multiplier factor of the unit.

class MySortFilterProxyModel : public QSortFilterProxyModel
{
public:
  MySortFilterProxyModel(QObject *parent = 0);

protected:
  bool lessThan(const QModelIndex &left, const QModelIndex &right) const override {
    const auto left_bytes = toBytes(left.data().toString());
    const auto right_bytes = toBytes(right.data().toString());

    return left_bytes < right_bytes;
  }

private:
  static int64_t toBytes(const QString& size)
  {
    static const QMap<QString, int64_t> s_sizes = {
      {"b", 1},
      {"kb", 1024},
      {"mb", 1024 * 1024},
      {"gb", 1024 * 1024 * 1204},
      {"tb", 1024 * 1024 * 1204 * 1024},
    };

    const auto tokens = size.toLower().split(' ', QString::SkipEmptyParts);
    if (tokens.size() == 0) return 0; // error
    const auto number = tokens[0].toInt();
    if (tokens.size() == 1) return number; // assume bytes
    return number * s_sizes.value(tokens[1], 0); // 0 for unknown units
  }
};

You can check a more complete example of how to subclass QSortFilterProxyModel here.

Upvotes: 2

Yuriy Rusinov
Yuriy Rusinov

Reputation: 61

When I have to define my comparison I write some model inherited from QSortFilterProxyModel and override virtual function QSortFilterProxyModel::lessThan.

Upvotes: 0

Related Questions