feat:完成直方图数据的动态设置

This commit is contained in:
duanshengchao 2025-09-05 14:54:34 +08:00
parent 4967df6628
commit 3e6cd36162
8 changed files with 178 additions and 36 deletions

View File

@ -3,11 +3,17 @@
CustomBars::CustomBars(QCPAxis* keyAxis, QCPAxis* valueAxis)
: QCPBars(keyAxis, valueAxis),
m_textVisible(false),
m_textAlignment(Qt::AlignCenter),
m_spacing(5),
m_font(QFont(QLatin1String("sans serif"), 12))
{}
void CustomBars::setTextVisible(bool visible)
{
m_textVisible = visible;
}
void CustomBars::setTextAlignment(Qt::Alignment alignment)
{
m_textAlignment = alignment;
@ -67,27 +73,31 @@ void CustomBars::draw(QCPPainter* painter)
///---自定义逻辑-start---
//draw text
QRectF barRect = getBarRect(it->key, it->value);
painter->setFont(m_font);
QString text = QString::number(it->value, 'g', 2); //获取当前value值保留两位精度
QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | m_textAlignment, text); //计算文字占用大小
if(mKeyAxis.data()->orientation() == Qt::Horizontal) //水平轴为keyAxis
if(m_textVisible)
{
if(mKeyAxis.data()->axisType() == QCPAxis::atTop) //上轴,文字放到柱图下方
textRect.moveTopLeft(barRect.bottomLeft() + QPointF(0, m_spacing));
else //下轴,文字放到柱图上方
textRect.moveBottomLeft(barRect.topLeft() - QPointF(0, m_spacing));
textRect.setWidth(barRect.width());
QRectF barRect = getBarRect(it->key, it->value);
painter->setFont(m_font);
QString text = QString::number(it->value, 'g', 2); //获取当前value值保留两位精度
QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | m_textAlignment, text); //计算文字占用大小
if(mKeyAxis.data()->orientation() == Qt::Horizontal) //水平轴为keyAxis
{
if(mKeyAxis.data()->axisType() == QCPAxis::atTop) //上轴,文字放到柱图下方
textRect.moveTopLeft(barRect.bottomLeft() + QPointF(0, m_spacing));
else //下轴,文字放到柱图上方
textRect.moveBottomLeft(barRect.topLeft() - QPointF(0, m_spacing));
textRect.setWidth(barRect.width());
}
else //垂直轴为keyAxis
{
if(mKeyAxis.data()->axisType() == QCPAxis::atLeft) //左轴,文字放到柱图右侧
textRect.moveTopLeft(barRect.topRight() + QPointF(m_spacing, 0));
else //右轴,文字放到柱图左侧
textRect.moveTopRight(barRect.topLeft() - QPointF(m_spacing, 0));
textRect.setHeight(barRect.height());
}
painter->drawText(textRect, Qt::TextDontClip | m_textAlignment, text);
}
else //垂直轴为keyAxis
{
if(mKeyAxis.data()->axisType() == QCPAxis::atLeft) //左轴,文字放到柱图右侧
textRect.moveTopLeft(barRect.topRight() + QPointF(m_spacing, 0));
else //右轴,文字放到柱图左侧
textRect.moveTopRight(barRect.topLeft() - QPointF(m_spacing, 0));
textRect.setWidth(barRect.height());
}
painter->drawText(textRect, Qt::TextDontClip | m_textAlignment, text);
///---自定义逻辑-end---
}
}
@ -114,6 +124,8 @@ dpBarsChart::dpBarsChart(QWidget* parent)
mainLayout->addWidget(m_pCustomPlot);
setLayout(mainLayout);
m_updateData = false;
connect(DataManager::instance(), &DataManager::dataUpdated, this, &dpBarsChart::onSignal_dataUpdated);
}
@ -185,13 +197,23 @@ void dpBarsChart::setTimeRange(TimeUnit unit)
{}
void dpBarsChart::setDateTime(const QDateTime& dateTime)
{}
{
if(m_updateData)
{
for(auto it = m_bars.begin(); it != m_bars.end(); ++it)
DataManager::instance()->requestData(it.key(), this);
m_pCustomPlot->replot();
}
}
void dpBarsChart::viewHistoricalData(const QDateTime& dateTime)
{}
void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
{
m_updateData = false; //停止更新数据
//先清除现有图形(直方图不像曲线一样有连续的过程展现,所以不采用动态更新,每次都清除、重新创建)
//m_pBarsGroup->clear();
m_pCustomPlot->clearPlottables();
@ -237,6 +259,7 @@ void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
bars.dataType = dataType;
bars.color = colorData.value<QColor>();
CustomBars* qBars = new CustomBars(keyAxis, valueAxis);
qBars->setTextVisible(true);
qBars->setName(bars.name);
qBars->setPen(bars.color); //边框颜色
qBars->setBrush(bars.color); //填充颜色
@ -261,6 +284,13 @@ void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
}
m_bars.insert(dataID, bars);
//实时模拟数据
DataManager::instance()->registerDataSource(dataID, [](){
static double min = 0, max = 10.0;
double randomFloat = min + QRandomGenerator::global()->generateDouble() * (max - min);
return QVariant::fromValue(randomFloat);
});
}
if(groupByType)
@ -308,6 +338,8 @@ void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
keyAxis->setRange(0, ticks.count() + 1);
valueAxis->setTickLabels(true);
QSharedPointer<QCPAxisTickerFixed > fixedTicker(new QCPAxisTickerFixed);
valueAxis->setTicker(fixedTicker);
valueAxis->setRange(0, 11);
for(auto it = m_bars.begin(); it != m_bars.end(); ++it)
@ -317,8 +349,8 @@ void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
//随机模拟值
QVector<double> values;
double randomVaule = 1 + QRandomGenerator::global()->generateDouble() * (8 - 1);
values << randomVaule;
//double randomVaule = 1 + QRandomGenerator::global()->generateDouble() * (8 - 1);
values << 0;
Bars bar = it.value();
bar.qBars->setData(bar.keys, values);
@ -337,8 +369,33 @@ void dpBarsChart::synchronizeConfigData(const configurationResults& cfg)
else
m_pCustomPlot->legend->setVisible(false);
m_pCustomPlot->replot();
//m_pCustomPlot->replot();
m_updateData = true;
}
void dpBarsChart::onSignal_dataUpdated(const QString& dataKey, const QVariant& data, const QDateTime& timestamp)
{}
void dpBarsChart::onSignal_dataUpdated(const QString& dataID, const QVariant& data, const QDateTime& timestamp)
{
auto it = m_bars.find(dataID);
if(it != m_bars.end())
{
static double marginFactor = 1.0;
double dData = data.toDouble();
Bars bar = it.value();
//调整所在轴的范围
QCPRange range = bar.qBars->valueAxis()->range();
if(dData > range.upper)
{
double upper = dData + marginFactor;
bar.qBars->valueAxis()->setRangeUpper(upper);
}
else if(dData < range.lower)
{
double lower = dData - marginFactor;
bar.qBars->valueAxis()->setRangeLower(lower);
}
//更新值
QVector<double> values;
values << dData;
bar.qBars->setData(bar.keys, values);
}
}

