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 <QDir>
|
||||
#include <QLocale>
|
||||
#include <qmath.h>
|
||||
#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) :
|
||||
QAbstractTableModel(parent),
|
||||
m_volumes(QStorageInfo::mountedVolumes())
|
||||
@ -106,11 +91,11 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
||||
case ColumnFileSystemName:
|
||||
return volume.fileSystemType();
|
||||
case ColumnTotal:
|
||||
return sizeToString(volume.bytesTotal());
|
||||
return QLocale().formattedDataSize(volume.bytesTotal());
|
||||
case ColumnFree:
|
||||
return sizeToString(volume.bytesFree());
|
||||
return QLocale().formattedDataSize(volume.bytesFree());
|
||||
case ColumnAvailable:
|
||||
return sizeToString(volume.bytesAvailable());
|
||||
return QLocale().formattedDataSize(volume.bytesAvailable());
|
||||
case ColumnIsReady:
|
||||
return volume.isReady();
|
||||
case ColumnIsReadOnly:
|
||||
@ -121,6 +106,7 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
||||
break;
|
||||
}
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
QLocale locale;
|
||||
const QStorageInfo &volume = m_volumes.at(index.row());
|
||||
return tr("Root path : %1\n"
|
||||
"Name: %2\n"
|
||||
@ -140,9 +126,9 @@ QVariant StorageModel::data(const QModelIndex &index, int role) const
|
||||
arg(volume.displayName()).
|
||||
arg(QString::fromUtf8(volume.device())).
|
||||
arg(QString::fromUtf8(volume.fileSystemType())).
|
||||
arg(sizeToString(volume.bytesTotal())).
|
||||
arg(sizeToString(volume.bytesFree())).
|
||||
arg(sizeToString(volume.bytesAvailable())).
|
||||
arg(locale.formattedDataSize(volume.bytesTotal())).
|
||||
arg(locale.formattedDataSize(volume.bytesFree())).
|
||||
arg(locale.formattedDataSize(volume.bytesAvailable())).
|
||||
arg(volume.isReady() ? tr("true") : tr("false")).
|
||||
arg(volume.isReadOnly() ? tr("true") : tr("false")).
|
||||
arg(volume.isValid() ? tr("true") : tr("false")).
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "qvariant.h"
|
||||
#include "qstringbuilder.h"
|
||||
#include "private/qnumeric_p.h"
|
||||
#include <cmath>
|
||||
#ifdef Q_OS_WIN
|
||||
# include <qt_windows.h>
|
||||
# include <time.h>
|
||||
@ -3780,6 +3781,76 @@ QString QLocale::toCurrencyString(double value, const QString &symbol, int preci
|
||||
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
|
||||
|
||||
|
@ -913,6 +913,19 @@ public:
|
||||
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(const QString &name);
|
||||
QLocale(Language language, Country country = AnyCountry);
|
||||
@ -1045,6 +1058,8 @@ public:
|
||||
{ return toCurrencyString(double(i), symbol, precision); }
|
||||
#endif
|
||||
|
||||
QString formattedDataSize(qint64 bytes, int precision = 2, DataSizeFormats format = DataSizeIecFormat);
|
||||
|
||||
QStringList uiLanguages() const;
|
||||
|
||||
bool operator==(const QLocale &other) const;
|
||||
|
@ -774,21 +774,7 @@ QString QFileSystemModelPrivate::size(const QModelIndex &index) const
|
||||
|
||||
QString QFileSystemModelPrivate::size(qint64 bytes)
|
||||
{
|
||||
// According to the Si standard KB is 1000 bytes, KiB is 1024
|
||||
// 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));
|
||||
return QLocale::system().formattedDataSize(bytes);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1308,22 +1308,7 @@ QString QDirModelPrivate::size(const QModelIndex &index) const
|
||||
// Nautilus - "9 items" (the number of children)
|
||||
}
|
||||
|
||||
// According to the Si standard KB is 1000 bytes, KiB is 1024
|
||||
// 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));
|
||||
return QLocale::system().formattedDataSize(n->info.size());
|
||||
}
|
||||
|
||||
QString QDirModelPrivate::type(const QModelIndex &index) const
|
||||
|
@ -137,6 +137,9 @@ private slots:
|
||||
void textDirection_data();
|
||||
void textDirection();
|
||||
|
||||
void formattedDataSize_data();
|
||||
void formattedDataSize();
|
||||
|
||||
private:
|
||||
QString m_decimal, m_thousand, m_sdate, m_ldate, m_time;
|
||||
QString m_sysapp;
|
||||
@ -2473,5 +2476,80 @@ void tst_QLocale::textDirection()
|
||||
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)
|
||||
#include "tst_qlocale.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user