// %flair:license{ // This file is part of the Flair framework distributed under the // CECILL-C License, Version 1.0. // %flair:license} #include "Scope.h" #include "Scrollbar.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define WHEEL_DIVIDOR 30 Scope::Scope(QString title, float ymin, float ymax, float view_size_s, unsigned int refresh_rate_ms, unsigned int history_size) : QwtPlot() { this->ymin = ymin; this->ymax = ymax; this->history_size = history_size; this->view_size_s = view_size_s; orig_view_size_s = view_size_s; // scroll bar scrollbar = new ScrollBar(Qt::Horizontal, canvas()); connect(scrollbar, SIGNAL(valueChanged(Qt::Orientation, float, float)), this, SLOT(scrollBarMoved(Qt::Orientation, float, float))); scrolling = true; // minimum size; sinon widget trop grand setMinimumHeight(10); setMinimumWidth(1); alignScales(); // is it necessary? // Initialize data elapsed_time_s = 0; // Assign a title setTitle(title); // Axis setAxisTitle(QwtPlot::xBottom, "Time (s)"); setAxisScale(QwtPlot::xBottom, 0, view_size_s); setAxisTitle(QwtPlot::yLeft, "Values"); setAxisScale(QwtPlot::yLeft, ymin, ymax); // grid QwtPlotGrid *grid = new QwtPlotGrid; grid->setPen(QPen(Qt::black, 0, Qt::DotLine)); grid->attach(this); // zoomer QwtPlotMagnifier *zoomer = new QwtPlotMagnifier(canvas()); zoomer->setWheelModifiers(Qt::ControlModifier); zoomer->setMouseButton(Qt::NoButton); zoomer->setAxisEnabled(xBottom, 0); // scroller QwtPlotPanner *scroller = new QwtPlotPanner(canvas()); scroller->setAxisEnabled(xBottom, 0); // legend QwtLegend *new_legend = new QwtLegend(); new_legend->setDefaultItemMode(QwtLegendData::Checkable); insertLegend(new_legend, QwtPlot::BottomLegend); connect(new_legend, SIGNAL(checked(const QVariant &, bool, int)), SLOT(legendChecked(const QVariant &, bool))); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(replot())); timer->start(refresh_rate_ms); } Scope::~Scope() { for (int i = 0; i < curves.count(); i++) { free(curves.at(i)->data_x); free(curves.at(i)->data_y); delete curves.at(i)->plot; free(curves.at(i)); } } void Scope::resetXView(void) { // setAxisScale(QwtPlot::xBottom,0, orig_view_size_s); changeViewSize(orig_view_size_s); } void Scope::resetYView(void) { setAxisScale(QwtPlot::yLeft, ymin, ymax); } bool Scope::eventFilter(QObject *o, QEvent *e) { switch (e->type()) { case QEvent::Resize: { const int fw = ((QwtPlotCanvas *)canvas())->frameWidth(); QRect rect; rect.setSize(((QResizeEvent *)e)->size()); rect.setRect(rect.x() + fw, rect.y() + fw, rect.width() - 2 * fw, rect.height() - 2 * fw); scrollbar->setGeometry(0, 0, rect.width(), 10); return true; } case QEvent::Wheel: { // ctrl+wheel is already handled for y zoom if (!(QApplication::keyboardModifiers() & Qt::ControlModifier)) { QWheelEvent *wheelevent = static_cast(e); if (view_size_s + wheelevent->delta() / WHEEL_DIVIDOR > 0) changeViewSize(view_size_s + wheelevent->delta() / WHEEL_DIVIDOR); } return true; } default: break; } return QwtPlot::eventFilter(o, e); } void Scope::changeViewSize(float new_view_size_s) { view_size_s = new_view_size_s; if (scrolling == false) { // 4 cas: on utilise le temps au milieu de la vue actuelle // le temps total est plus petit que le view_size_s, on affiche tout: if (elapsed_time_s < view_size_s) { scrolling = true; } else { double min = (min_scroll + max_scroll) / 2 - view_size_s / 2; double max = (min_scroll + max_scroll) / 2 + view_size_s / 2; // on va du debut jusqu'a view size if (min < 0) { min = 0; max = view_size_s; } // on va de fin-viewsize jusqu'a la fin if (max > elapsed_time_s) { min = elapsed_time_s - view_size_s; max = elapsed_time_s; } scrollbar->moveSlider( min, max); // move slider coupe le signal, on fait aussi le scrollbar scrollBarMoved(Qt::Horizontal, min, max); } } } void Scope::legendChecked(const QVariant &itemInfo, bool on) { QwtPlotItem *plotItem = infoToItem(itemInfo); if (plotItem) showCurve(plotItem, on); } void Scope::showCurve(QwtPlotItem *item, bool on) { item->setVisible(on); QwtLegend *lgd = qobject_cast(legend()); QList legendWidgets = lgd->legendWidgets(itemToInfo(item)); if (legendWidgets.size() == 1) { QwtLegendLabel *legendLabel = qobject_cast(legendWidgets[0]); if (legendLabel) legendLabel->setChecked(on); } } int Scope::addCurve(QPen pen, QString legend) { Curve *curve = (Curve *)malloc(sizeof(Curve)); curve->data_x = (double *)calloc(history_size, sizeof(double)); curve->data_y = (double *)calloc(history_size, sizeof(double)); curve->index = 0; curve->min_index = 0; curve->max_index = 0; // Insert new curve curve->plot = new QwtPlotCurve(legend); curve->plot->attach(this); curve->plot->setPen(pen); curves.append(curve); showCurve(curve->plot, true); return curves.count() - 1; } void Scope::updateCurve(Curve *curve) { if (scrolling == true) { curve->plot->setRawSamples(&curve->data_x[curve->min_index], &curve->data_y[curve->min_index], curve->max_index - curve->min_index); } if (curve->index == history_size) { // printf("a revoir qd on arrive a la fin, il faudrait faire un realloc pour // rien perdre\n"); // attention le setrawdata s'attend a ce que l'adresse change pas, ce qui // n'est pas le cas avec lerealloc // il faudra refaire un setrawdata ici curve->index = 0; curve->min_index = 0; scrolling = true; } // determine les index pour la visualisation if (scrolling == true) { curve->max_index = curve->index; computeMinIndex(curve, elapsed_time_s - view_size_s); } scrollbar->setBase(0, elapsed_time_s); if (scrolling == true) { scrollbar->moveSlider(elapsed_time_s - view_size_s, elapsed_time_s); if (elapsed_time_s < view_size_s) { setAxisScale(QwtPlot::xBottom, 0, view_size_s); } else { setAxisScale(QwtPlot::xBottom, elapsed_time_s - view_size_s, elapsed_time_s); } } else { scrollbar->moveSlider(min_scroll, max_scroll); } } void Scope::alignScales(void) { // The code below shows how to align the scales to // the canvas frame, but is also a good example demonstrating // why the spreaded API needs polishing. /* plot->canvas()->setFrameStyle(QFrame::Box | QFrame::Plain ); plot->canvas()->setLineWidth(1); */ for (int i = 0; i < QwtPlot::axisCnt; i++) { QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(i); if (scaleWidget) scaleWidget->setMargin(0); QwtScaleDraw *scaleDraw = (QwtScaleDraw *)axisScaleDraw(i); if (scaleDraw) scaleDraw->enableComponent(QwtAbstractScaleDraw::Backbone, false); } } void Scope::scrollBarMoved(Qt::Orientation o, float min, float max) { min_scroll = min; max_scroll = max; if (max == scrollbar->maxBaseValue()) { scrolling = true; } else { scrolling = false; setAxisScale(QwtPlot::xBottom, min, max); // determine les index pour la visualisation for (int i = 0; i < curves.count(); i++) { computeMinIndex(curves.at(i), min); computeMaxIndex(curves.at(i), max); curves.at(i)->plot->setRawSamples( &curves.at(i)->data_x[curves.at(i)->min_index], &curves.at(i)->data_y[curves.at(i)->min_index], curves.at(i)->max_index - curves.at(i)->min_index); } } } // TODO: faire une dichotomie void Scope::computeMinIndex(Curve *curve, float displayed_min_time) { if (curve->data_x[curve->index] > displayed_min_time) { if (curve->data_x[curve->min_index] < displayed_min_time) { while (curve->data_x[curve->min_index] < displayed_min_time && curve->min_index != curve->index) curve->min_index++; } else { while (curve->data_x[curve->min_index] > displayed_min_time && curve->min_index != 0) curve->min_index--; } } } void Scope::computeMaxIndex(Curve *curve, float displayed_max_time) { if (curve->data_x[curve->max_index] < displayed_max_time) { while (curve->data_x[curve->max_index] < displayed_max_time && curve->max_index != curve->index) curve->max_index++; } else { while (curve->data_x[curve->max_index] > displayed_max_time) curve->max_index--; } }