v2.0.0
Loading...
Searching...
No Matches
butterflyview.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "butterflyview.h"
42
43#include "scalingview.h"
44
47
48//=============================================================================================================
49// QT INCLUDES
50//=============================================================================================================
51
52#include <QPainter>
53#include <QPainterPath>
54#include <QDebug>
55#include <QSvgGenerator>
56#include <QSurfaceFormat>
57#include <QSettings>
58
59//=============================================================================================================
60// USED NAMESPACES
61//=============================================================================================================
62
63using namespace DISPLIB;
64
65//=============================================================================================================
66// DEFINE MEMBER METHODS
67//=============================================================================================================
68
69ButterflyView::ButterflyView(const QString& sSettingsPath,
70 QWidget *parent,
71 Qt::WindowFlags f)
72:
73 QRhiWidget(parent)
74, m_sSettingsPath(sSettingsPath)
75, m_pEvokedSetModel(nullptr)
76, m_bIsInit(false)
77, m_bShowMAG(true)
78, m_bShowGRAD(true)
79, m_bShowEEG(true)
80, m_bShowEOG(true)
81, m_bShowMISC(true)
83, m_qMapAverageActivation(QSharedPointer<QMap<QString, bool> >::create())
84, m_qMapAverageColor(QSharedPointer<QMap<QString, QColor> >::create())
85{
86 m_sSettingsPath = sSettingsPath;
87
88#if defined(WASMBUILD) || defined(__EMSCRIPTEN__)
89 setApi(QRhiWidget::Api::OpenGL);
90#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
91 setApi(QRhiWidget::Api::Metal);
92#elif defined(Q_OS_WIN)
93 setApi(QRhiWidget::Api::Direct3D11);
94#else
95 setApi(QRhiWidget::Api::OpenGL);
96#endif
97 setSampleCount(1);
98 setAttribute(Qt::WA_NativeWindow);
99
100 loadSettings();
101}
102
103//=============================================================================================================
104
109
110//=============================================================================================================
111
113{
114 // No-op: ButterflyView now derives from QRhiWidget directly.
115}
116
117//=============================================================================================================
118
119void ButterflyView::setEvokedSetModel(QSharedPointer<EvokedSetModel> model)
120{
121 m_pEvokedSetModel = model;
122
123 connect(m_pEvokedSetModel.data(), &EvokedSetModel::dataChanged,
124 this, &ButterflyView::dataUpdate, Qt::UniqueConnection);
125}
126
127//=============================================================================================================
128
130{
131 if(!m_bIsInit && m_pEvokedSetModel->isInit()) {
132 m_bIsInit = true;
133 }
134
136
137 update();
138}
139
140//=============================================================================================================
141
142QMap<QString, bool> ButterflyView::getModalityMap()
143{
144 return m_modalityMap;
145}
146
147//=============================================================================================================
148
149void ButterflyView::setModalityMap(const QMap<QString, bool> &modalityMap)
150{
151 m_modalityMap = modalityMap;
152 update();
153}
154
155//=============================================================================================================
156
157void ButterflyView::setScaleMap(const QMap<qint32,float> &scaleMap)
158{
159 m_scaleMap = scaleMap;
160 update();
161}
162
163//=============================================================================================================
164
165void ButterflyView::setSelectedChannels(const QList<int> &selectedChannels)
166{
167 m_lSelectedChannels = selectedChannels;
168 update();
169}
170
171//=============================================================================================================
172
174{
175 update();
176}
177
178//=============================================================================================================
179
180void ButterflyView::setBackgroundColor(const QColor& backgroundColor)
181{
182 m_colCurrentBackgroundColor = backgroundColor;
183 update();
184}
185
186//=============================================================================================================
187
192
193//=============================================================================================================
194
195void ButterflyView::takeScreenshot(const QString& fileName)
196{
197 if(fileName.contains(".svg", Qt::CaseInsensitive)) {
198 // Generate screenshot
199 QSvgGenerator svgGen;
200 svgGen.setFileName(fileName);
201 svgGen.setSize(this->size());
202 svgGen.setViewBox(this->rect());
203
204 QWidget::render(&svgGen);
205 }
206
207 if(fileName.contains(".png", Qt::CaseInsensitive)) {
208 QImage image(this->size(), QImage::Format_ARGB32);
209 image.fill(Qt::transparent);
210
211 QPainter painter(&image);
212 QWidget::render(&painter);
213 image.save(fileName);
214 }
215}
216
217//=============================================================================================================
218
219QSharedPointer<QMap<QString, QColor> > ButterflyView::getAverageColor() const
220{
221 return m_qMapAverageColor;
222}
223
224//=============================================================================================================
225
226QSharedPointer<QMap<QString, bool> > ButterflyView::getAverageActivation() const
227{
229}
230
231//=============================================================================================================
232
233void ButterflyView::setAverageColor(const QSharedPointer<QMap<QString, QColor> > qMapAverageColor)
234{
235 m_qMapAverageColor = qMapAverageColor;
236 update();
237}
238
239//=============================================================================================================
240
241void ButterflyView::setSingleAverageColor(const QColor& avgColor)
242{
243 for (QString mapKey : m_qMapAverageColor->keys())
244 m_qMapAverageColor->insert(mapKey, avgColor);
245}
246
247//=============================================================================================================
248
249void ButterflyView::setAverageActivation(const QSharedPointer<QMap<QString, bool> > qMapAverageActivation)
250{
251 m_qMapAverageActivation = qMapAverageActivation;
252 update();
253}
254
255//=============================================================================================================
256
257void ButterflyView::setChannelInfoModel(QSharedPointer<ChannelInfoModel> &pChannelInfoModel)
258{
259 m_pChannelInfoModel = pChannelInfoModel;
260}
261
262//=============================================================================================================
263
264void ButterflyView::showSelectedChannelsOnly(const QStringList& selectedChannels)
265{
267 qDebug() << "ButterflyView::showSelectedChannelsOnly - m_pChannelInfoModel is NULL. Returning. ";
268 return;
269 }
270
271 QList<int> selectedChannelsIndexes;
272
273 for(int i = 0; i<selectedChannels.size(); i++)
274 selectedChannelsIndexes<<m_pChannelInfoModel->getIndexFromOrigChName(selectedChannels.at(i));
275
276 setSelectedChannels(selectedChannelsIndexes);
277}
278
279//=============================================================================================================
280
281void ButterflyView::showSelectedChannels(const QList<int> selectedChannelsIndexes)
282{
283 setSelectedChannels(selectedChannelsIndexes);
284}
285
286//=============================================================================================================
287
289{
290 if (m_pEvokedSetModel) {
291 QList<int> lAllChannels;
292 for(int i = 0; i < m_pEvokedSetModel->rowCount(); i++) {
293 lAllChannels.append(i);
294 }
295 setSelectedChannels(lAllChannels);
296 }
297}
298
299//=============================================================================================================
300
302{
303 if(m_sSettingsPath.isEmpty()) {
304 return;
305 }
306
307 // Save Settings
308 QSettings settings("MNECPP");
309}
310
311//=============================================================================================================
312
314{
315 if(m_sSettingsPath.isEmpty()) {
316 return;
317 }
318
319 // Load Settings
320 QSettings settings("MNECPP");
321}
322
323//=============================================================================================================
324
325void ButterflyView::paintEvent(QPaintEvent *event)
326{
327 QPainter painter(this);
328
329 painter.save();
330 painter.setBrush(QBrush(m_colCurrentBackgroundColor));
331 painter.drawRect(QRect(-1,-1,this->width()+2,this->height()+2));
332 painter.restore();
333
334 painter.setRenderHint(QPainter::Antialiasing, true);
335
337 {
338 //Draw baseline correction area
339 if(m_pEvokedSetModel->getBaselineInfo().first.toString() != "None" &&
340 m_pEvokedSetModel->getBaselineInfo().second.toString() != "None") {
341 float from = m_pEvokedSetModel->getBaselineInfo().first.toFloat();
342 float to = m_pEvokedSetModel->getBaselineInfo().second.toFloat();
343
344 painter.save();
345 painter.setPen(QPen(Qt::red, 1, Qt::DashLine));
346 painter.setBrush(Qt::red);
347 painter.setOpacity(0.1);
348
349 if(m_pEvokedSetModel->getNumSamples() == 0){
350 qDebug() << "Unable to get data. Returning early.";
351 return;
352 }
353
354 float fDx = (float)(this->width()) / ((float)m_pEvokedSetModel->getNumSamples());
355
356 float fromSamp = ((from)*m_pEvokedSetModel->getSamplingFrequency())+m_pEvokedSetModel->getNumPreStimSamples();
357 float posX = fDx*(fromSamp);
358 float toSamp = ((to)*m_pEvokedSetModel->getSamplingFrequency())+m_pEvokedSetModel->getNumPreStimSamples();
359 float width = fDx*(toSamp-fromSamp);
360
361 QRect rect(posX,0,width,this->height());
362
363 painter.drawRect(rect);
364
365 painter.restore();
366 }
367
368 //Stimulus bar
369 if(m_pEvokedSetModel->getNumSamples() > 0) {
370 painter.save();
371 painter.setPen(QPen(Qt::red, 1, Qt::DashLine));
372
373 float fDx = (float)(this->width()) / ((float)m_pEvokedSetModel->getNumSamples());
374 float posX = fDx * ((float)m_pEvokedSetModel->getNumPreStimSamples());
375 painter.drawLine(posX, 1, posX, this->height());
376
377 painter.drawText(QPointF(posX+5,this->rect().bottomRight().y()-5), QString("0ms / Stimulus"));
378
379 painter.restore();
380 }
381
382 //Vertical time spacers
383 if(m_pEvokedSetModel->getNumberOfTimeSpacers() > 0)
384 {
385 painter.save();
386 QColor colorTimeSpacer = Qt::black;
387 colorTimeSpacer.setAlphaF(0.5);
388 painter.setPen(QPen(colorTimeSpacer, 1, Qt::DashLine));
389
390 float yStart = this->rect().topLeft().y();
391 float yEnd = this->rect().bottomRight().y();
392
393 float fDx = 1;
394 if(m_pEvokedSetModel->getNumSamples() != 0) {
395 fDx = (float)(this->width()) / ((float)m_pEvokedSetModel->getNumSamples());
396 }
397
398 float sampleCounter = m_pEvokedSetModel->getNumPreStimSamples();
399 int counter = 1;
400 float timeDistanceMSec = 50.0;
401 float timeDistanceSamples = (timeDistanceMSec/1000.0)*m_pEvokedSetModel->getSamplingFrequency(); //time distance corresponding to sampling frequency
402
403 //spacers before stim
404 while(sampleCounter-timeDistanceSamples>0) {
405 sampleCounter-=timeDistanceSamples;
406 float x = fDx*sampleCounter;
407 painter.drawLine(x, yStart, x, yEnd);
408 painter.drawText(QPointF(x+5,yEnd-5), QString("-%1ms").arg(timeDistanceMSec*counter));
409 counter++;
410 }
411
412 //spacers after stim
413 counter = 1;
414 sampleCounter = m_pEvokedSetModel->getNumPreStimSamples();
415 while(sampleCounter+timeDistanceSamples<m_pEvokedSetModel->getNumSamples()) {
416 sampleCounter+=timeDistanceSamples;
417 float x = fDx*sampleCounter;
418 painter.drawLine(x, yStart, x, yEnd);
419 painter.drawText(QPointF(x+5,yEnd-5), QString("%1ms").arg(timeDistanceMSec*counter));
420 counter++;
421 }
422
423 painter.restore();
424 }
425
426 //Zero line
427 if(m_pEvokedSetModel->getNumSamples() > 0) {
428 painter.save();
429 painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
430
431 painter.drawLine(0, this->height()/2, this->width(), this->height()/2);
432
433 painter.restore();
434 }
435
436 painter.translate(0,this->height()/2);
437
438 //Actual average data
439 for(qint32 r = 0; r < m_pEvokedSetModel->rowCount(); ++r) {
440 if(m_lSelectedChannels.contains(r)) {
441 qint32 kind = m_pEvokedSetModel->getKind(r);
442
443 //Display only selected kinds
444 switch(kind) {
445 case FIFFV_MEG_CH: {
446 qint32 unit = m_pEvokedSetModel->getUnit(r);
447 if(unit == FIFF_UNIT_T_M) {
448 if(m_modalityMap["GRAD"])
449 break;
450 else
451 continue;
452 }
453 else if(unit == FIFF_UNIT_T)
454 {
455 if(m_modalityMap["MAG"])
456 break;
457 else
458 continue;
459 }
460 continue;
461 }
462 case FIFFV_EEG_CH: {
463 if(m_modalityMap["EEG"])
464 break;
465 else
466 continue;
467 }
468 case FIFFV_EOG_CH: {
469 if(m_modalityMap["EOG"])
470 break;
471 else
472 continue;
473 }
474 case FIFFV_MISC_CH: {
475 if(m_modalityMap["MISC"])
476 break;
477 else
478 continue;
479 }
480 default:
481 continue;
482 }
483
484 painter.save();
485
486 createPlotPath(r, painter);
487
488 painter.restore();
489 }
490 }
491 }
492
493 return QRhiWidget::paintEvent(event);
494}
495
496//=============================================================================================================
497
498void ButterflyView::createPlotPath(qint32 row, QPainter& painter) const
499{
500 //get maximum range of respective channel type (range value in FiffChInfo does not seem to contain a reasonable value)
501 qint32 kind = m_pEvokedSetModel->getKind(row);
502 float fMaxValue = DISPLIB::getScalingValue(m_scaleMap, kind, m_pEvokedSetModel->getUnit(row));
503 bool bIsBad = m_pEvokedSetModel->getIsChannelBad(row);
504
505 if(bIsBad) {
506 painter.setOpacity(0.20);
507 } else {
508 painter.setOpacity(0.75);
509 }
510
511 float fValue;
512 float fScaleY = this->height()/(2*fMaxValue);
513
514 //restrictions for paint performance
515 float fWinMaxVal = ((float)this->height()-2)/2.0f;
516// qint32 iDownSampling = (m_pEvokedSetModel->getNumSamples() * 4 / (this->width()-2));
517// if(iDownSampling < 1) {
518// iDownSampling = 1;
519// }
520
521 QPointF qSamplePosition;
522
523 float fDx = (float)(this->width()-2) / ((float)m_pEvokedSetModel->getNumSamples()-1.0f);//((float)option.rect.width()) / m_pEvokedSetModel->getMaxSamples();
524
525 QList<DISPLIB::AvrTypeRowVector> rowVec = m_pEvokedSetModel->data(row,1).value<QList<DISPLIB::AvrTypeRowVector> >();
526
527 //Do for all average types
528 for(int j = 0; j < rowVec.size(); ++j) {
529 QString sAvrComment = rowVec.at(j).first;
530
531 // Select color for each average
532 if(m_pEvokedSetModel->isFreezed()) {
533 QColor freezeColor = m_qMapAverageColor->value(sAvrComment);
534 freezeColor.setAlphaF(0.5);
535 painter.setPen(QPen(freezeColor, 1));
536 } else {
537 painter.setPen(QPen(m_qMapAverageColor->value(sAvrComment)));
538 }
539
540 if(m_qMapAverageActivation->value(sAvrComment)) {
541 //Calculate downsampling factor of averaged data in respect to the items width
542 int dsFactor;
543 rowVec.at(j).second.cols() / this->width() < 1 ? dsFactor = 1 : dsFactor = rowVec.at(j).second.cols() / this->width();
544 if(dsFactor == 0) {
545 dsFactor = 1;
546 }
547
548 QPainterPath path(QPointF(1,0));
549 float y_base = path.currentPosition().y();
550
551 //Move to initial starting point
552 if(rowVec.at(j).second.cols() > 0)
553 {
554 float val = rowVec.at(j).second[0];
555 fValue = (val/*-rowVec.at(j)[m_pEvokedSetModel->getNumPreStimSamples()-1]*/)*fScaleY;//ToDo -> -2 PreStim is one too short
556
557 float newY = y_base+fValue;
558
559 qSamplePosition.setY(-newY);
560 qSamplePosition.setX(path.currentPosition().x());
561
562 path.moveTo(qSamplePosition);
563 }
564
565 //create lines from one to the next sample
566 qint32 i;
567 for(i = 1; i < rowVec.at(j).second.cols() && path.elementCount() <= this->width(); i += dsFactor) {
568 float val = /*rowVec.at(j)[m_pEvokedSetModel->getNumPreStimSamples()-1] - */rowVec.at(j).second[i]; //remove first sample data[0] as offset
569 fValue = val*fScaleY;
570
571 //Cut plotting if out of widget area
572 fValue = fValue > fWinMaxVal ? fWinMaxVal : fValue < -fWinMaxVal ? -fWinMaxVal : fValue;
573
574 float newY = y_base+fValue;
575
576 qSamplePosition.setY(-newY);
577
578 qSamplePosition.setX(path.currentPosition().x()+fDx);
579
580 path.lineTo(qSamplePosition);
581 }
582
583 // //create lines from one to the next sample for last path
584 // qint32 sample_offset = m_pEvokedSetModel->numVLines() + 1;
585 // qSamplePosition.setX(qSamplePosition.x() + fDx*sample_offset);
586 // lastPath.moveTo(qSamplePosition);
587
588 // for(i += sample_offset; i < lastData.size(); ++i) {
589 // float val = lastData[i] - lastData[0]; //remove first sample lastData[0] as offset
590 // fValue = val*fScaleY;
591
592 // float newY = y_base+fValue;
593
594 // qSamplePosition.setY(newY);
595 // qSamplePosition.setX(lastPath.currentPosition().x()+fDx);
596
597 // lastPath.lineTo(qSamplePosition);
598 // }
599
600 painter.drawPath(path);
601 }
602 }
603}
604
605//=============================================================================================================
606
607QSharedPointer<EvokedSetModel> ButterflyView::getEvokedSetModel()
608{
609 return m_pEvokedSetModel;
610}
611
612//=============================================================================================================
613
615{
616 setEvokedSetModel(Q_NULLPTR);
617}
Declaration of the ButterflyView class.
Declaration of the EvokedSetModel Class.
The declaration for ChannelInfoModel..
Declaration of the ScalingView Class.
#define FIFFV_EOG_CH
#define FIFFV_EEG_CH
#define FIFFV_MISC_CH
#define FIFFV_MEG_CH
#define FIFF_UNIT_T
#define FIFF_UNIT_T_M
2-D display widgets and visualisation helpers (charts, topography, colour maps).
float getScalingValue(const QMap< qint32, float > &qMapChScaling, int iChannelKind, int iChannelUnit)
QSharedPointer< QMap< QString, bool > > m_qMapAverageActivation
virtual void paintEvent(QPaintEvent *event)
QSharedPointer< EvokedSetModel > m_pEvokedSetModel
QList< int > m_lSelectedChannels
void createPlotPath(qint32 row, QPainter &painter) const
void setChannelInfoModel(QSharedPointer< ChannelInfoModel > &pChannelInfoModel)
QSharedPointer< QMap< QString, QColor > > getAverageColor() const
void setModalityMap(const QMap< QString, bool > &modalityMap)
void takeScreenshot(const QString &fileName)
QMap< QString, bool > getModalityMap()
const QColor & getBackgroundColor()
void setAverageColor(const QSharedPointer< QMap< QString, QColor > > qMapAverageColor)
void showSelectedChannels(const QList< int > selectedChannelsIndexes)
void setSelectedChannels(const QList< int > &selectedChannels)
QSharedPointer< ChannelInfoModel > m_pChannelInfoModel
QSharedPointer< QMap< QString, QColor > > m_qMapAverageColor
QSharedPointer< QMap< QString, bool > > getAverageActivation() const
void setEvokedSetModel(QSharedPointer< EvokedSetModel > model)
void setSingleAverageColor(const QColor &avgColor)
void setBackgroundColor(const QColor &backgroundColor)
void showSelectedChannelsOnly(const QStringList &selectedChannels)
void setScaleMap(const QMap< qint32, float > &scaleMap)
QMap< qint32, float > m_scaleMap
ButterflyView(const QString &sSettingsPath="", QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
QSharedPointer< EvokedSetModel > getEvokedSetModel()
QMap< QString, bool > m_modalityMap
void setAverageActivation(const QSharedPointer< QMap< QString, bool > > qMapAverageActivation)