MNE-CPP  0.1.9
A Framework for Electrophysiology
filterdesignview.cpp
Go to the documentation of this file.
1 //=============================================================================================================
37 //=============================================================================================================
38 // INCLUDES
39 //=============================================================================================================
40 
41 #include "filterdesignview.h"
42 #include "ui_filterdesignview.h"
43 
45 
46 #include "utils/mnemath.h"
47 
49 
50 #include <fiff/fiff_info.h>
51 
52 //=============================================================================================================
53 // QT INCLUDES
54 //=============================================================================================================
55 
56 #include <QDate>
57 #include <QFileDialog>
58 #include <QStandardPaths>
59 #include <QSvgGenerator>
60 #include <QCheckBox>
61 #include <QSettings>
62 #include <QApplication>
63 #include <QDesktopWidget>
64 #include <QKeyEvent>
65 
66 //=============================================================================================================
67 // EIGEN INCLUDES
68 //=============================================================================================================
69 
70 //=============================================================================================================
71 // USED NAMESPACES
72 //=============================================================================================================
73 
74 using namespace DISPLIB;
75 using namespace FIFFLIB;
76 using namespace UTILSLIB;
77 using namespace RTPROCESSINGLIB;
78 
79 //=============================================================================================================
80 // DEFINE MEMBER METHODS
81 //=============================================================================================================
82 
83 FilterDesignView::FilterDesignView(const QString& sSettingsPath,
84  QWidget *parent,
85  Qt::WindowFlags f)
86 : AbstractView(parent, f)
87 , m_pUi(new Ui::FilterDesignViewWidget)
88 , m_iFilterTaps(512)
89 , m_dSFreq(600)
90 {
91  m_sSettingsPath = sSettingsPath;
92  m_pUi->setupUi(this);
93 
94  initSpinBoxes();
95  initButtons();
98 
99  loadSettings();
101 }
102 
103 //=============================================================================================================
104 
106 {
107  saveSettings();
108 
109  delete m_pUi;
110 }
111 
112 //=============================================================================================================
113 
114 void FilterDesignView::setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
115 {
116  if(iMaxNumberFilterTaps%2 != 0) {
117  iMaxNumberFilterTaps--;
118  }
119 
120  m_pUi->m_spinBox_filterTaps->setMaximum(iMaxNumberFilterTaps);
121  m_pUi->m_spinBox_filterTaps->setMinimum(16);
122 
123  //Update filter depending on new window size
125 }
126 
127 //=============================================================================================================
128 
130 {
131  return m_pUi->m_spinBox_filterTaps->value();
132 }
133 
134 //=============================================================================================================
135 
136 void FilterDesignView::setSamplingRate(double dSamplingRate)
137 {
138  if(dSamplingRate <= 0) {
139  qWarning() << "[FilterDesignView::setSamplingRate] Sampling frequency is <= 0. Returning.";
140  }
141 
142  m_dSFreq = dSamplingRate;
143 
144  //Update min max of spin boxes to nyquist
145  double nyquistFrequency = m_dSFreq/2;
146 
147  m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
148  m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
149 
150  if(m_pUi->m_doubleSpinBox_to->value()>m_dSFreq/2) {
151  m_pUi->m_doubleSpinBox_to->setValue(m_dSFreq/2);
152  }
153 
154  if(m_pUi->m_doubleSpinBox_from->value()>m_dSFreq/2) {
155  m_pUi->m_doubleSpinBox_from->setValue(m_dSFreq/2);
156  }
157 
159 
161 }
162 
163 //=============================================================================================================
164 
165 void FilterDesignView::setFrom(double dFrom)
166 {
167  m_pUi->m_doubleSpinBox_from->setValue(dFrom);
169 }
170 
171 //=============================================================================================================
172 
173 void FilterDesignView::setTo(double dTo)
174 {
175  m_pUi->m_doubleSpinBox_to->setValue(dTo);
177 }
178 
179 //=============================================================================================================
180 
182 {
183  return m_filterKernel;
184 }
185 
186 //=============================================================================================================
187 
189 {
190  return m_pUi->m_comboBox_filterApplyTo->currentText();
191 }
192 
193 //=============================================================================================================
194 
195 void FilterDesignView::setChannelType(const QString& sType)
196 {
197  m_pUi->m_comboBox_filterApplyTo->setCurrentText(sType);
198  saveSettings();
199 }
200 
201 //=============================================================================================================
202 
204 {
205  if(m_sSettingsPath.isEmpty()) {
206  return;
207  }
208 
209  QSettings settings("MNECPP");
210 
211  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), m_filterKernel.getHighpassFreq());
212  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTo"), m_filterKernel.getLowpassFreq());
213  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), m_filterKernel.getFilterOrder());
214  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(m_filterKernel.getDesignMethod()));
215  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), m_filterKernel.getParksWidth()*(m_filterKernel.getSamplingFrequency()/2));
216  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), getChannelType());
217  settings.setValue(m_sSettingsPath + QString("/FilterDesignView/Position"), this->pos());
218 }
219 
220 //=============================================================================================================
221 
223 {
224  if(m_sSettingsPath.isEmpty()) {
225  return;
226  }
227 
228  QSettings settings("MNECPP");
229 
230  //Set stored filter settings from last session
231  m_pUi->m_doubleSpinBox_to->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTo"), 40.0).toDouble());
232  m_pUi->m_doubleSpinBox_from->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), 1.0).toDouble());
233  m_pUi->m_spinBox_filterTaps->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), 128).toInt());
234  m_pUi->m_comboBox_designMethod->setCurrentIndex(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(FilterParameter("Cosine"))).toInt());
235  m_pUi->m_doubleSpinBox_transitionband->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), 0.1).toDouble());
236  m_pUi->m_comboBox_filterApplyTo->setCurrentText(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), "All").toString());
237 
238  QPoint pos = settings.value(m_sSettingsPath + QString("/FilterDesignView/Position"), QPoint(100,100)).toPoint();
239 
240  QRect screenRect = QApplication::desktop()->screenGeometry();
241  if(!screenRect.contains(pos) && QGuiApplication::screens().size() == 1) {
242  move(QPoint(100,100));
243  } else {
244  move(pos);
245  }
246 }
247 
248 //=============================================================================================================
249 
251 {
252  switch(mode) {
253  case GuiMode::Clinical:
254  break;
255  default: // default is research mode
256  break;
257  }
258 }
259 
260 //=============================================================================================================
261 
262 void FilterDesignView::updateProcessingMode(ProcessingMode mode)
263 {
264  switch(mode) {
265  case ProcessingMode::Offline:
266  break;
267  default: // default is realtime mode
268  break;
269  }
270 }
271 
272 //=============================================================================================================
273 
275 {
276  connect(m_pUi->m_doubleSpinBox_from, &QDoubleSpinBox::editingFinished,
278 
279  connect(m_pUi->m_doubleSpinBox_to, &QDoubleSpinBox::editingFinished,
281 
282  connect(m_pUi->m_doubleSpinBox_transitionband, &QDoubleSpinBox::editingFinished,
284 
285  connect(m_pUi->m_spinBox_filterTaps, &QSpinBox::editingFinished,
287 
288  //Intercept events from the spin boxes to get control over key events
289  m_pUi->m_doubleSpinBox_from->installEventFilter(this);
290  m_pUi->m_doubleSpinBox_to->installEventFilter(this);
291  m_pUi->m_doubleSpinBox_transitionband->installEventFilter(this);
292 }
293 
294 //=============================================================================================================
295 
297 {
298  connect(m_pUi->m_pushButton_exportPlot,&QPushButton::released,
300 
301  connect(m_pUi->m_pushButton_exportFilter,&QPushButton::released,
303 
304  connect(m_pUi->m_pushButton_loadFilter,&QPushButton::released,
306 }
307 
308 //=============================================================================================================
309 
311 {
312  for(FilterParameter filterMethod : FilterKernel::m_designMethods){
313  m_pUi->m_comboBox_designMethod->addItem(filterMethod.getName());
314  }
315 
316  connect(m_pUi->m_comboBox_designMethod,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
318 
319  //Initial selection is a bandpass and Cosine design method
320  m_pUi->m_doubleSpinBox_from->setVisible(true);
321  m_pUi->m_label_lowpass->setVisible(true);
322 
323  m_pUi->m_doubleSpinBox_to->setVisible(true);
324  m_pUi->m_label_highpass->setVisible(true);
325 
326  m_pUi->m_spinBox_filterTaps->setVisible(true);
327  m_pUi->m_label_filterTaps->setVisible(true);
328 
329  connect(m_pUi->m_comboBox_filterApplyTo, &QComboBox::currentTextChanged,
331 }
332 
333 //=============================================================================================================
334 
336 {
337  m_pFilterPlotScene = new FilterPlotScene(m_pUi->m_graphicsView_filterPlot, this);
338 
339  m_pUi->m_graphicsView_filterPlot->setScene(m_pFilterPlotScene);
340  m_pUi->m_graphicsView_filterPlot->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
341  m_pUi->m_graphicsView_filterPlot->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
342 
344 }
345 
346 //=============================================================================================================
347 
348 void FilterDesignView::resizeEvent(QResizeEvent* event)
349 {
350  Q_UNUSED(event);
351  m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
352 }
353 
354 //=============================================================================================================
355 
356 void FilterDesignView::keyPressEvent(QKeyEvent * event)
357 {
358  if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
359  emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
360  }
361 
362  if((event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_Z) || event->key() == Qt::Key_Delete) {
363  emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
364  }
365 }
366 
367 //=============================================================================================================
368 
370 {
371  //Update the filter of the scene
372  m_pFilterPlotScene->updateFilter(m_filterKernel,
373  m_filterKernel.getSamplingFrequency(), //Pass the filters sampling frequency, not the one from the fiff info. Reason: sFreq from a loaded filter could be different
374  m_pUi->m_doubleSpinBox_from->value(),
375  m_pUi->m_doubleSpinBox_to->value());
376 
377  m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
378 }
379 
380 //=============================================================================================================
381 
383 {
384  Q_UNUSED(currentIndex);
385 
386  //Change visibility of filter tap spin boxes depending on filter design method
387  switch(m_pUi->m_comboBox_designMethod->currentIndex()) {
388  case 0: //Cosine
389 // m_pUi->m_spinBox_filterTaps->setVisible(false);
390 // m_pUi->m_label_filterTaps->setVisible(false);
391  m_pUi->m_spinBox_filterTaps->setVisible(true);
392  m_pUi->m_label_filterTaps->setVisible(true);
393  break;
394 
395  case 1: //Tschebyscheff
396  m_pUi->m_spinBox_filterTaps->setVisible(true);
397  m_pUi->m_label_filterTaps->setVisible(true);
398  break;
399  }
400 
402 }
403 
404 //=============================================================================================================
405 
407 {
408  emit updateFilterFrom(m_pUi->m_doubleSpinBox_from->value());
409  emit updateFilterTo(m_pUi->m_doubleSpinBox_to->value());
410 
411  //User defined filter parameters
412  double from = m_pUi->m_doubleSpinBox_from->value();
413  double to = m_pUi->m_doubleSpinBox_to->value();
414 
415  double trans_width = m_pUi->m_doubleSpinBox_transitionband->value();
416 
417  double bw = to-from;
418  double center = from+bw/2;
419 
420  double nyquistFrequency = m_dSFreq/2;
421 
422  //Calculate the needed fft length
423  m_iFilterTaps = m_pUi->m_spinBox_filterTaps->value();
424  if(m_pUi->m_spinBox_filterTaps->value()%2 != 0) {
425  m_iFilterTaps--;
426  }
427 
428  //set maximum and minimum for cut off frequency spin boxes
429  m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
430  m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
431  m_pUi->m_doubleSpinBox_to->setMinimum(0);
432  m_pUi->m_doubleSpinBox_from->setMinimum(0);
433 
434  if((m_pUi->m_doubleSpinBox_to->value() < m_pUi->m_doubleSpinBox_from->value())) {
435  m_pUi->m_doubleSpinBox_to->setValue(m_pUi->m_doubleSpinBox_from->value() + 1);
436  }
437 
438  m_pUi->m_doubleSpinBox_to->setMinimum(m_pUi->m_doubleSpinBox_from->value());
439  m_pUi->m_doubleSpinBox_from->setMaximum(m_pUi->m_doubleSpinBox_to->value());
440 
441  int iMethod = FilterKernel::m_designMethods.indexOf(FilterParameter(m_pUi->m_comboBox_designMethod->currentText()));
442 
443  //Generate filters
444  m_filterKernel = FilterKernel("Designed Filter",
445  FilterKernel::m_filterTypes.indexOf(FilterParameter("BPF")),
447  (double)center/nyquistFrequency,
448  (double)bw/nyquistFrequency,
449  (double)trans_width/nyquistFrequency,
450  m_dSFreq,
451  iMethod);
452 
454 
455  //update filter plot
457 
458  saveSettings();
459 }
460 
461 //=============================================================================================================
462 
463 void FilterDesignView::onSpinBoxFilterChannelType(const QString& channelType)
464 {
465  emit filterChannelTypeChanged(channelType);
466 }
467 
468 //=============================================================================================================
469 
471 {
472  // Open file dialog
473  QDate date;
474  QString fileName = QFileDialog::getSaveFileName(this,
475  "Save filter plot",
476  QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
477  tr("Vector graphic(*.svg);;Images (*.png)"));
478 
479  if(!fileName.isEmpty()) {
480  // Generate screenshot
481  if(fileName.contains(".svg")) {
482  QSvgGenerator svgGen;
483 
484  svgGen.setFileName(fileName);
485  QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
486  svgGen.setSize(QSize(rect.width(), rect.height()));
487  //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
488 
489  QPainter painter(&svgGen);
490  m_pFilterPlotScene->render(&painter);
491  }
492 
493  if(fileName.contains(".png")) {
494  m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
495  QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
496  image.fill(Qt::transparent); // Start all pixels transparent
497 
498  QPainter painter(&image);
499  m_pFilterPlotScene->render(&painter);
500  image.save(fileName);
501  }
502  }
503 }
504 
505 //=============================================================================================================
506 
508 {
509  //Generate appropriate name for the filter to be saved
510  QString filtername;
511 
512  filtername = QString("%1_%2_%3_Fs%4").arg(m_filterKernel.getFilterType().getName()).arg((int)m_filterKernel.getHighpassFreq()).arg((int)m_filterKernel.getLowpassFreq()).arg((int)m_filterKernel.getSamplingFrequency());
513 
514  //Do not pass m_filterKernel because this is most likely the User Defined filter which name should not change due to the filter model implementation. Hence use temporal copy of m_filterKernel.
515  FilterKernel filterWriteTemp = m_filterKernel;
516  filterWriteTemp.getName() = filtername;
517 
518  QString fileName = QFileDialog::getSaveFileName(this,
519  "Save filter coefficients",
520  QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(filtername),
521  tr("Text file(*.txt)"));
522 
523  FilterIO::writeFilter(fileName, filterWriteTemp);
524 }
525 
526 //=============================================================================================================
527 
529 {
530  QString path = QFileDialog::getOpenFileName(this,
531  QString("Load filter"),
532  QString("./"),
533  tr("txt files (*.txt)"));
534 
535  if(!path.isEmpty()) {
536  //Replace old with new filter operator
537  FilterKernel filterLoadTemp;
538 
539  if(!FilterIO::readFilter(path, filterLoadTemp)) {
540  return;
541  }
542  updateGuiFromFilter(filterLoadTemp);
543 
545 
547 
548  } else {
549  qDebug()<<"Could not load filter.";
550  }
551 }
552 
553 //=============================================================================================================
554 
556 {
557 
558 }
559 
560 //=============================================================================================================
561 
563 {
564  return m_pUi->m_doubleSpinBox_from->value();
565 }
566 
567 //=============================================================================================================
568 
570 {
571  return m_pUi->m_doubleSpinBox_to->value();
572 }
573 
574 //=============================================================================================================
575 
577 {
578  m_pUi->m_doubleSpinBox_from->setValue(filter.getHighpassFreq());
579  m_pUi->m_doubleSpinBox_to->setValue(filter.getLowpassFreq());
580  m_pUi->m_spinBox_filterTaps->setValue(filter.getFilterOrder());
581  m_pUi->m_doubleSpinBox_transitionband->setValue(filter.getParksWidth()*(filter.getSamplingFrequency()/2));
582 
583  m_pUi->m_comboBox_designMethod->setCurrentIndex(FilterKernel::m_designMethods.indexOf(filter.getDesignMethod()));
584 }
585 
586 //=============================================================================================================
587 
588 void FilterDesignView::guiStyleChanged(DISPLIB::AbstractView::StyleMode style)
589 {
590  Q_UNUSED(style)
592 }
FilterDesignView(const QString &sSettingsPath, QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
void guiStyleChanged(DISPLIB::AbstractView::StyleMode style)
RTPROCESSINGLIB::FilterKernel getCurrentFilter()
Contains the declaration of the FilterPlotScene class.
void filterChannelTypeChanged(const QString &channelType)
The FilterKernel class provides methods to create/design a FIR filter kernel.
Definition: filterkernel.h:132
QPointer< FilterPlotScene > m_pFilterPlotScene
void updateGuiMode(GuiMode mode)
void setChannelType(const QString &sType)
Definition: applytoview.h:58
Ui::FilterDesignViewWidget * m_pUi
void setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
void resizeEvent(QResizeEvent *event)
virtual void keyPressEvent(QKeyEvent *event)
MNEMath class declaration.
Contains the declaration of the FilterDesignView class.
void updateFilterTo(double dTo)
void onSpinBoxFilterChannelType(const QString &channelType)
The FilterParameter class.
Definition: filterkernel.h:83
void setSamplingRate(double dSamplingRate)
void changeStateSpinBoxes(int currentIndex)
void updateGuiFromFilter(const RTPROCESSINGLIB::FilterKernel &filter)
void updateFilterFrom(double dFrom)
void updateProcessingMode(ProcessingMode mode)
RTPROCESSINGLIB::FilterKernel m_filterKernel
FiffInfo class declaration.
FilterIO class declaration.
The AbstractView class provides the base calss for all Disp viewers.
Definition: abstractview.h:75
The FilterPlotScene class provides the scene where a filter response can be plotted.
void filterChanged(const RTPROCESSINGLIB::FilterKernel &activeFilter)