Improve conversion of bytes to human readable string

Fixes issues like showing 0 MiB when the value is just slightly less then 1 MiB.
This commit is contained in:
eelke 2024-04-12 13:26:10 +02:00
parent db2594a87c
commit 5bdd3fa95d
7 changed files with 125 additions and 44 deletions

View file

@ -5,6 +5,7 @@
#include "ResultTableModelUtil.h"
#include "CustomDataRole.h"
#include "AbstractEditorFactory.h"
#include "utils/HumanReadableBytes.h"
PgLabItemDelegate::PgLabItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
@ -111,37 +112,11 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
else {
forground_color = GetDefaultColorForType(oid);
if (meaning == DataMeaning::Bytes) {
QString suffix;
auto s = value.toLongLong();
double val;
if (s > 1024 * 1024 * 1000) {
val = s / (1024 * 1024 * 1024);
suffix = "GiB";
forground_color = QColorConstants::Svg::darkorange;
option->font.setBold(true);
}
else if (s > 1024 * 1000) {
val = s / (1024 * 1024);
suffix = "MiB";
forground_color = QColorConstants::Svg::darkgoldenrod;
}
else if (s > 1000) {
val = s / 1024;
suffix = "KiB";
forground_color = QColorConstants::Svg::darkgreen;
}
else {
val = s;
suffix = "B";
forground_color = QColorConstants::Svg::darkblue;
}
option->text = QString{ "%1 %2" }.arg(val, 0, 'g', 3).arg(suffix) ;
option->text = HandleBytes(value.toLongLong(), forground_color);
}
else {
auto str = value.toString();
auto s = str.left(100);
// auto f = s.indexOf('\n');
// option->text = ((f > 0) ? s.left(f) : s).toString();
option->text = s;
}
}
@ -161,30 +136,48 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
option->styleObject = nullptr;
}
void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
QString PgLabItemDelegate::HandleBytes(qlonglong s, QColor &forground_color) const
{
Q_ASSERT(index.isValid());
auto str = HumanReadableBytes(s);
if (s > 1024 * 1024 * 1024) {
forground_color = QColorConstants::Svg::darkorange;
}
else if (s > 1024 * 1024) {
forground_color = QColorConstants::Svg::darkgoldenrod;
}
else if (s > 1024) {
forground_color = QColorConstants::Svg::darkgreen;
}
else {
forground_color = QColorConstants::Svg::darkblue;
}
return QString::fromStdString(str);
}
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
const QWidget *widget = option.widget; // QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget; // QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}
QWidget *PgLabItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (m_editorFactory)
return m_editorFactory->createEditor(parent, option, index);
return QStyledItemDelegate::createEditor(parent, option, index);
if (m_editorFactory)
return m_editorFactory->createEditor(parent, option, index);
return QStyledItemDelegate::createEditor(parent, option, index);
}
void PgLabItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if (m_editorFactory)
m_editorFactory->setEditorData(editor, index);
if (m_editorFactory)
m_editorFactory->setEditorData(editor, index);
else
QStyledItemDelegate::setEditorData(editor, index);
}

View file

@ -29,7 +29,8 @@ protected:
virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;
private:
AbstractEditorFactory *m_editorFactory = nullptr;
AbstractEditorFactory *m_editorFactory = nullptr;
QString HandleBytes(qlonglong s, QColor &forground_color) const;
};
#endif // PGLABITEMDELEGATE_H

View file

@ -87,7 +87,8 @@ SOURCES += \
catalog/PgLanguage.cpp \
catalog/PgAcl.cpp \
catalog/PgSequence.cpp \
catalog/PgSequenceContainer.cpp
catalog/PgSequenceContainer.cpp \
utils/HumanReadableBytes.cpp
HEADERS += \
Pglablib.h \
@ -162,7 +163,8 @@ HEADERS += \
catalog/PgLanguage.h \
catalog/PgAcl.h \
catalog/PgSequence.h \
catalog/PgSequenceContainer.h
catalog/PgSequenceContainer.h \
utils/HumanReadableBytes.h
unix {
target.path = /usr/lib

View file

@ -0,0 +1,56 @@
#include "HumanReadableBytes.h"
#include <iomanip>
#include <sstream>
using namespace std;
namespace {
struct scale {
int64_t scale;
const char* prefix;
};
void fmt(std::ostringstream &o, double d)
{
;
if (d < 10.0)
{
o << fixed << setprecision(2) << d;
//return std::format("{:.3g}", d);
}
else if (d < 100.0)
{
o << fixed << setprecision(1) << d;
//return std::format("{:.3g}", d);
}
else
{
o << fixed << setprecision(0) << d;
}
}
}
std::string HumanReadableBytes(uint64_t bytes)
{
static scale scales[] = {
{ 1024ll * 1024 * 1024 * 1024, "Ti" },
{ 1024ll * 1024 * 1024, "Gi" },
{ 1024ll * 1024, "Mi" },
{ 1024ll, "ki" },
};
std::ostringstream out;
for (int i = 0; i < (sizeof(scales) / sizeof(scale)); ++i)
{
if (bytes >= scales[i].scale)
{
fmt(out, bytes / double(scales[i].scale));
out << " " << scales[i].prefix << "B";
return out.str();
}
}
out << bytes << " B";
return out.str();
}

View file

@ -0,0 +1,5 @@
#pragma once
#include <string>
std::string HumanReadableBytes(uint64_t bytes);

View file

@ -18,6 +18,7 @@ SOURCES += main.cpp \
tst_ConvertLangToSqlString.cpp \
tst_ConvertToMultiLineCString.cpp \
tst_ExplainJsonParser.cpp \
tst_HumanReadableBytes.cpp \
tst_escapeConnectionStringValue.cpp \
tst_expected.cpp \
tst_SqlLexer.cpp \

View file

@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "PrintTo_Qt.h"
#include "utils/HumanReadableBytes.h"
TEST(HumanReadableBytesTest, bytes7)
{
auto s = HumanReadableBytes(7);
ASSERT_EQ("7 B", s);
}
TEST(HumanReadableBytesTest, bytes1023)
{
auto s = HumanReadableBytes(1023);
ASSERT_EQ("1023 B", s);
}
TEST(HumanReadableBytesTest, bytes1k)
{
auto s = HumanReadableBytes(1024);
ASSERT_EQ("1.00 kiB", s);
}