33#include <QPainterPath>
50: QAbstractItemDelegate(parent)
63 for (
int i = 0; i < model->rowCount(); i++)
64 m_painterPaths.append(QPainterPath());
67 QColor colorMarker(233, 0, 43);
68 colorMarker.setAlpha(160);
70 m_penMarker = QPen(colorMarker, 2, Qt::DashLine);
72 m_penGrid = QPen(Qt::black, 1, Qt::DashLine);
73 m_penTimeSpacers = QPen(Qt::black, 1, Qt::DashLine);
75 m_penFreeze = QPen(Qt::darkGray, 1, Qt::SolidLine);
76 m_penFreezeSelected = QPen(Qt::darkRed, 1, Qt::SolidLine);
78 m_penFreezeBad = QPen(Qt::darkGray, 0.1, Qt::SolidLine);
79 m_penFreezeSelectedBad = QPen(Qt::darkRed, 1, Qt::SolidLine);
81 m_penNormal = QPen(Qt::darkBlue, 1, Qt::SolidLine);
82 m_penNormalSelected = QPen(Qt::red, 1, Qt::SolidLine);
84 m_penNormalBad = QPen(Qt::darkBlue, 0.1, Qt::SolidLine);
85 m_penNormalSelectedBad = QPen(Qt::red, 1, Qt::SolidLine);
87 m_penCrosshair = QPen(QColor(255, 140, 0), 1, Qt::SolidLine);
93 const QStyleOptionViewItem& option,
94 const QModelIndex& index)
const
96 float t_fPlotHeight = option.rect.height();
97 painter->setRenderHint(QPainter::Antialiasing,
true);
99 switch (index.column()) {
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());
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();
116 if (index.row() == m_iUpperItemIndex) {
118 painter->setBrushOrigin(option.rect.topLeft());
119 QRect rect = option.rect;
120 rect.setHeight(2000);
121 painter->fillRect(rect, backgroundBrush);
128 QBrush brush(QColor(254, 74, 93, 40));
129 painter->setBrushOrigin(option.rect.topLeft());
130 painter->fillRect(option.rect, brush);
143 QVariant variant = index.model()->data(index, Qt::DisplayRole);
148 if (data.second > 0) {
149 QPainterPath path(QPointF(option.rect.x(), option.rect.y()));
160 createGridPath(index, option, path, data);
163 painter->setPen(m_penGrid);
164 painter->drawPath(path);
168 createTimeSpacersPath(index, option, path, data);
171 painter->setPen(m_penTimeSpacers);
172 painter->drawPath(path);
176 path = QPainterPath(QPointF(option.rect.x(), option.rect.y()));
178 createTriggerPath(painter, index, option, path, data);
182 if (index.row() == t_pModel->getCurrentTriggerIndex() &&
183 t_pModel->triggerDetectionActive()) {
184 path = QPainterPath(QPointF(option.rect.x(), option.rect.y()));
185 QPointF textPosition;
186 createTriggerThresholdPath(index, option, path, data, textPosition);
189 painter->setPen(QPen(Qt::red, 1, Qt::DashLine));
190 painter->drawPath(path);
191 painter->drawText(textPosition, QString(
"%1 Threshold").arg(t_pModel->getTriggerName()));
195 path = QPainterPath(QPointF(option.rect.x(), option.rect.y()));
198 createPlotPath(index, option, path, data);
200 painter->setRenderHint(QPainter::Antialiasing,
true);
202 painter->translate(0, t_fPlotHeight / 2);
205 if (t_pModel->isFreezed()) {
206 if (option.state & QStyle::State_Selected)
207 painter->setPen(m_penFreezeSelectedBad);
209 painter->setPen(m_penFreezeBad);
211 if (option.state & QStyle::State_Selected)
212 painter->setPen(m_penNormalSelectedBad);
214 painter->setPen(m_penNormalBad);
217 if (t_pModel->isFreezed()) {
218 if (option.state & QStyle::State_Selected)
219 painter->setPen(m_penFreezeSelected);
221 painter->setPen(m_penFreeze);
223 if (option.state & QStyle::State_Selected)
224 painter->setPen(m_penNormalSelected);
226 painter->setPen(m_penNormal);
230 painter->drawPath(path);
234 path = QPainterPath(QPointF(option.rect.x(), option.rect.y()));
235 createCurrentPositionMarkerPath(index, option, path);
238 painter->setPen(m_penMarker);
239 painter->drawPath(path);
242 path = QPainterPath(QPointF(option.rect.x(), option.rect.y()));
243 createMarkerPath(index, option, path);
246 painter->setPen(QPen(Qt::green, 1, Qt::SolidLine));
247 painter->drawPath(path);
250 paintCrosshair(painter, index, option, data);
260 const QModelIndex& index)
const
262 QSize size = option.rect.size();
264 switch (index.column()) {
266 size = QSize(20, option.rect.height());
269 QList<QVector<float>> data = index.model()->data(index).value<QList<QVector<float>>>();
284 m_markerPosition = position;
285 m_iActiveRow = activeRow;
290void RtFiffRawViewDelegate::paintCrosshair(QPainter* painter,
291 const QModelIndex& index,
292 const QStyleOptionViewItem& option,
295 if (m_iActiveRow < 0 || m_markerPosition.x() < option.rect.left() || m_markerPosition.x() > option.rect.right()) {
300 painter->setPen(m_penCrosshair);
301 painter->drawLine(QPointF(m_markerPosition.x(), option.rect.top()), QPointF(m_markerPosition.x(), option.rect.bottom()));
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) {
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);
320 m_penNormal.setColor(signalColor);
321 m_penNormalBad.setColor(signalColor);
328 return m_penNormal.color();
335 m_iUpperItemIndex = iUpperItemIndex;
340void RtFiffRawViewDelegate::createPlotPath(
const QModelIndex& index,
341 const QStyleOptionViewItem& option,
346 int iPlotSizePx = option.rect.width();
348 double dPixelsPerSample =
static_cast<double>(iPlotSizePx) /
static_cast<double>(iNumSamples);
352 double dScaleY = option.rect.height() / (2 * dMaxYValueEstimate);
353 double dChannelOffset = path.currentPosition().y();
367 path.moveTo(calcPoint(path, 0., 0., dChannelOffset, dScaleY));
377 for (
int j = 0; j < data.second; ++j) {
379 path.lineTo(calcPoint(path, dPixelsPerSample, dY,
380 dChannelOffset, dScaleY));
386void RtFiffRawViewDelegate::createCurrentPositionMarkerPath(
const QModelIndex& index,
const QStyleOptionViewItem& option, QPainterPath& path)
const
388 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
390 float dDx = ((float)option.rect.width()) / t_pModel->
getMaxSamples();
393 float yStart = option.rect.topLeft().y();
394 float yEnd = option.rect.bottomRight().y();
396 path.moveTo(currentSampleIndex, yStart);
397 path.lineTo(currentSampleIndex, yEnd);
402void RtFiffRawViewDelegate::createGridPath(
const QModelIndex& index,
const QStyleOptionViewItem& option, QPainterPath& path,
RowVectorPair& data)
const
406 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
410 float distance = float(option.rect.width()) / (t_pModel->
numVLines() + 1);
412 float yStart = option.rect.topLeft().y();
414 float yEnd = option.rect.bottomRight().y();
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);
426void RtFiffRawViewDelegate::createTimeSpacersPath(
const QModelIndex& index,
const QStyleOptionViewItem& option, QPainterPath& path,
RowVectorPair& data)
const
430 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
434 float distanceSec = float(option.rect.width()) / (t_pModel->
numVLines() + 1);
437 float yStart = option.rect.topLeft().y();
439 float yEnd = option.rect.bottomRight().y();
441 for (qint32 t = 0; t < t_pModel->
numVLines() + 1; ++t) {
443 float x = (distanceSec * t) + (distanceSpacers * (i + 1));
444 path.moveTo(x, yStart);
445 path.lineTo(x, yEnd);
453void RtFiffRawViewDelegate::createTriggerPath(QPainter* painter,
454 const QModelIndex& index,
455 const QStyleOptionViewItem& option,
462 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
466 QMap<double, QColor> mapTriggerTypeColors = t_pModel->
getTriggerColor();
468 float yStart = option.rect.topLeft().y();
469 float yEnd = option.rect.bottomRight().y();
470 float dDx = ((float)option.rect.width()) / t_pModel->
getMaxSamples();
475 for (
int u = 0; u < detectedTriggers.size(); ++u) {
476 QPainterPath markerPath;
478 int triggerPos = detectedTriggers[u].first;
481 if (mapTriggerTypeColors.contains(detectedTriggers[u].second)) {
482 painter->setPen(QPen(mapTriggerTypeColors[detectedTriggers[u].second], 1.5, Qt::SolidLine));
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);
490 painter->drawPath(markerPath);
495 for (
int u = 0; u < detectedTriggersOld.size(); ++u) {
496 QPainterPath markerPath;
498 int triggerPos = detectedTriggersOld[u].first;
503 if (mapTriggerTypeColors.contains(detectedTriggersOld[u].second)) {
504 painter->setPen(QPen(mapTriggerTypeColors[detectedTriggersOld[u].second], 1.5, Qt::SolidLine));
507 markerPath.moveTo(option.rect.x() +
static_cast<qreal
>(triggerPos) * dDx, yStart);
508 markerPath.lineTo(option.rect.x() +
static_cast<qreal
>(triggerPos) * dDx, yEnd);
510 painter->drawPath(markerPath);
518void RtFiffRawViewDelegate::createTriggerThresholdPath(
const QModelIndex& index,
519 const QStyleOptionViewItem& option,
522 QPointF& textPosition)
const
526 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
529 qint32 kind = t_pModel->
getKind(index.row());
530 double dMaxValue = 1e-9f;
541 double dScaleY = option.rect.height() / (2 * dMaxValue);
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);
547 textPosition = QPointF(option.rect.topLeft().x() + 5, option.rect.topLeft().y() + option.rect.height() / 2 + dScaleY * triggerThreshold - 5);
552void RtFiffRawViewDelegate::createMarkerPath(
const QModelIndex& index,
553 const QStyleOptionViewItem& option,
554 QPainterPath& path)
const
556 const RtFiffRawViewModel* t_pModel =
static_cast<const RtFiffRawViewModel*
>(index.model());
562 double dDx =
static_cast<double>(option.rect.width()) /
static_cast<double>(iMaxSample);
564 float yStart = option.rect.topLeft().y();
565 float yEnd = option.rect.bottomRight().y();
567 int iEarliestDrawnSample = iOffset - iMaxSample + iTimeCursorSample;
568 int iLatestDrawnSample = iOffset + iTimeCursorSample;
570 auto eventSamples = t_pModel->
getEventsToDisplay(iEarliestDrawnSample, iLatestDrawnSample);
572 for (
int iEventSample : eventSamples) {
573 int iLastStartingSample = iOffset - iMaxSample;
574 int iDrawPositionInSamples = (iEventSample - iLastStartingSample) % iMaxSample;
576 float iPositionInPixels = option.rect.x() +
static_cast<float>(iDrawPositionInSamples) * dDx;
578 path.moveTo(iPositionInPixels, yStart);
579 path.lineTo(iPositionInPixels, yEnd);
585QPointF RtFiffRawViewDelegate::calcPoint(QPainterPath& path,
589 const double yScale)
const
591 double x = path.currentPosition().x() + dx;
592 double yScaled = -((yScale * y) - yBase);
597 return QPointF(x, yScaled);
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.
double getTriggerThreshold() const
int getNumberOfTimeSpacers() const
const QMap< qint32, float > & getScaling() const
int getCurrentOverlapAddDelay() const
FIFFLIB::fiff_int_t getKind(qint32 row) const
qint32 getMaxSamples() const
QList< QPair< int, double > > getDetectedTriggers() const
int getFirstSampleOffset() const
QMap< double, QColor > getTriggerColor() const
std::vector< int > getEventsToDisplay(int iBegin, int iEnd) const
qint32 getCurrentSampleIndex() const
QList< QPair< int, double > > getDetectedTriggersOld() const
double getMaxValueFromRawViewModel(int row) const
QTableView-based real-time FIFF raw-data browser host.