MNE-CPP  beta 1.0
filterwindow.cpp
Go to the documentation of this file.
1 //=============================================================================================================
37 //*************************************************************************************************************
38 //=============================================================================================================
39 // INCLUDES
40 //=============================================================================================================
41 
42 #include "filterwindow.h"
43 
44 
45 //*************************************************************************************************************
46 //=============================================================================================================
47 // USED NAMESPACES
48 //=============================================================================================================
49 
50 using namespace MNEBrowseRawQt;
51 
52 
53 //*************************************************************************************************************
54 //=============================================================================================================
55 // DEFINE MEMBER METHODS
56 //=============================================================================================================
57 
58 FilterWindow::FilterWindow(MainWindow *mainWindow, QWidget *parent)
59 : QDockWidget(parent)
60 , ui(new Ui::FilterWindowDockWidget)
61 , m_pFilterPlotScene(new FilterPlotScene)
62 , m_pMainWindow(mainWindow)
63 {
64  ui->setupUi(this);
65 
66  initSpinBoxes();
67  initButtons();
68  initComboBoxes();
69  initFilterPlot();
70  initTableViews();
71 
72  m_iWindowSize = MODEL_WINDOW_SIZE;
73  m_iFilterTaps = MODEL_NUM_FILTER_TAPS;
74 }
75 
76 
77 //*************************************************************************************************************
78 
80 {
81  delete ui;
82 }
83 
84 
85 //*************************************************************************************************************
86 
88 {
90 
91  //Update min max of spin boxes to nyquist
92  double samplingFrequency = m_pMainWindow->m_pDataWindow->getDataModel()->m_fiffInfo.sfreq;
93  double nyquistFrequency = samplingFrequency/2;
94 
95  ui->m_doubleSpinBox_highpass->setMaximum(nyquistFrequency);
96  ui->m_doubleSpinBox_lowpass->setMaximum(nyquistFrequency);
97 }
98 
99 
100 //*************************************************************************************************************
101 
102 void FilterWindow::initSpinBoxes()
103 {
104  connect(ui->m_doubleSpinBox_lowpass,static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
106 
107  connect(ui->m_doubleSpinBox_highpass,static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
109 
110  connect(ui->m_doubleSpinBox_transitionband,static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
112 
113  connect(ui->m_spinBox_filterTaps,static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
115 
116  //Intercept events from the spin boxes to get control over key events
117  ui->m_doubleSpinBox_lowpass->installEventFilter(this);
118  ui->m_doubleSpinBox_highpass->installEventFilter(this);
119  ui->m_doubleSpinBox_transitionband->installEventFilter(this);
120 }
121 
122 
123 //*************************************************************************************************************
124 
125 void FilterWindow::initButtons()
126 {
127  connect(ui->m_pushButton_applyFilter,&QPushButton::released,
129 
130  connect(ui->m_pushButton_undoFiltering,&QPushButton::released,
132 
133  connect(ui->m_pushButton_exportPlot,&QPushButton::released,
135 
136  connect(ui->m_pushButton_exportFilter,&QPushButton::released,
138 }
139 
140 
141 //*************************************************************************************************************
142 
143 void FilterWindow::initComboBoxes()
144 {
145  connect(ui->m_comboBox_designMethod,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
147 
148  connect(ui->m_comboBox_filterType,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
150 
151  //Initial selection is a lowpass and Cosine design method
152  ui->m_doubleSpinBox_lowpass->setVisible(true);
153  ui->m_label_lowpass->setVisible(true);
154  ui->m_label_lowpass->setText("Cut-Off (Hz):");
155 
156  ui->m_doubleSpinBox_highpass->setVisible(false);
157  ui->m_label_highpass->setVisible(false);
158  ui->m_doubleSpinBox_highpass->setEnabled(false);
159 
160  ui->m_spinBox_filterTaps->setVisible(false);
161  ui->m_label_filterTaps->setVisible(false);
162 
163  //If add filter to channel type combo box changes -> also change combo box for undo filtering
164  connect(ui->m_comboBox_filterApplyTo, &QComboBox::currentTextChanged,
165  ui->m_comboBox_filterUndoTo, &QComboBox::setCurrentText);
166 }
167 
168 
169 //*************************************************************************************************************
170 
171 void FilterWindow::initFilterPlot()
172 {
173  ui->m_graphicsView_filterPlot->setScene(m_pFilterPlotScene);
174 }
175 
176 
177 //*************************************************************************************************************
178 
179 void FilterWindow::initTableViews()
180 {
181  ui->m_tableView_activeFilters->setModel(m_pMainWindow->m_pChInfoWindow->getDataModel());
182 
183  //Hide columns
184  ui->m_tableView_activeFilters->hideColumn(0);
185  ui->m_tableView_activeFilters->hideColumn(2);
186  ui->m_tableView_activeFilters->hideColumn(3);
187  ui->m_tableView_activeFilters->hideColumn(4);
188  ui->m_tableView_activeFilters->hideColumn(5);
189  ui->m_tableView_activeFilters->hideColumn(6);
190  ui->m_tableView_activeFilters->hideColumn(7);
191  ui->m_tableView_activeFilters->hideColumn(8);
192 
193  ui->m_tableView_activeFilters->verticalHeader()->hide();
194 
195  ui->m_tableView_activeFilters->resizeColumnsToContents();
196  ui->m_groupBox_activeFilters->adjustSize();
197  ui->m_groupBox_activeFilters->adjustSize();
198 }
199 
200 
201 //*************************************************************************************************************
202 
203 void FilterWindow::updateFilterPlot()
204 {
205  //Update the filter of the scene
206  QMutableMapIterator<QString,QSharedPointer<MNEOperator> > it(m_pMainWindow->m_pDataWindow->getDataModel()->m_Operators);
207  while(it.hasNext()) {
208  it.next();
209  if(it.key() == "User defined (See 'Adjust/Filter')") {
210  m_pFilterPlotScene->updateFilter(it.value(),
211  m_pMainWindow->m_pDataWindow->getDataModel()->m_fiffInfo.sfreq,
212  ui->m_doubleSpinBox_lowpass->value(),
213  ui->m_doubleSpinBox_highpass->value());
214  }
215  }
216 
217  ui->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
218 }
219 
220 
221 //*************************************************************************************************************
222 
223 void FilterWindow::resizeEvent(QResizeEvent* event)
224 {
225  Q_UNUSED(event);
226  ui->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
227 }
228 
229 
230 //*************************************************************************************************************
231 
232 void FilterWindow::keyPressEvent(QKeyEvent * event)
233 {
234  if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
235  applyFilter();
236 
237  if((event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_Z) || event->key() == Qt::Key_Delete)
238  undoFilter();
239 }
240 
241 
242 //*************************************************************************************************************
243 
244 bool FilterWindow::eventFilter(QObject *obj, QEvent *event)
245 {
246  if(obj == ui->m_doubleSpinBox_highpass || obj == ui->m_doubleSpinBox_lowpass || obj == ui->m_doubleSpinBox_transitionband) {
247  if (event->type() == QEvent::KeyPress) {
248  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
249 
250  if((keyEvent->modifiers() == Qt::ControlModifier && keyEvent->key() == Qt::Key_Z)/* || keyEvent->key() == Qt::Key_Delete*/)
251  undoFilter();
252  else // standard event processing
253  return QObject::eventFilter(obj, event);
254 
255  return true;
256  } else {
257  // standard event processing
258  return QObject::eventFilter(obj, event);
259  }
260  }
261 
262  return true;
263 }
264 
265 
266 //*************************************************************************************************************
267 
269 {
270  Q_UNUSED(currentIndex);
271 
272  //Change visibility offilter tap spin boxes depending on filter design method
273  switch(ui->m_comboBox_designMethod->currentIndex()) {
274  case 0: //Cosine
275  ui->m_spinBox_filterTaps->setVisible(false);
276  ui->m_label_filterTaps->setVisible(false);
277  break;
278 
279  case 1: //Tschebyscheff
280  ui->m_spinBox_filterTaps->setVisible(true);
281  ui->m_label_filterTaps->setVisible(true);
282  break;
283  }
284 
285  //Change visibility of spin boxes depending on filter type
286  switch(ui->m_comboBox_filterType->currentIndex()) {
287  case 0: //Lowpass
288  ui->m_doubleSpinBox_lowpass->setVisible(true);
289  ui->m_label_lowpass->setVisible(true);
290  ui->m_label_lowpass->setText("Cut-Off (Hz):");
291 
292  ui->m_doubleSpinBox_highpass->setVisible(false);
293  ui->m_label_highpass->setVisible(false);
294  ui->m_doubleSpinBox_highpass->setEnabled(false);
295  break;
296 
297  case 1: //Highpass
298  ui->m_doubleSpinBox_highpass->setVisible(true);
299  ui->m_label_highpass->setVisible(true);
300  ui->m_label_highpass->setText("Cut-Off (Hz):");
301 
302  ui->m_doubleSpinBox_lowpass->setVisible(false);
303  ui->m_label_lowpass->setVisible(false);
304  ui->m_doubleSpinBox_highpass->setEnabled(true);
305  break;
306 
307  case 2: //Bandpass
308  ui->m_doubleSpinBox_highpass->setVisible(true);
309  ui->m_label_highpass->setVisible(true);
310  ui->m_doubleSpinBox_lowpass->setVisible(true);
311  ui->m_label_lowpass->setText("Cut-Off Low (Hz):");
312 
313  ui->m_label_lowpass->setVisible(true);
314  ui->m_doubleSpinBox_lowpass->setEnabled(true);
315  ui->m_doubleSpinBox_highpass->setEnabled(true);
316  ui->m_label_highpass->setText("Cut-Off High (Hz):");
317  break;
318  }
319 
321 }
322 
323 
324 //*************************************************************************************************************
325 
327 {
328  //User defined filter parameters
329  double lowpassHz = ui->m_doubleSpinBox_lowpass->value();
330  double highpassHz = ui->m_doubleSpinBox_highpass->value();
331 
332  double trans_width = ui->m_doubleSpinBox_transitionband->value();
333 
334  double bw = highpassHz-lowpassHz;
335  double center = lowpassHz+bw/2;
336 
337  double samplingFrequency = m_pMainWindow->m_pDataWindow->getDataModel()->m_fiffInfo.sfreq;
338  double nyquistFrequency = samplingFrequency/2;
339 
340  //Calculate the needed fft length
341  int filterTaps = ui->m_spinBox_filterTaps->value();
342  int fftLength = m_iWindowSize;
343  int exp = ceil(MNEMath::log2(fftLength));
344  fftLength = pow(2, exp+1);
345 
346  //set maximum and minimum for cut off frequency spin boxes
347  if(ui->m_comboBox_filterType->currentText() == "Bandpass") {
348  ui->m_doubleSpinBox_highpass->setMinimum(ui->m_doubleSpinBox_lowpass->value());
349  ui->m_doubleSpinBox_lowpass->setMaximum(ui->m_doubleSpinBox_highpass->value());
350  }
351  else {
352  ui->m_doubleSpinBox_highpass->setMaximum(nyquistFrequency);
353  ui->m_doubleSpinBox_lowpass->setMaximum(nyquistFrequency);
354  }
355 
356  //set current fft length info label
357  ui->m_label_fftLength->setText(QString().number(fftLength));
358 
359  //set filter design method
360  FilterOperator::DesignMethod dMethod;
361  if(ui->m_comboBox_designMethod->currentText() == "Tschebyscheff")
362  dMethod = FilterOperator::Tschebyscheff;
363 
364  if(ui->m_comboBox_designMethod->currentText() == "Cosine")
365  dMethod = FilterOperator::Cosine;
366 
367  //Generate filters
368  QSharedPointer<MNEOperator> userDefinedFilterOperator;
369 
370  if(ui->m_comboBox_filterType->currentText() == "Lowpass") {
371  userDefinedFilterOperator = QSharedPointer<MNEOperator>(
372  new FilterOperator("User defined (See 'Adjust/Filter')",FilterOperator::LPF,filterTaps,lowpassHz/nyquistFrequency,0.2,(double)trans_width/nyquistFrequency,samplingFrequency,fftLength,dMethod));
373  }
374 
375  if(ui->m_comboBox_filterType->currentText() == "Highpass") {
376  userDefinedFilterOperator = QSharedPointer<MNEOperator>(
377  new FilterOperator("User defined (See 'Adjust/Filter')",FilterOperator::HPF,filterTaps,highpassHz/nyquistFrequency,0.2,(double)trans_width/nyquistFrequency,samplingFrequency,fftLength,dMethod));
378  }
379 
380  if(ui->m_comboBox_filterType->currentText() == "Bandpass") {
381  userDefinedFilterOperator = QSharedPointer<MNEOperator>(
382  new FilterOperator("User defined (See 'Adjust/Filter')",FilterOperator::BPF,filterTaps,(double)center/nyquistFrequency,(double)bw/nyquistFrequency,(double)trans_width/nyquistFrequency,samplingFrequency,fftLength,dMethod));
383  }
384 
385  //Replace old with new filter operator
386  QMutableMapIterator<QString,QSharedPointer<MNEOperator> > it(m_pMainWindow->m_pDataWindow->getDataModel()->m_Operators);
387  while(it.hasNext()) {
388  it.next();
389  if(it.key() == "User defined (See 'Adjust/Filter')") {
390  it.setValue(userDefinedFilterOperator);
391  }
392  }
393 
394  //update filter plot
395  updateFilterPlot();
396 }
397 
398 
399 //*************************************************************************************************************
400 
402 {
403  //Undo all previous filters first
404  m_pMainWindow->m_pDataWindow->getDataModel()->undoFilter(ui->m_comboBox_filterApplyTo->currentText());
405 
406  QMutableMapIterator<QString,QSharedPointer<MNEOperator> > it(m_pMainWindow->m_pDataWindow->getDataModel()->m_Operators);
407 
408  while(it.hasNext()) {
409  it.next();
410  if(it.key() == "User defined (See 'Adjust/Filter')") {
411  m_pMainWindow->m_pDataWindow->getDataModel()->applyOperator(QModelIndexList(), it.value(), ui->m_comboBox_filterApplyTo->currentText());
412  }
413  }
414 
415  m_pMainWindow->m_pDataWindow->updateDataTableViews();
416 }
417 
418 
419 //*************************************************************************************************************
420 
422 {
423  m_pMainWindow->m_pDataWindow->getDataModel()->undoFilter(ui->m_comboBox_filterUndoTo->currentText());
424 
425  m_pMainWindow->m_pDataWindow->updateDataTableViews();
426 }
427 
428 
429 //*************************************************************************************************************
430 
432 {
433  // Open file dialog
434  QDate date;
435  QString fileName = QFileDialog::getSaveFileName(this,
436  "Save filter plot",
437  QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
438  tr("Vector graphic(*.svg);;Images (*.png)"));
439 
440  if(!fileName.isEmpty())
441  {
442  // Generate screenshot
443  if(fileName.contains(".svg"))
444  {
445  QSvgGenerator svgGen;
446 
447  svgGen.setFileName(fileName);
448  QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
449  svgGen.setSize(QSize(rect.width(), rect.height()));
450  //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
451 
452  QPainter painter(&svgGen);
453  m_pFilterPlotScene->render(&painter);
454  }
455 
456  if(fileName.contains(".png"))
457  {
458  m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
459  QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
460  image.fill(Qt::transparent); // Start all pixels transparent
461 
462  QPainter painter(&image);
463  m_pFilterPlotScene->render(&painter);
464  image.save(fileName);
465  }
466  }
467 }
468 
469 
470 //*************************************************************************************************************
471 
473 {
474  // Open file dialog
475  QDate date;
476  QString fileName = QFileDialog::getSaveFileName(this,
477  "Save filter coefficients",
478  QString("%1/%2_%3_%4_FilterCoeffs").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
479  tr("Text file(*.txt)"));
480 
481  if(!fileName.isEmpty())
482  {
483  QFile file(fileName);
484  if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
485  return;
486 
487  //get user defined filter oeprator
488  QSharedPointer<FilterOperator> currentFilter;
489 
490  QMutableMapIterator<QString,QSharedPointer<MNEOperator> > it(m_pMainWindow->m_pDataWindow->getDataModel()->m_Operators);
491  while(it.hasNext()) {
492  it.next();
493  if(it.key() == "User defined (See 'Adjust/Filter')") {
494  if(it.value()->m_OperatorType == MNEOperator::FILTER) {
495  currentFilter = it.value().staticCast<FilterOperator>();
496 
497  //Write coefficients to file
498  QTextStream out(&file);
499  for(int i = 0 ; i<currentFilter->m_dCoeffA.cols() ;i++)
500  out << currentFilter->m_dCoeffA(i) << "\n";
501  }
502  }
503  }
504 
505  file.close();
506  }
507 }
508 
509 
510 
Definition: aboutwindow.h:52
void undoFilter(QModelIndexList chlist, const QSharedPointer< MNEOperator > &filterPtr)
Definition: rawmodel.cpp:844
void applyOperator(QModelIndexList chlist, const QSharedPointer< MNEOperator > &operatorPtr, const QString &chType)
Definition: rawmodel.cpp:681
QMap< QString, QSharedPointer< MNEOperator > > m_Operators
Definition: rawmodel.h:183
FilterWindow(MainWindow *mainWindow, QWidget *parent=0)
void changeStateSpinBoxes(int currentIndex)
The FilterPlotScene class provides the scene where a filter respone can be plotted.
Contains the declaration of the FilterWindow class.
void updateFilter(QSharedPointer< MNEOperator > operatorFilter, int samplingFreq, int cutOffLow, int cutOffHigh)