Files
2026-07-29 18:08:57 +08:00

105 lines
3.7 KiB
C++

#include "ProgressBar.h"
#include <QPainter>
#include <QMetaObject>
#include <QThread>
#include <algorithm>
#include <cmath>
Progress_Bar::Progress_Bar(const std::vector<Tick>& tick_list, QWidget *parent) : QWidget(parent), tick_list(tick_list) {
setAttribute(Qt::WA_OpaquePaintEvent, true);
timer = startTimer(30);
}
double Progress_Bar::get_draw_width(double value) {
int n = tick_list.size();
if (n < 2 || QWidget::width() <= 0)
return 0.0;
if (!std::isfinite(value))
value = tick_list.front().value;
int i = 1;
for(; i < n - 1; ++i) {
double cur = tick_list[i].value;
if(value < cur || qFuzzyCompare(value, cur)) {
break;
}
}
int lower_offset = i - 1;
int upper_offset = i;
double lower = tick_list[lower_offset].value;
double upper = tick_list[upper_offset].value;
double w_step = (double)QWidget::width()/(double)(n-1);
double last_rate = upper == lower ? 0.0 : (value - lower)/(upper - lower);
last_rate = std::clamp(last_rate, 0.0, 1.0);
double draw_width = w_step * (last_rate + lower_offset);
return std::clamp(draw_width, 0.0, (double)QWidget::width());
}
void Progress_Bar::setValue(double value) {
if (thread() != QThread::currentThread()) {
queued_value.store(value, std::memory_order_release);
if (!queued_value_update.exchange(true, std::memory_order_acq_rel)) {
QMetaObject::invokeMethod(this, [this]() {
double value = queued_value.load(std::memory_order_acquire);
queued_value_update.store(false, std::memory_order_release);
setValue(value);
}, Qt::QueuedConnection);
}
return;
}
this->value = value;
update();
}
void Progress_Bar::paintEvent(QPaintEvent *event) {
Q_UNUSED(event)
QPainter painter(this);
painter.setPen(Qt::NoPen);
painter.fillRect(rect(), QApplication::style()->standardPalette().brush(QPalette::Window));
painter.setPen(QPen(Qt::black));
auto w = (double)QWidget::width();
auto hh = (double)height()/2;
int n = tick_list.size();
int tick_w = 1;
double draw_width = get_draw_width(value);
int fill_width = std::max(0, static_cast<int>(draw_width) - tick_w - 4);
if (fill_width > 0)
painter.fillRect(0, 0, fill_width, height()/2, Qt::black);
int cur_x = 0;
int tick_step = std::max(1, tick_w + (int)tick_space_w);
while(cur_x + tick_w + 4 < draw_width){
painter.fillRect(cur_x, 0, tick_w, height()/2, Qt::gray);
cur_x += tick_step;
}
int line_y = (int)hh + 2;
int marker_x = std::clamp(cur_x - (int)tick_space_w, 0, std::max(0, QWidget::width() - 4));
painter.fillRect(marker_x, 0, 4, line_y, Qt::yellow);
painter.drawLine(0, line_y , (int)w, line_y);
QFont font;
font.setBold(true);
QFontMetrics fm(font);
for(int i = 0; i < n; ++i){
int tick_x = (int)((double)(QWidget::width() * i)/(double)(n - 1));
int text_w = fm.horizontalAdvance( tick_list[i].text);
int text_x;
if(i == n - 1) {
text_x = tick_x - text_w;
tick_x -=1;
} else if( i == 0) {
text_x = tick_x;
} else {
text_x = tick_x - text_w/2;
}
painter.setPen(Qt::black);
painter.drawLine(tick_x, line_y, tick_x, (line_y + 5));
painter.setPen(tick_list[i].color);
painter.setFont(font);
painter.drawText(QRectF(text_x, line_y, text_w, line_y), Qt::AlignCenter, tick_list[i].text);
}
}
void Progress_Bar::timerEvent(QTimerEvent *event) {
if(event->timerId() == timer) {
if(set_value) set_value();
update();
}
QObject::timerEvent(event);
}