v2.0.0
Loading...
Searching...
No Matches
rtfiffrawviewdelegate.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
22#include "rtfiffrawviewmodel.h"
23#include "../rtfiffrawview.h"
24
25#include "../scalingview.h"
26
27//=============================================================================================================
28// QT INCLUDES
29//=============================================================================================================
30
31#include <QPainter>
32#include <QDebug>
33#include <QPainterPath>
34
35//=============================================================================================================
36// EIGEN INCLUDES
37//=============================================================================================================
38
39//=============================================================================================================
40// USED NAMESPACES
41//=============================================================================================================
42
43using namespace DISPLIB;
44
45//=============================================================================================================
46// DEFINE MEMBER METHODS
47//=============================================================================================================
48
50: QAbstractItemDelegate(parent)
51, m_pParent(parent)
52, m_dMaxValue(0.0)
53, m_dScaleY(0.0)
54, m_iActiveRow(-1)
55, m_iUpperItemIndex(0)
56{
57}
58
59//=============================================================================================================
60
61void RtFiffRawViewDelegate::initPainterPaths(const QAbstractTableModel* model)
62{
63 for (int i = 0; i < model->rowCount(); i++)
64 m_painterPaths.append(QPainterPath());
65
66 // Init pens
67 QColor colorMarker(233, 0, 43);
68 colorMarker.setAlpha(160);
69
70 m_penMarker = QPen(colorMarker, 2, Qt::DashLine);
71
72 m_penGrid = QPen(Qt::black, 1, Qt::DashLine);
73 m_penTimeSpacers = QPen(Qt::black, 1, Qt::DashLine);
74
75 m_penFreeze = QPen(Qt::darkGray, 1, Qt::SolidLine);
76 m_penFreezeSelected = QPen(Qt::darkRed, 1, Qt::SolidLine);
77
78 m_penFreezeBad = QPen(Qt::darkGray, 0.1, Qt::SolidLine);
79 m_penFreezeSelectedBad = QPen(Qt::darkRed, 1, Qt::SolidLine);
80
81 m_penNormal = QPen(Qt::darkBlue, 1, Qt::SolidLine);
82 m_penNormalSelected = QPen(Qt::red, 1, Qt::SolidLine);
83
84 m_penNormalBad = QPen(Qt::darkBlue, 0.1, Qt::SolidLine);
85 m_penNormalSelectedBad = QPen(Qt::red, 1, Qt::SolidLine);
86
87 m_penCrosshair = QPen(QColor(255, 140, 0), 1, Qt::SolidLine);
88}
89
90//=============================================================================================================
91
92void RtFiffRawViewDelegate::paint(QPainter* painter,
93 const QStyleOptionViewItem& option,
94 const QModelIndex& index) const
95{
96 float t_fPlotHeight = option.rect.height();
97 painter->setRenderHint(QPainter::Antialiasing, true);
98
99 switch (index.column()) {
100 case 0: { //chnames
101 painter->save();
102
103 painter->rotate(-90);
104 painter->drawText(QRectF(-option.rect.y() - t_fPlotHeight, 0, t_fPlotHeight, 20), Qt::AlignCenter, index.model()->data(index, Qt::DisplayRole).toString());
105
106 painter->restore();
107 break;
108 }
109
110 case 1: { //data plot
111 QBrush backgroundBrush = index.model()->data(index, Qt::BackgroundRole).value<QBrush>();
112 bool bIsBadChannel = index.model()->data(index.model()->index(index.row(), 2), Qt::DisplayRole).toBool();
113
114 // Plot background based on user chosen color
115 // This is a rather ugly hack in order to cope with QOpenGLWidget's/QtableView's problem when setting a background color
116 if (index.row() == m_iUpperItemIndex) {
117 painter->save();
118 painter->setBrushOrigin(option.rect.topLeft());
119 QRect rect = option.rect;
120 rect.setHeight(2000);
121 painter->fillRect(rect, backgroundBrush);
122 painter->restore();
123 }
124
125 // Draw special background when channel is marked as bad
126 if (bIsBadChannel) {
127 painter->save();
128 QBrush brush(QColor(254, 74, 93, 40));
129 painter->setBrushOrigin(option.rect.topLeft());
130 painter->fillRect(option.rect, brush);
131 painter->restore();
132 }
133
134 // //Highlight selected channels
135 // if(option.state & QStyle::State_Selected) {
136 // QPointF oldBO = painter->brushOrigin();
137 // painter->setBrushOrigin(option.rect.topLeft());
138 // painter->fillRect(option.rect, option.palette.highlight());
139 // painter->setBrushOrigin(oldBO);
140 // }
141
142 //Get data
143 QVariant variant = index.model()->data(index, Qt::DisplayRole);
144 RowVectorPair data = variant.value<RowVectorPair>();
145
146 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
147
148 if (data.second > 0) {
149 QPainterPath path(QPointF(option.rect.x(), option.rect.y()));
150
151 // //Plot hovering marker
152 // createMarkerPath(option, path);
153
154 // painter->save();
155 // painter->setPen(m_penMarker);
156 // painter->drawPath(path);
157 // painter->restore();
158
159 //Plot grid
160 createGridPath(index, option, path, data);
161
162 painter->save();
163 painter->setPen(m_penGrid);
164 painter->drawPath(path);
165 painter->restore();
166
167 //Plot time spacers
168 createTimeSpacersPath(index, option, path, data);
169
170 painter->save();
171 painter->setPen(m_penTimeSpacers);
172 painter->drawPath(path);
173 painter->restore();
174
175 //Plot detected triggers
176 path = QPainterPath(QPointF(option.rect.x(), option.rect.y())); //QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));
177 painter->save();
178 createTriggerPath(painter, index, option, path, data);
179 painter->restore();
180
181 //Plot trigger threshold
182 if (index.row() == t_pModel->getCurrentTriggerIndex() &&
183 t_pModel->triggerDetectionActive()) {
184 path = QPainterPath(QPointF(option.rect.x(), option.rect.y())); //QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));
185 QPointF textPosition;
186 createTriggerThresholdPath(index, option, path, data, textPosition);
187
188 painter->save();
189 painter->setPen(QPen(Qt::red, 1, Qt::DashLine));
190 painter->drawPath(path);
191 painter->drawText(textPosition, QString("%1 Threshold").arg(t_pModel->getTriggerName()));
192 painter->restore();
193 }
194
195 path = QPainterPath(QPointF(option.rect.x(), option.rect.y())); //QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));
196
197 //Plot data path
198 createPlotPath(index, option, path, data);
199
200 painter->setRenderHint(QPainter::Antialiasing, true);
201 painter->save();
202 painter->translate(0, t_fPlotHeight / 2);
203
204 if (bIsBadChannel) {
205 if (t_pModel->isFreezed()) {
206 if (option.state & QStyle::State_Selected)
207 painter->setPen(m_penFreezeSelectedBad);
208 else
209 painter->setPen(m_penFreezeBad);
210 } else {
211 if (option.state & QStyle::State_Selected)
212 painter->setPen(m_penNormalSelectedBad);
213 else
214 painter->setPen(m_penNormalBad);
215 }
216 } else {
217 if (t_pModel->isFreezed()) {
218 if (option.state & QStyle::State_Selected)
219 painter->setPen(m_penFreezeSelected);
220 else
221 painter->setPen(m_penFreeze);
222 } else {
223 if (option.state & QStyle::State_Selected)
224 painter->setPen(m_penNormalSelected);
225 else
226 painter->setPen(m_penNormal);
227 }
228 }
229
230 painter->drawPath(path);
231 painter->restore();
232
233 //Plot current position marker
234 path = QPainterPath(QPointF(option.rect.x(), option.rect.y())); //QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));
235 createCurrentPositionMarkerPath(index, option, path);
236
237 painter->save();
238 painter->setPen(m_penMarker);
239 painter->drawPath(path);
240 painter->restore();
241
242 path = QPainterPath(QPointF(option.rect.x(), option.rect.y())); //QPointF(option.rect.x()+t_rtmsaModel->relFiffCursor(),option.rect.y()));
243 createMarkerPath(index, option, path);
244
245 painter->save();
246 painter->setPen(QPen(Qt::green, 1, Qt::SolidLine));
247 painter->drawPath(path);
248 painter->restore();
249
250 paintCrosshair(painter, index, option, data);
251 }
252 break;
253 }
254 }
255}
256
257//=============================================================================================================
258
259QSize RtFiffRawViewDelegate::sizeHint(const QStyleOptionViewItem& option,
260 const QModelIndex& index) const
261{
262 QSize size = option.rect.size();
263
264 switch (index.column()) {
265 case 0:
266 size = QSize(20, option.rect.height());
267 break;
268 case 1:
269 QList<QVector<float>> data = index.model()->data(index).value<QList<QVector<float>>>();
270 // qint32 nsamples = (static_cast<const RtFiffRawViewModel*>(index.model()))->lastSample()-(static_cast<const RtFiffRawViewModel*>(index.model()))->firstSample();
271 // size = QSize(nsamples*m_dDx,m_dPlotHeight);
272 Q_UNUSED(option);
273 break;
274 }
275
276 return size;
277}
278
279//=============================================================================================================
280
282 int activeRow)
283{
284 m_markerPosition = position;
285 m_iActiveRow = activeRow;
286}
287
288//=============================================================================================================
289
290void RtFiffRawViewDelegate::paintCrosshair(QPainter* painter,
291 const QModelIndex& index,
292 const QStyleOptionViewItem& option,
293 const RowVectorPair& data) const
294{
295 if (m_iActiveRow < 0 || m_markerPosition.x() < option.rect.left() || m_markerPosition.x() > option.rect.right()) {
296 return;
297 }
298
299 painter->save();
300 painter->setPen(m_penCrosshair);
301 painter->drawLine(QPointF(m_markerPosition.x(), option.rect.top()), QPointF(m_markerPosition.x(), option.rect.bottom()));
302
303 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
304 const double dDx = static_cast<double>(option.rect.width()) / static_cast<double>(t_pModel->getMaxSamples());
305 const int iColumn = static_cast<int>((m_markerPosition.x() - option.rect.x()) / dDx);
306 if (index.row() == m_iActiveRow && iColumn >= 0 && iColumn < data.second) {
307 const double dScaleY = option.rect.height() / (2 * t_pModel->getMaxValueFromRawViewModel(index.row()));
308 const QPointF point(option.rect.x() + (iColumn + 1) * dDx, option.rect.center().y() + 0.5 - data.first[iColumn] * dScaleY);
309 painter->setPen(Qt::NoPen);
310 painter->setBrush(m_penCrosshair.color());
311 painter->drawEllipse(point, 3.0, 3.0);
312 }
313 painter->restore();
314}
315
316//=============================================================================================================
317
318void RtFiffRawViewDelegate::setSignalColor(const QColor& signalColor)
319{
320 m_penNormal.setColor(signalColor);
321 m_penNormalBad.setColor(signalColor);
322}
323
324//=============================================================================================================
325
327{
328 return m_penNormal.color();
329}
330
331//=============================================================================================================
332
334{
335 m_iUpperItemIndex = iUpperItemIndex;
336}
337
338//=============================================================================================================
339
340void RtFiffRawViewDelegate::createPlotPath(const QModelIndex& index,
341 const QStyleOptionViewItem& option,
342 QPainterPath& path,
343 const RowVectorPair& data) const
344{
345 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
346 int iPlotSizePx = option.rect.width();
347 int iNumSamples = t_pModel->getMaxSamples();
348 double dPixelsPerSample = static_cast<double>(iPlotSizePx) / static_cast<double>(iNumSamples);
349
350 //get maximum range of respective channel type (range value in FiffChInfo does not seem to contain a reasonable value)
351 double dMaxYValueEstimate = t_pModel->getMaxValueFromRawViewModel(index.row());
352 double dScaleY = option.rect.height() / (2 * dMaxYValueEstimate);
353 double dChannelOffset = path.currentPosition().y();
354
355 // qDebug() << " - - - - - - - - - - - - - - - - - - - - - ";
356 // qDebug() << " iPlotSizePx = " << iPlotSizePx;
357 // qDebug() << " iNumSamples = " << iNumSamples;
358 // qDebug() << " dPixelsPerSample = " << dPixelsPerSample;
359 // qDebug() << " iTimeCursorSample = " << iTimeCursorSample;
360 // qDebug() << " firstValuePreviousPlot = " << firstValuePreviousPlot;
361
362 // qDebug() << " dMaxYValueEstimate = " << dMaxYValueEstimate;
363 // qDebug() << " dScaleY = " << dScaleY;
364 // qDebug() << " dChannelOffset = " << dChannelOffset;
365
366 // Move to initial starting point
367 path.moveTo(calcPoint(path, 0., 0., dChannelOffset, dScaleY));
368 double dY(0);
369
370 // The plot works as a rolling time-cursor, ploting data on top of previous
371 // runs. You always plot one whole window of data, between first sample and
372 // numSamplesToPlot (or data.second) Even if the only change is a new block
373 // of samples to the left of the time-cursor. At any time, to the left of
374 // the time-cursor you have the most recent data (A part) corresponding to
375 // the current roll. To the right of the time-cursor, you have data
376 // corresponding to the previous roll.
377 for (int j = 0; j < data.second; ++j) {
378 dY = data.first[j];
379 path.lineTo(calcPoint(path, dPixelsPerSample, dY,
380 dChannelOffset, dScaleY));
381 }
382}
383
384//=============================================================================================================
385
386void RtFiffRawViewDelegate::createCurrentPositionMarkerPath(const QModelIndex& index, const QStyleOptionViewItem& option, QPainterPath& path) const
387{
388 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
389
390 float dDx = ((float)option.rect.width()) / t_pModel->getMaxSamples();
391 float currentSampleIndex = option.rect.x() + t_pModel->getCurrentSampleIndex() * dDx;
392
393 float yStart = option.rect.topLeft().y();
394 float yEnd = option.rect.bottomRight().y();
395
396 path.moveTo(currentSampleIndex, yStart);
397 path.lineTo(currentSampleIndex, yEnd);
398}
399
400//=============================================================================================================
401
402void RtFiffRawViewDelegate::createGridPath(const QModelIndex& index, const QStyleOptionViewItem& option, QPainterPath& path, RowVectorPair& data) const
403{
404 Q_UNUSED(data)
405
406 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
407
408 if (t_pModel->numVLines() > 0) {
409 //vertical lines
410 float distance = float(option.rect.width()) / (t_pModel->numVLines() + 1);
411
412 float yStart = option.rect.topLeft().y();
413
414 float yEnd = option.rect.bottomRight().y();
415
416 for (qint32 i = 0; i < t_pModel->numVLines(); ++i) {
417 float x = distance * (i + 1);
418 path.moveTo(x, yStart);
419 path.lineTo(x, yEnd);
420 }
421 }
422}
423
424//=============================================================================================================
425
426void RtFiffRawViewDelegate::createTimeSpacersPath(const QModelIndex& index, const QStyleOptionViewItem& option, QPainterPath& path, RowVectorPair& data) const
427{
428 Q_UNUSED(data)
429
430 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
431
432 if (t_pModel->getNumberOfTimeSpacers() > 0) {
433 //vertical lines
434 float distanceSec = float(option.rect.width()) / (t_pModel->numVLines() + 1);
435 float distanceSpacers = distanceSec / (t_pModel->getNumberOfTimeSpacers() + 1);
436
437 float yStart = option.rect.topLeft().y();
438
439 float yEnd = option.rect.bottomRight().y();
440
441 for (qint32 t = 0; t < t_pModel->numVLines() + 1; ++t) {
442 for (qint32 i = 0; i < t_pModel->getNumberOfTimeSpacers(); ++i) {
443 float x = (distanceSec * t) + (distanceSpacers * (i + 1));
444 path.moveTo(x, yStart);
445 path.lineTo(x, yEnd);
446 }
447 }
448 }
449}
450
451//=============================================================================================================
452
453void RtFiffRawViewDelegate::createTriggerPath(QPainter* painter,
454 const QModelIndex& index,
455 const QStyleOptionViewItem& option,
456 QPainterPath& path,
457 RowVectorPair& data) const
458{
459 Q_UNUSED(data)
460 Q_UNUSED(path)
461
462 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
463
464 QList<QPair<int, double>> detectedTriggers = t_pModel->getDetectedTriggers();
465 QList<QPair<int, double>> detectedTriggersOld = t_pModel->getDetectedTriggersOld();
466 QMap<double, QColor> mapTriggerTypeColors = t_pModel->getTriggerColor();
467
468 float yStart = option.rect.topLeft().y();
469 float yEnd = option.rect.bottomRight().y();
470 float dDx = ((float)option.rect.width()) / t_pModel->getMaxSamples();
471
472 int currentSampleIndex = t_pModel->getCurrentSampleIndex();
473
474 //Newly detected triggers
475 for (int u = 0; u < detectedTriggers.size(); ++u) {
476 QPainterPath markerPath;
477
478 int triggerPos = detectedTriggers[u].first;
479
480 painter->save();
481 if (mapTriggerTypeColors.contains(detectedTriggers[u].second)) {
482 painter->setPen(QPen(mapTriggerTypeColors[detectedTriggers[u].second], 1.5, Qt::SolidLine));
483 }
484
485 if (triggerPos <= currentSampleIndex + t_pModel->getCurrentOverlapAddDelay()) {
486 markerPath.moveTo(option.rect.x() + static_cast<qreal>(triggerPos) * dDx, yStart);
487 markerPath.lineTo(option.rect.x() + static_cast<qreal>(triggerPos) * dDx, yEnd);
488 }
489
490 painter->drawPath(markerPath);
491 painter->restore();
492 }
493
494 //Old detected triggers
495 for (int u = 0; u < detectedTriggersOld.size(); ++u) {
496 QPainterPath markerPath;
497
498 int triggerPos = detectedTriggersOld[u].first;
499
500 if (triggerPos >= currentSampleIndex + t_pModel->getCurrentOverlapAddDelay()) {
501 painter->save();
502
503 if (mapTriggerTypeColors.contains(detectedTriggersOld[u].second)) {
504 painter->setPen(QPen(mapTriggerTypeColors[detectedTriggersOld[u].second], 1.5, Qt::SolidLine));
505 }
506
507 markerPath.moveTo(option.rect.x() + static_cast<qreal>(triggerPos) * dDx, yStart);
508 markerPath.lineTo(option.rect.x() + static_cast<qreal>(triggerPos) * dDx, yEnd);
509
510 painter->drawPath(markerPath);
511 painter->restore();
512 }
513 }
514}
515
516//=============================================================================================================
517
518void RtFiffRawViewDelegate::createTriggerThresholdPath(const QModelIndex& index,
519 const QStyleOptionViewItem& option,
520 QPainterPath& path,
521 RowVectorPair& data,
522 QPointF& textPosition) const
523{
524 Q_UNUSED(data)
525
526 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
527
528 //get maximum range of respective channel type (range value in FiffChInfo does not seem to contain a reasonable value)
529 qint32 kind = t_pModel->getKind(index.row());
530 double dMaxValue = 1e-9f;
531
532 switch (kind) {
533 case FIFFV_STIM_CH: {
534 dMaxValue = 5.0;
535 if (t_pModel->getScaling().contains(FIFFV_STIM_CH))
536 dMaxValue = t_pModel->getScaling()[FIFFV_STIM_CH];
537 break;
538 }
539 }
540
541 double dScaleY = option.rect.height() / (2 * dMaxValue);
542 double triggerThreshold = -1 * (t_pModel->getTriggerThreshold());
543
544 path.moveTo(option.rect.topLeft().x(), option.rect.topLeft().y() + option.rect.height() / 2 + dScaleY * triggerThreshold);
545 path.lineTo(option.rect.topRight().x(), option.rect.topLeft().y() + option.rect.height() / 2 + dScaleY * triggerThreshold);
546
547 textPosition = QPointF(option.rect.topLeft().x() + 5, option.rect.topLeft().y() + option.rect.height() / 2 + dScaleY * triggerThreshold - 5);
548}
549
550//=============================================================================================================
551
552void RtFiffRawViewDelegate::createMarkerPath(const QModelIndex& index,
553 const QStyleOptionViewItem& option,
554 QPainterPath& path) const
555{
556 const RtFiffRawViewModel* t_pModel = static_cast<const RtFiffRawViewModel*>(index.model());
557
558 int iOffset = t_pModel->getFirstSampleOffset();
559 int iTimeCursorSample = t_pModel->getCurrentSampleIndex();
560 int iMaxSample = t_pModel->getMaxSamples();
561
562 double dDx = static_cast<double>(option.rect.width()) / static_cast<double>(iMaxSample);
563
564 float yStart = option.rect.topLeft().y();
565 float yEnd = option.rect.bottomRight().y();
566
567 int iEarliestDrawnSample = iOffset - iMaxSample + iTimeCursorSample;
568 int iLatestDrawnSample = iOffset + iTimeCursorSample;
569
570 auto eventSamples = t_pModel->getEventsToDisplay(iEarliestDrawnSample, iLatestDrawnSample);
571
572 for (int iEventSample : eventSamples) {
573 int iLastStartingSample = iOffset - iMaxSample;
574 int iDrawPositionInSamples = (iEventSample - iLastStartingSample) % iMaxSample;
575
576 float iPositionInPixels = option.rect.x() + static_cast<float>(iDrawPositionInSamples) * dDx;
577
578 path.moveTo(iPositionInPixels, yStart);
579 path.lineTo(iPositionInPixels, yEnd);
580 }
581}
582
583//=============================================================================================================
584
585QPointF RtFiffRawViewDelegate::calcPoint(QPainterPath& path,
586 const double dx,
587 const double y,
588 const double yBase,
589 const double yScale) const
590{
591 double x = path.currentPosition().x() + dx;
592 double yScaled = -((yScale * y) - yBase); //- because y pixels grow downwards.
593
594 // qDebug() << " x = " << x;
595 // qDebug() << " y = " << yScaled;
596
597 return QPointF(x, yScaled);
598}
599
600//=============================================================================================================
#define FIFFV_STIM_CH
Per-modality vertical-scale spinbox panel (one spin per channel type).
Real-time FIFF raw-data table browser backed by DISPLIB::RtFiffRawViewModel + DISPLIB::RtFiffRawViewD...
QAbstractItemDelegate painting one FIFF channel's signal trace inside DISPLIB::RtFiffRawView.
Circular-buffer QAbstractTableModel feeding the real-time FIFF raw browser.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
QPair< const double *, qint32 > RowVectorPair
void setUpperItemIndex(int iUpperItemIndex)
RtFiffRawViewDelegate(RtFiffRawView *parent=0)
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
void initPainterPaths(const QAbstractTableModel *model)
void setSignalColor(const QColor &signalColor)
void markerMoved(QPoint position, int activeRow)
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
Circular-buffer QAbstractTableModel feeding the real-time FIFF raw browser.
const QMap< qint32, float > & getScaling() const
FIFFLIB::fiff_int_t getKind(qint32 row) const
QList< QPair< int, double > > getDetectedTriggers() const
QMap< double, QColor > getTriggerColor() const
std::vector< int > getEventsToDisplay(int iBegin, int iEnd) const
QList< QPair< int, double > > getDetectedTriggersOld() const
double getMaxValueFromRawViewModel(int row) const
QTableView-based real-time FIFF raw-data browser host.