View File

@ -24,6 +24,7 @@ public:
double spacing() { return m_spacing; }
QFont font() { return m_font; }
void setTextVisible(bool);
void setTextAlignment(Qt::Alignment);
void setSpacing(double);
void setFont(QFont);
@ -32,6 +33,7 @@ protected:
virtual void draw(QCPPainter* painter) override;
private:
bool m_textVisible;
Qt::Alignment m_textAlignment; //文字对齐方式
double m_spacing; //文字与柱状图之间的间距,单位为像素
QFont m_font; //文字的字体
@ -52,7 +54,7 @@ public:
void synchronizeConfigData(const configurationResults&) override;
public slots:
void onSignal_dataUpdated(const QString& dataKey, const QVariant& data, const QDateTime& timestamp);
void onSignal_dataUpdated(const QString& dataID, const QVariant& data, const QDateTime& timestamp);
private:
struct Bars
@ -78,6 +80,7 @@ private:
//QCPBarsGroup* m_pBarsGroup;
QHash<QString, Bars> m_bars;
QHash<RealTimeDataType, QCPBarsGroup*> m_barsGroups;
bool m_updateData;
};
#endif

View File

@ -293,6 +293,7 @@ void dpConfigurationDialog::setPanel(DataPanel* pPanel)
{
ui->specialSettings->setVisible(true);
ui->specialSettings->setCurrentIndex(1);
ui->barOrientation->setCurrentIndex((int)pPanel->m_cofigurationResults.barOrientataion);
break;
}
default:
@ -363,7 +364,6 @@ void dpConfigurationDialog::onBtnClicked_confirm()
m_pDataPanel->m_cofigurationResults.dataServiceIP = ui->serviceIP->text();
m_pDataPanel->m_cofigurationResults.dataServicePort = ui->servicePort->text().toInt();
m_pDataPanel->m_cofigurationResults.showLegend = ui->radioBtn_showLegend->isChecked();
m_pDataPanel->m_cofigurationResults.groupByType = ui->radioBtn_groupByType->isChecked();
m_pDataPanel->m_cofigurationResults.axisArrangement = (AxisArrangementMode)ui->axisArrangement->currentIndex();
DataPanelType panelType = m_pDataPanel->getType();
@ -380,6 +380,12 @@ void dpConfigurationDialog::onBtnClicked_confirm()
}
break;
}
case barChart:
{
m_pDataPanel->m_cofigurationResults.barOrientataion = (BarOrientation)ui->barOrientation->currentIndex();
m_pDataPanel->m_cofigurationResults.groupByType = ui->radioBtn_groupByType->isChecked();
break;
}
default:
break;
}

View File

@ -30,8 +30,8 @@ enum AxisArrangementMode //坐标轴排列方式
enum BarOrientation //直方图方向
{
Horizontal = 0,
Vertical
Vertical = 0,
Horizontal
};
#include <QStandardItemModel>

