Add QLocale::formattedDataSize and consolidate use cases
It should be easier to translate sizes in bytes to human-readable strings consistently rather than having to repeat this code (and the string translations) in various places. The FileDialog in QtQuick.Controls has a use for this, too. [ChangeLog][QtCore][QLocale] Added QLocale::formattedDataSize() for formatting quantities of bytes as kB, MB, GB etc. Done-with: Edward Welbourne <edward.welbourne@qt.io> Change-Id: I27bca146c3eba90fa7a5d52ef6626ce85723e3f0 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
ccca8c9435
commit
9d23aebb27
@ -52,25 +52,10 @@
|
|||||||
#include "storagemodel.h"
|
#include "storagemodel.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QLocale>
|
||||||
#include <qmath.h>
|
#include <qmath.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
static QString sizeToString(qint64 size)
|
|
||||||
{
|
|
||||||
static const char *const strings[] = { "b", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
|
|
||||||
|
|
||||||
if (size <= 0)
|
|
||||||
return StorageModel::tr("0 b");
|
|
||||||
|
|
||||||
double power = std::log((double)size)/std::log(1024.0);
|
|
||||||
int intPower = (int)power;
|
|
||||||
intPower = intPower >= 8 ? 8 - 1 : intPower;
|
|
||||||
|
|
||||||
double normSize = size / std::pow(1024.0, intPower);
|
|
||||||
//: this should expand to "1.23 GB"
|
|
||||||
return StorageModel::tr("%1 %2").arg(normSize, 0, 'f', intPower > 0 ? 2 : 0).arg(strings[intPower]);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageModel::StorageModel(QObject *parent) :
|
StorageModel::StorageModel(QObject *parent) :
|
||||||
QAbstractTableModel(parent),
|
QAbstractTableModel(parent),
|
||||||
m_volumes(QStorageInfo::mountedVolumes())
|
m_volumes(QStorageInfo::mountedVolumes())
|
||||||
@ -106,11 +91,11 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
|||||||
case ColumnFileSystemName:
|
case ColumnFileSystemName:
|
||||||
return volume.fileSystemType();
|
return volume.fileSystemType();
|
||||||
case ColumnTotal:
|
case ColumnTotal:
|
||||||
return sizeToString(volume.bytesTotal());
|
return QLocale().formattedDataSize(volume.bytesTotal());
|
||||||
case ColumnFree:
|
case ColumnFree:
|
||||||
return sizeToString(volume.bytesFree());
|
return QLocale().formattedDataSize(volume.bytesFree());
|
||||||
case ColumnAvailable:
|
case ColumnAvailable:
|
||||||
return sizeToString(volume.bytesAvailable());
|
return QLocale().formattedDataSize(volume.bytesAvailable());
|
||||||
case ColumnIsReady:
|
case ColumnIsReady:
|
||||||
return volume.isReady();
|
return volume.isReady();
|
||||||
case ColumnIsReadOnly:
|
case ColumnIsReadOnly:
|
||||||
@ -121,6 +106,7 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (role == Qt::ToolTipRole) {
|
} else if (role == Qt::ToolTipRole) {
|
||||||
|
QLocale locale;
|
||||||
const QStorageInfo &volume = m_volumes.at(index.row());
|
const QStorageInfo &volume = m_volumes.at(index.row());
|
||||||
return tr("Root path : %1\n"
|
return tr("Root path : %1\n"
|
||||||
"Name: %2\n"
|
"Name: %2\n"
|
||||||
@ -140,9 +126,9 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
|||||||
arg(volume.displayName()).
|
arg(volume.displayName()).
|
||||||
arg(QString::fromUtf8(volume.device())).
|
arg(QString::fromUtf8(volume.device())).
|
||||||
arg(QString::fromUtf8(volume.fileSystemType())).
|
arg(QString::fromUtf8(volume.fileSystemType())).
|
||||||
arg(sizeToString(volume.bytesTotal())).
|
arg(locale.formattedDataSize(volume.bytesTotal())).
|
||||||
arg(sizeToString(volume.bytesFree())).
|
arg(locale.formattedDataSize(volume.bytesFree())).
|
||||||
arg(sizeToString(volume.bytesAvailable())).
|
arg(locale.formattedDataSize(volume.bytesAvailable())).
|
||||||
arg(volume.isReady() ? tr("true") : tr("false")).
|
arg(volume.isReady() ? tr("true") : tr("false")).
|
||||||
arg(volume.isReadOnly() ? tr("true") : tr("false")).
|
arg(volume.isReadOnly() ? tr("true") : tr("false")).
|
||||||
arg(volume.isValid() ? tr("true") : tr("false")).
|
arg(volume.isValid() ? tr("true") : tr("false")).
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "qvariant.h"
|
#include "qvariant.h"
|
||||||
#include "qstringbuilder.h"
|
#include "qstringbuilder.h"
|
||||||
#include "private/qnumeric_p.h"
|
#include "private/qnumeric_p.h"
|
||||||
|
#include <cmath>
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
# include <qt_windows.h>
|
# include <qt_windows.h>
|
||||||
# include <time.h>
|
# include <time.h>
|
||||||
@ -3780,6 +3781,76 @@ QString QLocale::toCurrencyString(double value, const QString &symbol, int preci
|
|||||||
return format.arg(str, sym);
|
return format.arg(str, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.10
|
||||||
|
|
||||||
|
\enum QLocale::DataSizeFormat
|
||||||
|
|
||||||
|
Specifies the format for representation of data quantities.
|
||||||
|
|
||||||
|
\omitvalue DataSizeBase1000
|
||||||
|
\omitvalue DataSizeSIQuantifiers
|
||||||
|
\value DataSizeIecFormat format using base 1024 and IEC prefixes: KiB, MiB, GiB, ...
|
||||||
|
\value DataSizeTraditionalFormat format using base 1024 and SI prefixes: kB, MB, GB, ...
|
||||||
|
\value DataSizeSIFormat format using base 1000 and SI prefixes: kB, MB, GB, ...
|
||||||
|
|
||||||
|
\sa formattedDataSize()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 5.10
|
||||||
|
|
||||||
|
Converts a size in bytes to a human-readable localized string, expressed in
|
||||||
|
a unit for which the numeric portion is at least 1 but as low as
|
||||||
|
possible. For example if \a bytes is 16384, \a precision is 2, and \a format
|
||||||
|
is \c DataSizeIecFormat (the default), this function returns "16.00 KiB";
|
||||||
|
for 1330409069609 bytes it returns "1.21 GiB"; and so on. If \a format is \c
|
||||||
|
DataSizeIecFormat or \c DataSizeTraditionalFormat, the given number of bytes
|
||||||
|
is divided by a power of 1024, with result less than 1024; for \c
|
||||||
|
DataSizeSIFormat, it is divided by a power of 1000, with result less than
|
||||||
|
1000. DataSizeIecFormat uses the new IEC standard quantifiers Ki, Mi and so
|
||||||
|
on, whereas DataSizeSIFormat uses and DataSizeTraditionalFormat abuses the
|
||||||
|
older SI quantifiers k, M, etc.
|
||||||
|
|
||||||
|
\sa refresh(), caching()
|
||||||
|
*/
|
||||||
|
QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats format)
|
||||||
|
{
|
||||||
|
int power, base = 1000;
|
||||||
|
if (!bytes) {
|
||||||
|
power = 0;
|
||||||
|
} else if (format & DataSizeBase1000) {
|
||||||
|
power = int(std::log10(qAbs(bytes)) / 3);
|
||||||
|
} else { // Compute log2(bytes) / 10:
|
||||||
|
power = int((63 - qCountLeadingZeroBits(quint64(qAbs(bytes)))) / 10);
|
||||||
|
base = 1024;
|
||||||
|
}
|
||||||
|
// Only go to doubles if we'll be using a quantifier:
|
||||||
|
const QString number = power
|
||||||
|
? toString(bytes / std::pow(double(base), power), 'f', qMin(precision, 3 * power))
|
||||||
|
: toString(bytes);
|
||||||
|
|
||||||
|
// We don't support sizes in units larger than exbibytes because
|
||||||
|
// the number of bytes would not fit into qint64.
|
||||||
|
Q_ASSERT(power <= 6 && power >= 0);
|
||||||
|
QString unit;
|
||||||
|
if (power > 0) {
|
||||||
|
quint16 index, size;
|
||||||
|
if (format & DataSizeSIQuantifiers) {
|
||||||
|
index = d->m_data->m_byte_si_quantified_idx;
|
||||||
|
size = d->m_data->m_byte_si_quantified_size;
|
||||||
|
} else {
|
||||||
|
index = d->m_data->m_byte_iec_quantified_idx;
|
||||||
|
size = d->m_data->m_byte_iec_quantified_size;
|
||||||
|
}
|
||||||
|
unit = getLocaleListData(byte_unit_data + index, size, power - 1);
|
||||||
|
} else {
|
||||||
|
unit = getLocaleData(byte_unit_data + d->m_data->m_byte_idx, d->m_data->m_byte_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return number + QLatin1Char(' ') + unit;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\since 4.8
|
\since 4.8
|
||||||
|
|
||||||
|
@ -913,6 +913,19 @@ public:
|
|||||||
CurrencyDisplayName
|
CurrencyDisplayName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DataSizeFormat {
|
||||||
|
// Single-bit values, for internal use.
|
||||||
|
DataSizeBase1000 = 1, // use factors of 1000 instead of IEC's 1024;
|
||||||
|
DataSizeSIQuantifiers = 2, // use SI quantifiers instead of IEC ones.
|
||||||
|
|
||||||
|
// Flags values for use in API:
|
||||||
|
DataSizeIecFormat = 0, // base 1024, KiB, MiB, GiB, ...
|
||||||
|
DataSizeTraditionalFormat = DataSizeSIQuantifiers, // base 1024, kB, MB, GB, ...
|
||||||
|
DataSizeSIFormat = DataSizeBase1000 | DataSizeSIQuantifiers // base 1000, kB, MB, GB, ...
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(DataSizeFormats, DataSizeFormat)
|
||||||
|
Q_FLAG(DataSizeFormats)
|
||||||
|
|
||||||
QLocale();
|
QLocale();
|
||||||
QLocale(const QString &name);
|
QLocale(const QString &name);
|
||||||
QLocale(Language language, Country country = AnyCountry);
|
QLocale(Language language, Country country = AnyCountry);
|
||||||
@ -1045,6 +1058,8 @@ public:
|
|||||||
{ return toCurrencyString(double(i), symbol, precision); }
|
{ return toCurrencyString(double(i), symbol, precision); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QString formattedDataSize(qint64 bytes, int precision = 2, DataSizeFormats format = DataSizeIecFormat);
|
||||||
|
|
||||||
QStringList uiLanguages() const;
|
QStringList uiLanguages() const;
|
||||||
|
|
||||||
bool operator==(const QLocale &other) const;
|
bool operator==(const QLocale &other) const;
|
||||||
|
@ -774,21 +774,7 @@ QString QFileSystemModelPrivate::size(const QModelIndex &index) const
|
|||||||
|
|
||||||
QString QFileSystemModelPrivate::size(qint64 bytes)
|
QString QFileSystemModelPrivate::size(qint64 bytes)
|
||||||
{
|
{
|
||||||
// According to the Si standard KB is 1000 bytes, KiB is 1024
|
return QLocale::system().formattedDataSize(bytes);
|
||||||
// but on windows sizes are calculated by dividing by 1024 so we do what they do.
|
|
||||||
const qint64 kb = 1024;
|
|
||||||
const qint64 mb = 1024 * kb;
|
|
||||||
const qint64 gb = 1024 * mb;
|
|
||||||
const qint64 tb = 1024 * gb;
|
|
||||||
if (bytes >= tb)
|
|
||||||
return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
|
|
||||||
if (bytes >= gb)
|
|
||||||
return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
|
|
||||||
if (bytes >= mb)
|
|
||||||
return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
|
|
||||||
if (bytes >= kb)
|
|
||||||
return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
|
|
||||||
return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1308,22 +1308,7 @@ QString QDirModelPrivate::size(const QModelIndex &index) const
|
|||||||
// Nautilus - "9 items" (the number of children)
|
// Nautilus - "9 items" (the number of children)
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to the Si standard KB is 1000 bytes, KiB is 1024
|
return QLocale::system().formattedDataSize(n->info.size());
|
||||||
// but on windows sizes are calulated by dividing by 1024 so we do what they do.
|
|
||||||
const quint64 kb = 1024;
|
|
||||||
const quint64 mb = 1024 * kb;
|
|
||||||
const quint64 gb = 1024 * mb;
|
|
||||||
const quint64 tb = 1024 * gb;
|
|
||||||
quint64 bytes = n->info.size();
|
|
||||||
if (bytes >= tb)
|
|
||||||
return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
|
|
||||||
if (bytes >= gb)
|
|
||||||
return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
|
|
||||||
if (bytes >= mb)
|
|
||||||
return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
|
|
||||||
if (bytes >= kb)
|
|
||||||
return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
|
|
||||||
return QFileSystemModel::tr("%1 byte(s)").arg(QLocale().toString(bytes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QDirModelPrivate::type(const QModelIndex &index) const
|
QString QDirModelPrivate::type(const QModelIndex &index) const
|
||||||
|
@ -137,6 +137,9 @@ private slots:
|
|||||||
void textDirection_data();
|
void textDirection_data();
|
||||||
void textDirection();
|
void textDirection();
|
||||||
|
|
||||||
|
void formattedDataSize_data();
|
||||||
|
void formattedDataSize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
|
QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
|
||||||
QString m_sysapp;
|
QString m_sysapp;
|
||||||
@ -2473,5 +2476,80 @@ void tst_QLocale::textDirection()
|
|||||||
QCOMPARE(locale.textDirection() == Qt::RightToLeft, rightToLeft);
|
QCOMPARE(locale.textDirection() == Qt::RightToLeft, rightToLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QLocale::formattedDataSize_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QLocale::Language>("language");
|
||||||
|
QTest::addColumn<int>("decimalPlaces");
|
||||||
|
QTest::addColumn<QLocale::DataSizeFormats>("units");
|
||||||
|
QTest::addColumn<int>("bytes");
|
||||||
|
QTest::addColumn<QString>("output");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
QLocale::Language lang;
|
||||||
|
const char *bytes;
|
||||||
|
const char abbrev;
|
||||||
|
const char sep; // decimal separator
|
||||||
|
} data[] = {
|
||||||
|
{ "English", QLocale::English, "bytes", 'B', '.' },
|
||||||
|
{ "French", QLocale::French, "octets", 'o', ',' },
|
||||||
|
{ "C", QLocale::C, "bytes", 'B', '.' }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto row : data) {
|
||||||
|
#define ROWB(id, deci, num, text) \
|
||||||
|
QTest::addRow("%s-%s", row.name, id) \
|
||||||
|
<< row.lang << deci << format \
|
||||||
|
<< num << (QString(text) + QChar(' ') + QString(row.bytes))
|
||||||
|
#define ROWQ(id, deci, num, head, tail) \
|
||||||
|
QTest::addRow("%s-%s", row.name, id) \
|
||||||
|
<< row.lang << deci << format \
|
||||||
|
<< num << (QString(head) + QChar(row.sep) + QString(tail) + QChar(row.abbrev))
|
||||||
|
|
||||||
|
// Metatype system fails to handle raw enum members as format; needs variable
|
||||||
|
{
|
||||||
|
const QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat;
|
||||||
|
ROWB("IEC-0", 2, 0, "0");
|
||||||
|
ROWB("IEC-10", 2, 10, "10");
|
||||||
|
ROWQ("IEC-12Ki", 2, 12345, "12", "06 Ki");
|
||||||
|
ROWQ("IEC-16Ki", 2, 16384, "16", "00 Ki");
|
||||||
|
ROWQ("IEC-1235k", 2, 1234567, "1", "18 Mi");
|
||||||
|
ROWQ("IEC-1374k", 2, 1374744, "1", "31 Mi");
|
||||||
|
ROWQ("IEC-1234M", 2, 1234567890, "1", "15 Gi");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const QLocale::DataSizeFormats format = QLocale::DataSizeTraditionalFormat;
|
||||||
|
ROWB("Trad-0", 2, 0, "0");
|
||||||
|
ROWB("Trad-10", 2, 10, "10");
|
||||||
|
ROWQ("Trad-12Ki", 2, 12345, "12", "06 k");
|
||||||
|
ROWQ("Trad-16Ki", 2, 16384, "16", "00 k");
|
||||||
|
ROWQ("Trad-1235k", 2, 1234567, "1", "18 M");
|
||||||
|
ROWQ("Trad-1374k", 2, 1374744, "1", "31 M");
|
||||||
|
ROWQ("Trad-1234M", 2, 1234567890, "1", "15 G");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const QLocale::DataSizeFormats format = QLocale::DataSizeSIFormat;
|
||||||
|
ROWB("Decimal-0", 2, 0, "0");
|
||||||
|
ROWB("Decimal-10", 2, 10, "10");
|
||||||
|
ROWQ("Decimal-16Ki", 2, 16384, "16", "38 k");
|
||||||
|
ROWQ("Decimal-1234k", 2, 1234567, "1", "23 M");
|
||||||
|
ROWQ("Decimal-1374k", 2, 1374744, "1", "37 M");
|
||||||
|
ROWQ("Decimal-1234M", 2, 1234567890, "1", "23 G");
|
||||||
|
}
|
||||||
|
#undef ROWQ
|
||||||
|
#undef ROWB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QLocale::formattedDataSize()
|
||||||
|
{
|
||||||
|
QFETCH(QLocale::Language, language);
|
||||||
|
QFETCH(int, decimalPlaces);
|
||||||
|
QFETCH(QLocale::DataSizeFormats, units);
|
||||||
|
QFETCH(int, bytes);
|
||||||
|
QFETCH(QString, output);
|
||||||
|
QCOMPARE(QLocale(language).formattedDataSize(bytes, decimalPlaces, units), output);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QLocale)
|
QTEST_MAIN(tst_QLocale)
|
||||||
#include "tst_qlocale.moc"
|
#include "tst_qlocale.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user