#include "traceplot.h" #include "tracemarker.h" #include "preferences.h" #include #include #include std::set TracePlot::plots; using namespace std; TracePlot::TracePlot(TraceModel &model, QWidget *parent) : QWidget(parent), model(model), selectedMarker(nullptr), dropPending(false), dropTrace(nullptr) { contextmenu = new QMenu(); markedForDeletion = false; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); lastUpdate = QTime::currentTime(); sweep_fmin = std::numeric_limits::lowest(); sweep_fmax = std::numeric_limits::max(); // get notified when the span changes connect(&model, &TraceModel::SpanChanged, this, qOverload(&TracePlot::updateSpan)); plots.insert(this); cursorLabel = new QLabel("Test", this); cursorLabel->setStyleSheet("color: white;"); auto font = cursorLabel->font(); font.setPixelSize(12); cursorLabel->setFont(font); cursorLabel->hide(); setMouseTracking(true); setAcceptDrops(true); } TracePlot::~TracePlot() { plots.erase(this); delete contextmenu; delete cursorLabel; } void TracePlot::enableTrace(Trace *t, bool enabled) { if(traces[t] != enabled) { traces[t] = enabled; if(enabled) { // connect signals connect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot); connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot); connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded); connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved); } else { // disconnect from notifications disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot); disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot); disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded); disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved); } updateContextMenu(); replot(); } } void TracePlot::mouseDoubleClickEvent(QMouseEvent *) { emit doubleClicked(this); } void TracePlot::updateSpan(double min, double max) { sweep_fmin = min; sweep_fmax = max; triggerReplot(); } void TracePlot::initializeTraceInfo() { // Populate already present traces auto tvect = model.getTraces(); for(auto t : tvect) { newTraceAvailable(t); } // connect notification of traces added at later point connect(&model, &TraceModel::traceAdded, this, &TracePlot::newTraceAvailable); } void TracePlot::contextMenuEvent(QContextMenuEvent *event) { contextmenu->exec(event->globalPos()); if(markedForDeletion) { emit deleted(this); delete this; } } void TracePlot::paintEvent(QPaintEvent *event) { Q_UNUSED(event) auto pref = Preferences::getInstance(); QPainter p(this); // p.setRenderHint(QPainter::Antialiasing); // fill background p.setBackground(QBrush(pref.General.graphColors.background)); p.fillRect(0, 0, width(), height(), QBrush(pref.General.graphColors.background)); // show names of active traces QFont font = p.font(); font.setPixelSize(12); p.setFont(font); int x = 1; for(auto t : traces) { if(!t.second || !t.first->isVisible()) { continue; } auto textArea = QRect(x, 0, width() - x, marginTop); QRect usedArea; p.setPen(t.first->color()); p.drawText(textArea, 0, t.first->name() + " ", &usedArea); x += usedArea.width(); if(x >= width()) { // used up all available space break; } } p.setViewport(marginLeft, marginTop, width() - marginLeft - marginRight, height() - marginTop - marginBottom); p.setWindow(0, 0, width() - marginLeft - marginRight, height() - marginTop - marginBottom); draw(p); } void TracePlot::mousePressEvent(QMouseEvent *event) { auto clickPoint = event->pos() - QPoint(marginLeft, marginTop); // check if click was near a marker unsigned int closestDistance = numeric_limits::max(); TraceMarker *closestMarker = nullptr; for(auto t : traces) { if(!t.second) { // this trace is disabled, skip continue; } auto markers = t.first->getMarkers(); for(auto m : markers) { if(!m->isMovable()) { continue; } auto markerPoint = markerToPixel(m); if(markerPoint.isNull()) { // invalid, skip continue; } auto diff = markerPoint - clickPoint; unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y(); if(distance < closestDistance) { closestDistance = distance; closestMarker = m; } } } if(closestDistance <= 400) { selectedMarker = closestMarker; } else { selectedMarker = nullptr; } } void TracePlot::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event) selectedMarker = nullptr; } void TracePlot::mouseMoveEvent(QMouseEvent *event) { auto clickPoint = event->pos() - QPoint(marginLeft, marginTop); if(selectedMarker) { auto trace = selectedMarker->getTrace(); selectedMarker->setPosition(nearestTracePoint(trace, clickPoint)); cursorLabel->hide(); } else { auto text = mouseText(clickPoint); if(!text.isEmpty()) { cursorLabel->setText(text); cursorLabel->adjustSize(); cursorLabel->move(event->pos() + QPoint(15, 0)); cursorLabel->show(); } else { cursorLabel->hide(); } } } void TracePlot::leaveEvent(QEvent *event) { Q_UNUSED(event); cursorLabel->hide(); selectedMarker = nullptr; } void TracePlot::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("trace/pointer")) { auto data = event->mimeData()->data("trace/pointer"); QDataStream stream(&data, QIODevice::ReadOnly); quintptr dropPtr; stream >> dropPtr; auto trace = (Trace*) dropPtr; if(supported(trace)) { event->acceptProposedAction(); dropPending = true; dropTrace = trace; } } triggerReplot(); } void TracePlot::dropEvent(QDropEvent *event) { if(dropTrace) { traceDropped(dropTrace, event->pos() - - QPoint(marginLeft, marginTop)); } dropPending = false; dropTrace = nullptr; replot(); } void TracePlot::dragLeaveEvent(QDragLeaveEvent *event) { Q_UNUSED(event) dropPending = false; dropTrace = nullptr; replot(); } std::set TracePlot::getPlots() { return plots; } void TracePlot::newTraceAvailable(Trace *t) { if(supported(t)) { traces[t] = false; connect(t, &Trace::deleted, this, &TracePlot::traceDeleted); connect(t, &Trace::nameChanged, this, &TracePlot::updateContextMenu); connect(t, &Trace::typeChanged, this, &TracePlot::updateContextMenu); } updateContextMenu(); } void TracePlot::traceDeleted(Trace *t) { enableTrace(t, false); traces.erase(t); updateContextMenu(); triggerReplot(); } void TracePlot::triggerReplot() { auto now = QTime::currentTime(); if (lastUpdate.msecsTo(now) >= MinUpdateInterval) { lastUpdate = now; replot(); } } void TracePlot::markerAdded(TraceMarker *m) { connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot); connect(m, &TraceMarker::symbolChanged, this, &TracePlot::triggerReplot); triggerReplot(); } void TracePlot::markerRemoved(TraceMarker *m) { disconnect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot); disconnect(m, &TraceMarker::symbolChanged, this, &TracePlot::triggerReplot); triggerReplot(); }