29 #include <QVBoxLayout>
40 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
42 _layout =
new QVBoxLayout(
this);
43 _layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
47 _plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
48 _plot->setMinimumHeight(225);
62 _layout->addWidget(_plot);
67 connect(_plot, SIGNAL(mousePress(QMouseEvent*)),
this, SLOT(scopeMousePress()));
68 connect(_plot, SIGNAL(mouseWheel(QWheelEvent*)),
this, SLOT(scopeMouseWheel()));
69 connect(_plot, SIGNAL(mouseDoubleClick(QMouseEvent*)),
this, SLOT(rescaleAxes()));
72 _plot->setContextMenuPolicy(Qt::CustomContextMenu);
73 connect(_plot, SIGNAL(customContextMenuRequested(
const QPoint&)),
this, SLOT(scopeContextMenuRequest(
const QPoint&)));
79 setMouseTracking(
true);
94 QFont legend_font = font();
95 legend_font.setPointSize(8);
100 border_pen.setColor(QColor(
"white"));
128 if (event->mimeData()->hasFormat(
"text/plain"))
event->acceptProposedAction();
133 QString message =
event->mimeData()->text();
140 event->acceptProposedAction();
145 if (event->button() == Qt::LeftButton)
154 if (!(event->buttons() & Qt::LeftButton))
157 if (event->y() >= height() - 5)
160 setCursor(Qt::SizeVerCursor);
166 setCursor(Qt::ArrowCursor);
174 if (event->y() <=
_plot->minimumHeight() +
_layout->margin() + 20)
return;
180 QPoint window_coords = mapFrom(window(), QPoint(0, window()->height()));
181 if (event->y() < window_coords.y() - 3)
184 setFixedHeight(event->y());
190 setFixedHeight(height() + 2);
203 const SignalHelper::SignalData* signal =
_signal_helper->signalData(signal_key);
206 addSignal(signal_key, value_idx, *signal);
209 PRINT_WARNING(
"ScopeWidget::addSignal(): signal " << signal_key.toStdString() <<
" not found.");
212 void ScopeWidget::addSignal(
const QString& signal_key,
int value_idx,
const SignalHelper::SignalData& signal_data)
224 data.legend_text = signal_data.name;
225 data.zero_order_hold = signal_data.zero_order_hold;
226 if (signal_data.dimension > 1)
227 data.legend_text +=
"/" + ((value_idx < signal_data.value_labels.size()) ? signal_data.value_labels[value_idx] : QString::number(value_idx));
228 data.value_idx = value_idx;
229 data.task_id = signal_data.task_id;
232 data.plottable =
nullptr;
234 switch (signal_data.signal->getType())
238 const TimeSeries* ts =
static_cast<const TimeSeriesSignal*
>(signal_data.signal.get())->getTimeSeries();
239 data.plottable =
addTimeSeriesGraph(*ts, value_idx, data.graph_color, data.legend_text, signal_data.zero_order_hold,
true);
244 data.ts_sequence = std::static_pointer_cast<const TimeSeriesSequenceSignal>(signal_data.signal)->getSequencePtr();
251 data.plottable =
addBoxPlot(*set, data.graph_color, data.legend_text,
true);
256 QMessageBox::warning(
this, tr(
"Cannot plot Signal"),
"Signal type not supported yet.");
262 bool zero_order_hold,
bool replot)
266 if (value_idx < 0 || value_idx >= time_series.getValueDimension())
271 QVector<double> time;
272 for (
const double& t : time_series.getTime()) time.push_back(t + time_series.getTimeFromStart());
274 QVector<double> values;
276 for (
int t = 0; t < values_mat.cols(); ++t)
278 values.push_back(values_mat(value_idx, t));
283 graph->
setPen(QPen(color));
287 graph->
setData(time, values,
true);
321 if (!data.plottable)
return;
330 if (data.value_idx < 0 || data.value_idx >= time_series.
getValueDimension())
return;
332 QVector<double> time;
335 QVector<double> values;
337 for (
int t = 0; t < values_mat.cols(); ++t)
339 values.push_back(values_mat(data.value_idx, t));
352 if (graph->
data()->isEmpty())
366 for (
int i = 0; i < active_signals.size(); ++i)
369 if (active_signals[i].value().plottable ==
nullptr)
379 QCPGraph* graph =
dynamic_cast<QCPGraph*
>(active_signals[i].value().plottable);
380 if (!graph)
continue;
382 if (active_signals[i].value().value_idx < measurement->getValues().size())
384 updateTimeSeriesGraph(active_signals[i].value(), measurement->getTime(), measurement->getValues()[active_signals[i].value().value_idx],
392 "Received values for plotting a time-series to scope, but the dimension is smaller than the values "
418 if (!data.ts_sequence)
return;
427 auto found_it = std::find_if(data.ts_sequence->getSequence().rbegin(), data.ts_sequence->getSequence().rend(),
428 [
this](
const TimeSeries::Ptr& ts) { return ts->getTimeFromStart() <= _current_preview_time; });
429 if (found_it != data.ts_sequence->getSequence().rend())
432 current_ts = *found_it;
437 if (data.ts_sequence->getSequence().empty())
439 PRINT_ERROR(
"ScopeWidget::updateTimeSeriesSequenceGraph(): time series sequence is empty.");
442 current_ts = data.ts_sequence->getSequence().front();
454 data.plottable =
addTimeSeriesGraph(*current_ts, data.value_idx, data.graph_color, data.legend_text, data.zero_order_hold,
replot);
461 if (indexed_values_set.isEmpty())
return nullptr;
465 for (
const auto& item : indexed_values_set.getData())
468 std::vector<double> data = item.second;
471 double minimum = *std::min_element(data.begin(), data.end());
472 double maximum = *std::max_element(data.begin(), data.end());
474 int num_quartile1 = data.size() / 4;
475 int num_quartile2 = data.size() / 2;
476 int num_quartile3 = num_quartile1 + num_quartile2;
478 std::nth_element(data.begin(), data.begin() + num_quartile1, data.end());
479 std::nth_element(data.begin() + num_quartile1 + 1, data.begin() + num_quartile2, data.end());
480 std::nth_element(data.begin() + num_quartile2 + 1, data.begin() + num_quartile3, data.end());
482 double lower_quartile = data[num_quartile1];
483 double median = data[num_quartile2];
484 double upper_quartile = data[num_quartile3];
486 box->
addData(item.first, minimum, lower_quartile, median, upper_quartile, maximum);
515 removed = (
int)iterators.size();
539 if (prev_task_id == task_id - 1)
581 QVector<QHash<QString, ScopeWidget::SignalData>::iterator> active_list;
594 found = (signal_key.compare(key) == 0);
601 found = (token_active.back().compare(token_source.back()) == 0);
606 PRINT_WARNING(
"ScopeWidget::findActiveSignal(): selected search type not implemented.");
609 if (found) active_list.push_back(it);
627 key = tokens.front();
628 if (tokens.size() > 1)
631 value_idx = tokens.back().toInt(&
ok);
634 PRINT_ERROR(
"QString::fromValueEnvodedKey: cannot detect value index in " << value_encoded_key.toStdString());
643 PRINT_WARNING(
"QString::fromValueEnvodedKey: no value_idx found in " << value_encoded_key.toStdString() <<
". Setting value_idx to 0");
690 if (graph_item->
selectTest(point,
false) >= 0)
692 QAction* remove_action =
new QAction(tr(
"Remove signal"),
this);
693 connect(remove_action, &QAction::triggered, [
this, it]() {
697 menu.addAction(remove_action);
706 QAction* rescale_axes =
new QAction(tr(
"Rescale axes"),
this);
707 connect(rescale_axes, &QAction::triggered, [
this]() {
rescaleAxes(); });
708 menu.addAction(rescale_axes);
712 bool signal_options =
false;
716 signal_options =
true;
721 signal_options =
true;
724 if (signal_options) menu.addSeparator();
726 QAction* close_scope =
new QAction(tr(
"Close scope"),
this);
727 connect(close_scope, &QAction::triggered, [
this]() { deleteLater(); });
728 menu.addAction(close_scope);
732 menu.exec(
_plot->mapToGlobal(point));
742 if (it.value().plottable && it.value().plottable->selected())
759 if (!graph)
return false;
761 QVariant active = graph->property(
"active");
763 if (active.isValid() && active.toBool())
return true;
777 graph->setProperty(
"active", active);