#include "xlruchartsform.h" #include "ui_xlruchartsform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QtCharts; class MyChartView: public QChartView { //Q_OBJECT public: void mousePressEvent(QMouseEvent* event) override { #if 0 QChart* _chart=chart(); //qreal x = (event->pos()).x(); //qreal y = (event->pos()).y(); //qreal xVal = _chart->mapToValue(event->pos()).x(); qreal yVal = _chart->mapToValue(event->pos()).y(); if (true) //xVal <= maxX && xVal >= minX && yVal <= maxY && yVal >= minY) { QToolTip::showText(mapToGlobal(event->pos()), QString("%1").arg(yVal)); } #endif QChartView::mousePressEvent(event); } }; class Sensor { public: QString name; QLineSeries* series; MyChartView* view; QValueAxis* axis; Sensor(): series(0), view(0), axis(0) { } }; class XlruChartsForm::Implementation { public: QtCharts::QChart* ch; QVector sensors; QVector s; MyChartView* mainView; QDateTimeAxis* commonX; QValueAxis* temperatureY; QValueAxis* currentY; QValueAxis* voltageY; QVector cViews; int t_id; QDateTime timeStart; qint64 timeStartMs; QDateTime timeNow; qint64 timeNowMs; qint64 lastSampleMs; bool tgtAlive; QMessageBox alertBox; int max_temperature; int max_temp_period; bool max_temp_restart; Implementation(): alertBox(QMessageBox::Warning, "WARNING", "Overtemperature") { alertBox.setWindowFlags(Qt::WindowStaysOnTopHint); alertBox.setModal( false ); tgtAlive=false; lastSampleMs=0; max_temperature=0; max_temp_period=0; max_temp_restart=false; } QChart* newCharts() { QChart* c=new QChart; c->setAnimationOptions(QChart::NoAnimation); return c; } QLineSeries* newSeries(const QString& name, QChart* c, QValueAxis* axis) { QLineSeries* tmp=new QLineSeries; s.append(tmp); c->addSeries(tmp); tmp->attachAxis(commonX); tmp->attachAxis(axis); //c->setAxisX(commonX, tmp); //c->setAxisY(axis, tmp); tmp->setName(name); return tmp; } }; XlruChartsForm::XlruChartsForm(QWidget *parent) : QWidget(parent), ui(new Ui::XlruChartsForm), p_(*new XlruChartsForm::Implementation), m_tooltip(0) { ui->setupUi(this); ui->tbEnable->setChecked(true); p_.mainView=new MyChartView; p_.mainView->setOptimizationFlag(QGraphicsView::DontSavePainterState, true); p_.mainView->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true); p_.mainView->setRenderHint(QPainter::Antialiasing, false); p_.mainView->setRenderHint(QPainter::TextAntialiasing, false); p_.mainView->setRenderHint(QPainter::SmoothPixmapTransform, false); p_.mainView->setRubberBand( QChartView::HorizontalRubberBand ); p_.mainView->chart()->layout()->setContentsMargins(0, 0, 0, 0); p_.mainView->chart()->setBackgroundRoundness(0); p_.mainView->chart()->setMargins(QMargins(0, 0, 0, 0)); //p_.mainView->setToolTip("Monitoring Temperature/Current/Voltage"); //p_.mainView->chart()->setMinimumSize(50, 50); p_.ch=p_.mainView->chart(); //theChart=this->chart(); //if (commonX==0) { p_.commonX= new QDateTimeAxis; p_.commonX->setFormat("h:mm:ss"); p_.commonX->setTickCount(0); p_.commonX->setGridLineVisible(false); p_.timeNow=QDateTime::currentDateTime(); p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch(); p_.timeStart=p_.timeNow; p_.timeStartMs=p_.timeNowMs; p_.commonX->setMin(p_.timeNow); p_.commonX->setMax(p_.timeNow.addSecs(60)); p_.temperatureY=new QValueAxis; p_.temperatureY->setGridLineVisible(false); p_.temperatureY->setRange(10, 80); p_.temperatureY->setVisible(true); p_.temperatureY->setTitleText("Temp(C)"); p_.temperatureY->setTitleVisible(true); p_.currentY=new QValueAxis; p_.currentY->setGridLineVisible(false); p_.currentY->setRange(0, 10); p_.currentY->setVisible(true); p_.currentY->setTitleText("Curr(A)"); p_.currentY->setTitleVisible(true); p_.voltageY=new QValueAxis; p_.voltageY->setGridLineVisible(false); p_.voltageY->setRange(-20, 20); p_.voltageY->setVisible(false); p_.voltageY->setTitleText("Volt(V)"); p_.voltageY->setTitleVisible(true); p_.mainView->chart()->addAxis(p_.commonX, Qt::AlignBottom); p_.mainView->chart()->addAxis(p_.temperatureY, Qt::AlignRight); p_.mainView->chart()->addAxis(p_.currentY, Qt::AlignRight); p_.mainView->chart()->addAxis(p_.voltageY, Qt::AlignRight); } p_.ch->setMargins(QMargins(2, 2, 2, 2)); ui->verticalLayout->addWidget(p_.mainView); this->temperatureAxis=p_.temperatureY; this->currentAxis=p_.currentY; this->voltageAxis=p_.voltageY; p_.t_id=this->startTimer(2000); } void XlruChartsForm::timerEvent(QTimerEvent * e) { if (e->timerId()==p_.t_id) { p_.timeNow=QDateTime::currentDateTime(); p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch(); #if 0 int i=0; foreach (QLineSeries* s, p_.s) { int r=qrand(); addTemperatureSample(i, (r*(float(300)/RAND_MAX)-150)); ++i; } #endif #if 0 for(unsigned int ii=6; ii<6+4; ++ii) { addSample(ii, 10*ii); } #endif if ((p_.timeNowMs-p_.lastSampleMs)>1000*10) //Dead? { p_.tgtAlive=false; } else { p_.commonX->setMax(p_.timeNow); Qt::CheckState cs=ui->cbAutoPurge->checkState(); switch(cs) { default: case Qt::Unchecked: break; case Qt::PartiallyChecked: { qint64 delta=p_.timeNowMs-p_.timeStartMs; if (delta>(1000*60*60)) { p_.timeStart=p_.timeNow.addMSecs(-60*30); p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch(); p_.commonX->setMin(p_.timeStart); } } break; case Qt::Checked: { if (!p_.tgtAlive) { p_.timeStart=p_.timeNow; p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch(); p_.commonX->setMin(p_.timeStart); } } break; } p_.tgtAlive=true; if (p_.max_temp_restart) { ui->lblMaxTemp->setText(QString("%1").arg(p_.max_temp_period)); p_.max_temp_period=-1000; } p_.max_temp_restart=false; } } } int XlruChartsForm::addSensor(const QString& name, QtCharts::QValueAxis* a, int id) { if (id==-1) { id=p_.s.size(); } if (id>=p_.s.size()) { p_.sensors.resize(id+1); } else { //Already exist!! return -1; } QLineSeries* s=p_.newSeries(name, p_.ch, a); Sensor sensor; sensor.series=s; sensor.view=p_.mainView; sensor.name=name; sensor.axis=a; p_.sensors[id]=sensor; return id; } int XlruChartsForm::addTemperatureSensor(const QString& name, int id) { return addSensor(name, p_.temperatureY, id); } int XlruChartsForm::addCurrentSensor(const QString& name, int id) { return addSensor(name, p_.currentY, id); } int XlruChartsForm::addVoltageSensor(const QString& name, int id) { return addSensor(name, p_.voltageY, id); } #if 0 void XlruChartsForm::addSampleAxis(int id, float value, QtCharts::QValueAxis *a) { if ((id>=0) && (idappend(p_.timeNowMs, value); if (a->min()>value) { a->setMin(value); } else if (a->max()setMax(value); } } } #endif void XlruChartsForm::addSample(int id, float value) { if (!ui->tbEnable->isChecked()) return; if ((id>=0) && (idappend(p_.timeNowMs, value); QtCharts::QValueAxis *a=p_.sensors[id].axis; if (a!=0) { if (a->min()>value) { a->setMin(value); } else if (a->max()setMax(value); } if (a==temperatureAxis) { if (value>=ui->sbTempAlarm->value()) { if (ui->cbAlarms->isChecked()) { QApplication::beep(); p_.alertBox.setText(QString("%1 Overtemperature = %2").arg(p_.sensors[id].name).arg(value)); p_.alertBox.show(); p_.alertBox.raise(); } } if (value>p_.max_temperature) p_.max_temperature=value; if (value>p_.max_temp_period) { p_.max_temp_period=value; p_.max_temp_restart=true; } } } } } void XlruChartsForm::addCurrentSample(int id, float value) { addSample(id, value); //, p_.currentY); //p_.currentY->setVisible(true); } void XlruChartsForm::addTemperatureSample(int id, float value) { addSample(id, value); //, p_.temperatureY); } void XlruChartsForm::addVoltageSample(int id, float value) { addSample(id, value); //, p_.currentY); } XlruChartsForm::~XlruChartsForm() { delete &p_; delete ui; } void XlruChartsForm::on_toolButton_clicked() { //Purge auto ts=p_.timeNow.addSecs(-60*10); p_.timeStart=ts; p_.timeStartMs=ts.toMSecsSinceEpoch(); p_.commonX->setMin(ts); } void XlruChartsForm::on_tbClear_clicked() { foreach (QXYSeries* s, p_.s) { s->clear(); } p_.commonX->setMin(p_.commonX->max()); } void XlruChartsForm::connectMarkers() { // Connect all markers to handler foreach (QLegendMarker* marker, p_.ch->legend()->markers()) { // Disconnect possible existing connection to avoid multiple connections QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked())); QObject::connect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked())); } } void XlruChartsForm::disconnectMarkers() { foreach (QLegendMarker* marker, p_.ch->legend()->markers()) { QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked())); } } void XlruChartsForm::handleMarkerClicked() { QLegendMarker* marker = qobject_cast (sender()); Q_ASSERT(marker); switch (marker->type()) { case QLegendMarker::LegendMarkerTypeXY: { // Toggle visibility of series marker->series()->setVisible(!marker->series()->isVisible()); // Turn legend marker back to visible, since hiding series also hides the marker // and we don't want it to happen now. marker->setVisible(true); // Dim the marker, if series is not visible qreal alpha = 1.0; if (!marker->series()->isVisible()) { alpha = 0.5; } QColor color; QBrush brush = marker->labelBrush(); color = brush.color(); color.setAlphaF(alpha); brush.setColor(color); marker->setLabelBrush(brush); brush = marker->brush(); color = brush.color(); color.setAlphaF(alpha); brush.setColor(color); marker->setBrush(brush); QPen pen = marker->pen(); color = pen.color(); color.setAlphaF(alpha); pen.setColor(color); marker->setPen(pen); break; } default: { qDebug() << "Unknown marker type"; break; } } } #define USE_CALLOUT 1 void XlruChartsForm::connectTooltip() { // Connect all series to handler foreach (Sensor s, p_.sensors) { connect(s.series, &QLineSeries::clicked, this, &XlruChartsForm::keepCallout); connect(s.series, &QLineSeries::hovered, this, &XlruChartsForm::tooltip); } } void XlruChartsForm::keepCallout() { #if USE_CALLOUT==1 QAbstractSeries *series = qobject_cast(sender()); m_callouts.append(m_tooltip); m_tooltip = new Callout(p_.ch, series); m_tooltip->hide(); #endif } void XlruChartsForm::tooltip(QPointF point, bool state) { #if USE_CALLOUT==0 if (!state) return; QAbstractSeries *series = qobject_cast(sender()); QString msg=QString("%1=%2 ").arg(series->name()).arg(point.y()); QPointF anchor = series->chart()->mapToPosition(point, series); QToolTip::showText(mapToGlobal(QPoint(anchor.x(), anchor.y())), msg, this); #else QAbstractSeries *series = qobject_cast(sender()); if (m_tooltip == 0){ m_tooltip = new Callout(p_.ch, series); } if (state) { m_tooltip->setSeries(series); //qreal yVal = p_.ch->mapToValue(point, series).y(); m_tooltip->setText(QString("%1\n Y: %2 ").arg(series->name()).arg(point.y())); m_tooltip->setAnchor(point); m_tooltip->setZValue(11); m_tooltip->updateGeometry(); m_tooltip->show(); } else { m_tooltip->hide(); } #endif }