BioSignalPi  v2
plottingwidget.cpp
Go to the documentation of this file.
1 #include "plottingwidget.h"
2 #include "sliderwidget.h"
3 #include "loadbigfile.h"
4 #include <plot.h>
5 #include <QtWidgets>
6 
8  QWidget( parent )
9 {
10  recordingTime = 0;
11  nFrames = 0;
12  currentFrame = 0;
13 
14  setupComponents();
15  setupActions();
16  setupLayout();
17 }
18 
19 /*
20  * Load the supported ECG formats from the application directory.
21  *
22  * Supported formats: .txt, .edf, .bdf
23  */
24 void PlottingWidget::getEcgFileList()
25 {
26  ecgFilesWidget->clear();
27 
28  QStringList nameFilter;
29  nameFilter << "*.txt";
30  nameFilter << "*.edf";
31  nameFilter << "*.bdf";
32  QDir directory(qApp->applicationDirPath());
33  QStringList ecgFiles = directory.entryList(nameFilter);
34 
35  ecgFilesWidget->addItems(ecgFiles);
36 }
37 
38 /*
39  * If a file is double-clicked, load it in a different thread to prevent locking the GUI
40  */
41 void PlottingWidget::fileSelected(QListWidgetItem *item)
42 {
43  tlbl->setText("Loading "+item->text());
44  progBar->setVisible(true);
45  progBar->setRange(0,0);
46  progBar->setValue(0);
47 
48  LoadBigFile *loadFile = new LoadBigFile(item->text());
49 
50  connect(loadFile, SIGNAL(sendFileData(QVector<QVector<QPointF> >)), this, SLOT(initEcg(QVector<QVector<QPointF> >)));
51 
52  loadFile->start();
53 
54  //Read header file and display information about the recording
55  QFileInfo fInfo(item->text());
56  QFile headerFile(fInfo.baseName()+"_header.txt");
57  if (headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
58  QTextStream headerTextStream(&headerFile);
59  QString headerText;
60 
61  while (!headerTextStream.atEnd()) {
62  headerText += headerTextStream.readLine() + "\n";
63  }
64 
65  headerTextArea->setText(headerText);
66  headerTextStream.flush();
67  headerFile.close();
68  }
69 }
70 
71 /*
72  * Clear the plot
73  */
74 void PlottingWidget::clearPlot()
75 {
76  for (int ii = 0; ii<4; ii++) {
77  d_plots[ii]->ClearPlot();
78  }
79 
80 
81 }
82 
83 /*
84  * If the slider for browsing the ecg is moved, update the plot to show the new interval of
85  * the ECG.
86  */
87 void PlottingWidget::updatePlot(int index)
88 {
89  int windowSize = timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt();
90  int startingPosition = index*windowSize;
91  currentFrame = index;
92 
93  for (int ii = 0; ii<4; ii++) {
94  d_plots[ii]->setInterval(startingPosition, startingPosition+windowSize);
95  }
96  // d_plot->setInterval(startingPosition, startingPosition+windowSize);
97 }
98 
99 /*
100  * If the window size of the plot is changed, go back to the start and update the plotted interval
101  */
102 void PlottingWidget::windowSizeChanged(int index)
103 {
104  if (index > 1) {
105  currentFrame = 0;
106  int windowSize = timeframeComboBox->itemData(index).toInt();
107  nFrames = static_cast<int>(recordingTime/timeframeComboBox->itemData(index).toInt());
108 
109  for (int ii = 0; ii<4; ii++) {
110  d_plots[ii]->setInterval(0, windowSize);
111  }
112 
113  //d_plot->setInterval(0, windowSize);
114  } else if (index == 0) {
115  for (int ii = 0; ii<4; ii++) {
116  d_plots[ii]->setInterval(0, recordingTime);
117  }
118 
119  // d_plot->setInterval(0, recordingTime);
120  nFrames = 0;
121  }
122 
123  startingPointSlider->setInterval(0, nFrames);
124  startingPointSlider->setValue(0);
125 }
126 
127 /*
128  * Init the ECG vector and display the ECG.
129  */
130 void PlottingWidget::initEcg(QVector<QVector<QPointF> > fileData)
131 {
132  ecgVals = fileData;
133 
134  tlbl->setText("Done!");
135  progBar->setMaximum(10);
136  progBar->setValue(10);
137 
138  recordingTime = static_cast<int>(ecgVals.last().at(0).x());
139  nFrames = static_cast<int>(recordingTime/timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt());
140 
141  startingPointSlider->setInterval(0,nFrames);
142 
143  plotVector();
144 
145  for (int ii = 0; ii<4; ii++) {
146  d_plots[ii]->setInterval(0, timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt());
147  }
148 
149  if (recordingTime>5) {
150  QVector<double> maxVector;
151  QVector<double> minVector;
152 
153  for (int ii = 0; ii<4; ii++) {
154  maxVector.append(ecgVals[500].at(ii).y());
155  minVector.append(ecgVals[500].at(ii).y());
156  }
157 
158  for (int ii = 500; ii<ecgVals.length(); ii++) {
159  for (int jj = 0; jj<4; jj++) {
160  if(ecgVals[ii].at(jj).y()<maxVector[jj]) {
161  if (ecgVals[ii].at(jj).y()<minVector[jj])
162  {
163  minVector[jj] = ecgVals[ii].at(jj).y();
164  }
165  } else {
166  maxVector[jj] = ecgVals[ii].at(jj).y();
167  }
168  }
169  }
170 
171  for (int ii = 0; ii<4; ii++) {
172  d_plots[ii]->setYAxis(minVector[ii], maxVector[ii]);
173  }
174 
175  }
176 
177  // d_plot->setInterval(0, timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt());
178 }
179 
180 /*
181  * Plot the vector obtained from the initEcg method
182  */
183 void PlottingWidget::plotVector()
184 {
185  const int sampleSize = ecgVals.length();
186  d_plots[0]->setInterval(0, ecgVals.last().at(0).x());
187 
188  for (int ii = 0; ii<4; ii++) {
189  d_plots[ii]->ClearPlot();
190  }
191  // d_plot->ClearPlot();
192 
193  for (int ii = 0; ii < sampleSize; ii++) {
194  for (int jj = 0; jj<4; jj++) {
195  d_plots[jj]->AppendPoint(ecgVals.at(ii).at(jj));
196  }
197  // d_plot[ii]->AppendPoint(ecgVals.at(ii).at(0));
198  }
199 
200  for (int ii = 0; ii<4; ii++) {
201  d_plots[ii]->DrawCurveSegment(sampleSize);
202  }
203  // d_plot->DrawCurveSegment(sampleSize);
204 
205 }
206 
207 /*
208  * Show the previous frame of the signal
209  */
210 void PlottingWidget::prev()
211 {
212  if (currentFrame > 0) {
213  int windowSize = timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt();
214  currentFrame = startingPointSlider->value() - 1;
215  startingPointSlider->setValue(currentFrame);
216 
217  for (int ii = 0; ii<4; ii++) {
218  d_plots[ii]->setInterval(currentFrame*windowSize, (currentFrame+1)*windowSize);
219  }
220  // d_plot->setInterval(currentFrame*windowSize, (currentFrame+1)*windowSize);
221  }
222 }
223 
224 /*
225  * Start ECG playback
226  */
227 void PlottingWidget::play()
228 {
229  int windowSize = timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt();
230  playTimer->start(windowSize*1000);
231 }
232 
233 /*
234  * Pause ECG playback
235  */
236 void PlottingWidget::pause()
237 {
238  playTimer->stop();
239 }
240 
241 /*
242  * Show the next fram of the signal
243  */
244 void PlottingWidget::next()
245 {
246  if (currentFrame<nFrames) {
247  int windowSize = timeframeComboBox->itemData(timeframeComboBox->currentIndex()).toInt();
248  currentFrame = startingPointSlider->value() + 1;
249  startingPointSlider->setValue(currentFrame);
250 
251  for (int ii = 0; ii<4; ii++) {
252  d_plots[ii]->setInterval(currentFrame*windowSize, (currentFrame+1)*windowSize);
253  }
254  // d_plot->setInterval(currentFrame*windowSize, (currentFrame+1)*windowSize);
255  }
256 }
257 
258 /*
259  * Catches the event when the mouse is in the widget are and the scroll button is moved.
260  * If scrolled away from the user the next methos is triggered and a step forward is made,
261  * if scrolled towards the user the prev method is triggered to browse backwards.
262  */
263 void PlottingWidget::wheelEvent(QWheelEvent *event)
264 {
265  int delta = event->delta();
266 
267  if (delta > 0) {
268  next();
269  } else if (delta < 0) {
270  prev();
271  }
272 }
273 
274 /*
275  * Setup the necessary components that is used in the widget
276  */
277 void PlottingWidget::setupComponents()
278 {
279  //Timer used to play a recorded ECG
280  playTimer = new QTimer();
281 
282  //Buttons
283  getFilesButton = new QPushButton("Get ECG files");
284  resetPlotButton = new QPushButton("Reset plot");
285 
286  //Header text area
287  headerTextArea = new QTextEdit("");
288 
289  //Progressbar label
290  tlbl = new QLabel("no file selected");
291 
292  //List of ecg files
293  ecgFilesWidget = new QListWidget;
294  ecgFilesWidget->setFixedSize(QSize(QWIDGETSIZE_MAX,100));
295 
296  //Plot widget
297  //d_plot = new Plot("",this);
298  d_plots.append(new Plot("Lead I", this));
299  d_plots.append(new Plot("Lead II", this));
300  d_plots.append(new Plot("Lead III", this));
301  d_plots.append(new Plot("Respiration", this));
302 
303  d_plots[0]->setCurveColor(Qt::red);
304  d_plots[1]->setCurveColor(Qt::blue);
305  d_plots[2]->setCurveColor(Qt::green);
306  d_plots[3]->setCurveColor(Qt::black);
307 
308  d_plots[3]->setCurveMargin(0.0001);
309 
310  //Progress bar
311  progBar = new QProgressBar();
312  progBar->setRange(0,0);
313  progBar->setVisible(false);
314  progBar->setTextVisible(false);
315 
316  //Window size component
317  timeframeComboBox = new QComboBox();
318  timeframeComboBox->addItem("Whole recording", 0);
319  timeframeComboBox->insertSeparator(1);
320  timeframeComboBox->addItem("5 s", 5);
321  timeframeComboBox->addItem("10 s", 10);
322  timeframeComboBox->addItem("1 min", 60);
323  timeframeComboBox->setCurrentIndex(2);
324 
325  //Slider to navigate the recorded signal
326  startingPointSlider = new SliderWidget();
327 }
328 
329 /*
330  * Setup actions and the signal-slots used in the widget
331  */
332 void PlottingWidget::setupActions()
333 {
334  //ECG navigation control buttons
335  prevAction = new QAction(QIcon(":/images/images/media-skip-backward-6.png"), "Prev.", this);
336  playAction = new QAction(QIcon(":/images/images/media-playback-start-6.png"), "Play", this);
337  pauseAction = new QAction(QIcon(":/images/images/media-playback-pause-6.png"), "Pause", this);
338  nextAction = new QAction(QIcon(":/images/images/media-skip-forward-6.png"), "Next", this);
339 
340  //Signals-slots
341  connect(prevAction, SIGNAL(triggered()), this, SLOT(prev()));
342  connect(nextAction, SIGNAL(triggered()), this, SLOT(next()));
343  connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
344  connect(pauseAction, SIGNAL(triggered()), this, SLOT(pause()));
345  connect(timeframeComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(windowSizeChanged(int)));
346  connect(getFilesButton, SIGNAL(clicked()), this, SLOT(getEcgFileList()));
347  connect(resetPlotButton, SIGNAL(clicked()), this, SLOT(clearPlot()));
348  connect(startingPointSlider, SIGNAL(valueChanged(int)), this, SLOT(updatePlot(int)));
349  connect(ecgFilesWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(fileSelected(QListWidgetItem*)));
350  connect(playTimer, SIGNAL(timeout()), this, SLOT(next()));
351 }
352 
353 /*
354  * Setup the layout of the widget
355  */
356 void PlottingWidget::setupLayout()
357 {
358 
359  QLabel *wsLabel = new QLabel("Window size");
360 
361  //Setup the navigation control
362  QToolBar *playbackButtons = new QToolBar;
363  playbackButtons->addAction(prevAction);
364  playbackButtons->addAction(playAction);
365  playbackButtons->addAction(pauseAction);
366  playbackButtons->addAction(nextAction);
367 
368  //Progressbar area
369  QHBoxLayout *progressBarLayout = new QHBoxLayout;
370  progressBarLayout->addWidget(tlbl, 2);
371  progressBarLayout->addWidget(progBar, 8);
372 
373  //Window size control area
374  QHBoxLayout *wsLayout = new QHBoxLayout;
375  wsLayout->addStretch();
376  wsLayout->addWidget(wsLabel);
377  wsLayout->addWidget(timeframeComboBox);
378  wsLayout->addStretch();
379 
380  //Area with navigation control components
381  QHBoxLayout *playbackLayout = new QHBoxLayout;
382  playbackLayout->addWidget(playbackButtons);
383  playbackLayout->addWidget(startingPointSlider);
384 
385  //Sidebar layout
386  QVBoxLayout *sidebarLayout = new QVBoxLayout;
387  sidebarLayout->addWidget(getFilesButton);
388  sidebarLayout->addWidget(ecgFilesWidget);
389  sidebarLayout->addLayout(progressBarLayout);
390  sidebarLayout->addLayout(wsLayout);
391  sidebarLayout->addWidget(headerTextArea);
392  sidebarLayout->addStretch();
393  sidebarLayout->addWidget(resetPlotButton);
394 
395  //Plot layout
396  QVBoxLayout *plotLayout = new QVBoxLayout;
397  //plotLayout->addWidget(d_plot);
398 
399  for (int ii = 0; ii < 4; ii++) {
400  plotLayout->addWidget(d_plots[ii]);
401  }
402 
403  plotLayout->addLayout(playbackLayout);
404 
405  //Main layout
406  QHBoxLayout *mainLayout = new QHBoxLayout;
407  mainLayout->addLayout(sidebarLayout, 1);
408  mainLayout->addLayout(plotLayout, 9);
409 
410  setLayout(mainLayout);
411 }
PlottingWidget(QWidget *=NULL)
void wheelEvent(QWheelEvent *event)
Handles the events triggered by moving the scroll button Catches the event when the mouse is in the w...
Extension of QwtPlot used for plotting data.
Definition: plot.h:14
void setInterval(int, int)
Class used by PlottingWidget to enable a slider for the different signals plotted.
Definition: sliderwidget.h:14
Class used to load a big .txt file in a new thread.
Definition: loadbigfile.h:14
void setValue(int)