View File

@ -12,7 +12,7 @@ dpLineChart::dpLineChart(QWidget* parent)
m_timeRange = 60 * 1000;
m_axisArrangementMode = AlternateSides;
m_showLegend = false;
//m_showLegend = false;
m_updateData = false;
QBoxLayout* mainLayout = new QBoxLayout(QBoxLayout::LeftToRight);
@ -81,7 +81,7 @@ void dpLineChart::initQCP()
m_pCustomPlot->yAxis2->grid()->setPen(m_chartStyle.gridPen);
//x轴用时间格式
QSharedPointer<QCPAxisTickerDateTime> timeTicker(new QCPAxisTickerDateTime);
timeTicker->setDateTimeFormat("hh:mm:ss:zzz\nyyyy/MM/dd");
timeTicker->setDateTimeFormat("hh:mm:ss");
//qDebug() << timeTicker->dateTimeFormat();
m_pCustomPlot->xAxis->setTicker(timeTicker);
//qDebug() << m_pCustomPlot->xAxis->range();
@ -163,62 +163,135 @@ void dpLineChart::reLayoutLegend()
void dpLineChart::setTimeRange(TimeUnit unit)
{
QSharedPointer<QCPAxisTicker> ticker = m_pCustomPlot->xAxis->ticker();
QSharedPointer<QCPAxisTickerDateTime> dateTicker = qSharedPointerCast<QCPAxisTickerDateTime>(ticker);
switch(unit)
{
case TU_Year:
{
m_timeRange = m_curDateTime.date().daysInYear() * 24 * 60 * 60 * (qint64)1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("dd/MM\nyyyy");
break;
}
case TU_Month:
{
m_timeRange = m_curDateTime.date().daysInMonth() * 24 * 60 * 60 * (qint64)1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("dd/MM\nyyyy");
break;
}
case TU_Day:
{
m_timeRange = 24 * 60 * 60 * 1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("dd/MM\nyyyy");
break;
}
case TU_Hour:
{
m_timeRange = 60 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm");
break;
}
case TU_Minute_30:
{
m_timeRange = 30 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm");
break;
}
case TU_Minute_20:
{
m_timeRange = 20 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm");
break;
}
case TU_Minute_15:
{
m_timeRange = 15 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm");
break;
}
case TU_Minute_10:
{
m_timeRange = 10 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm");
break;
}
case TU_Minute_5:
{
m_timeRange = 5 * 60 *1000;
break;
}
case TU_Minute_3:
{
m_timeRange = 3 * 60 *1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss");
break;
}
case TU_Minute_1:
{
m_timeRange = 60 * 1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss");
break;
}
case TU_Second_30:
{
m_timeRange = 30 * 1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss");
break;
}
case TU_Second_10:
{
m_timeRange = 10 * 1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss");
break;
}
case TU_Second_1:
{
m_timeRange = 1 * 1000;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss:zzz");
break;
}
case TU_MSecond_500:
{
m_timeRange = 500;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss:zzz");
break;
}
case TU_MSecond_100:
{
m_timeRange = 100;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss:zzz");
break;
}
case TU_MSecond_50:
{
m_timeRange = 50;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss:zzz");
break;
}
case TU_MSecond_10:
{
m_timeRange = 10;
if(!dateTicker.isNull())
dateTicker->setDateTimeFormat("hh:mm:ss:zzz");
break;
}
default:
break;
}
@ -459,6 +532,8 @@ void dpLineChart::synchronizeConfigData(const configurationResults& cfg)
}
graph.qGraph = newGraph;
m_graphs.insert(graphID, graph);
//实时模拟数据
DataManager::instance()->registerDataSource(graphID, [](){
static double min = 0, max = 10.0;
double randomFloat = min + QRandomGenerator::global()->generateDouble() * (max - min);

View File

@ -70,7 +70,7 @@ private:
QVector<Axis> m_axes;
QHash<QString, Graph> m_graphs;
AxisArrangementMode m_axisArrangementMode;
bool m_showLegend;
//bool m_showLegend;
bool m_updateData;
};

View File

@ -91,7 +91,8 @@ void DataManager::registerDataSource(const QString& dataKey, std::function<QVari
}
QWriteLocker locker(&m_cacheLock);
m_dataSources[dataKey] = fetcher;
if(!m_dataSources.contains(dataKey))
m_dataSources[dataKey] = fetcher;
//初始化缓存条目
if(!m_cache.contains(dataKey))

View File

@ -624,7 +624,7 @@ background-color: rgb(24, 32, 38);
</rect>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="axisCfg">
<property name="styleSheet">
@ -769,12 +769,12 @@ background-color:transparent;
</property>
<item>
<property name="text">
<string>水平方向</string>
<string>垂直方向</string>
</property>
</item>
<item>
<property name="text">
<string>垂直方向</string>
<string>水平方向</string>
</property>
</item>
</widget>