1 // Copyright (c) 2016 The SigViewer Development Team
2 // Licensed under the GNU General Public License (GPL)
3 // https://www.gnu.org/licenses/gpl
4
5
6 #include "basic_header_info_dialog.h"
7 #include "file_handling/basic_header.h"
8 #include "file_handling_impl/xdf_reader.h"
9
10 #include <cmath>
11 #include <algorithm>
12
13 #include <QPushButton>
14 #include <QVBoxLayout>
15 #include <QHBoxLayout>
16 #include <QHeaderView>
17 #include <QSettings>
18 #include <QFileInfo>
19 #include <QtXml>
20
21 namespace sigviewer
22 {
23
24 // constructor
BasicHeaderInfoDialog(QSharedPointer<BasicHeader> header,QWidget * parent)25 BasicHeaderInfoDialog::BasicHeaderInfoDialog(QSharedPointer<BasicHeader> header,
26 QWidget* parent)
27 : QDialog(parent),
28 basic_header_(header)
29 {
30 setWindowTitle(tr("Basic Header Info"));
31 QVBoxLayout* top_layout = new QVBoxLayout(this);
32 top_layout->setMargin(10);
33 top_layout->setSpacing(10);
34 info_tree_widget_ = new QTreeWidget(this);
35 top_layout->addWidget(info_tree_widget_);
36 QHBoxLayout* button_layout = new QHBoxLayout;
37 button_layout->setMargin(0);
38 top_layout->addLayout(button_layout);
39 button_layout->addStretch(1);
40 close_button_ = new QPushButton(tr("Close"), this);
41 toggle_button_ = new QPushButton(tr("Collapse All"), this);
42 button_layout->addWidget(toggle_button_);
43 button_layout->addWidget(close_button_);
44 button_layout->addStretch(1);
45 buildTree();
46 resize(850, 850);
47 top_layout->activate();
48 readSettings();
49 connect(close_button_, SIGNAL(clicked()), this, SLOT(closeInfoDialog()));
50 connect(this, SIGNAL(finished(int)), this, SLOT(closeInfoDialog()));
51 connect(toggle_button_, SIGNAL(clicked()), this, SLOT(toggleCollapseExpand()));
52 // connect(info_tree_widget_, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(showStreamName(QTreeWidgetItem*)));
53 // connect(info_tree_widget_, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(hideStreamName(QTreeWidgetItem*)));
54 }
55
toggleCollapseExpand()56 void BasicHeaderInfoDialog::toggleCollapseExpand()
57 {
58 if (toggle_button_->text().compare("Collapse All") == 0)
59 {
60 info_tree_widget_->collapseAll();
61 toggle_button_->setText("Expand All");
62 }
63 else if (toggle_button_->text().compare("Expand All") == 0)
64 {
65 info_tree_widget_->expandAll();
66 toggle_button_->setText("Collapse All");
67 }
68 }
69
showStreamName(QTreeWidgetItem * item)70 void BasicHeaderInfoDialog::showStreamName(QTreeWidgetItem *item)
71 {
72 if (item->text(0).startsWith("Stream", Qt::CaseInsensitive))
73 {
74 QRegExp rx("(\\d+)");
75 int pos = rx.indexIn(item->text(0));
76 QString str;
77 if (pos > -1)
78 {
79 str = rx.cap(1);
80 }
81 int streamNumber = str.toInt() - 1; //-1 to switch back to 0-based indexing
82
83 // int streamNumber = item->text(0).remove("Stream ").toInt() - 1;//-1 to switch back to 0 index
84 item->setText(1, QString::fromStdString(XDFdata->streams[streamNumber].info.name));
85 }
86 }
87
hideStreamName(QTreeWidgetItem * item)88 void BasicHeaderInfoDialog::hideStreamName(QTreeWidgetItem *item)
89 {
90 if (item->text(0).startsWith("Stream", Qt::CaseInsensitive))
91 {
92 item->setText(1, "");
93 }
94 }
95
closeInfoDialog()96 void BasicHeaderInfoDialog::closeInfoDialog()
97 {
98 QSettings settings;
99 settings.beginGroup("BasicHeaderInfoDialog");
100 settings.setValue("geometry", saveGeometry());
101 settings.endGroup();
102
103 close();
104 }
105
readSettings()106 void BasicHeaderInfoDialog::readSettings()
107 {
108 QSettings settings;
109 settings.beginGroup("BasicHeaderInfoDialog");
110 restoreGeometry(settings.value("geometry").toByteArray());
111 settings.endGroup();
112 }
113
114 // build tree
buildTree()115 void BasicHeaderInfoDialog::buildTree()
116 {
117 info_tree_widget_->setIconSize(QSize(16, 16));
118 info_tree_widget_->setRootIsDecorated(true);
119 QStringList header_labels;
120 header_labels << tr("Property") << tr("Value");
121 info_tree_widget_->setHeaderLabels(header_labels);
122
123 info_tree_widget_->header()->setSectionResizeMode(QHeaderView::Interactive);
124 info_tree_widget_->setColumnWidth(0, width() * 0.65);
125 info_tree_widget_->setAnimated(true);
126
127 QTreeWidgetItem* root_item;
128 QTreeWidgetItem* tmp_item;
129
130 // basic
131 root_item = new QTreeWidgetItem(info_tree_widget_);
132 root_item->setText(0, tr("Basic"));
133 // root_item->setIcon(0, QIcon(":/images/ic_info_outline_black_24dp.png"));
134
135 tmp_item = new QTreeWidgetItem(root_item);
136 tmp_item->setText(0, tr("File Type"));
137 tmp_item->setText(1, basic_header_->getFileTypeString());
138
139 QMap<QString, QString> recording_info = basic_header_->getRecordingInfo();
140 foreach (QString key, recording_info.keys())
141 {
142 tmp_item = new QTreeWidgetItem(root_item);
143 tmp_item->setText(0, key);
144 tmp_item->setText(1, recording_info[key]);
145 }
146
147
148 // tmp_item = new QTreeWidgetItem(root_item);
149 // tmp_item->setText(0, tr("Recording Time"));
150 // tmp_item->setText(1, basic_header_->getRecordingTime().toString("dd.MM.yyyy hh:mm:ss"));
151 // tmp_item = new QTreeWidgetItem(root_item);
152 // // tmp_item ->setTextAlignment(1, Qt::AlignRight);
153 // tmp_item->setText(0, tr("Triggered"));
154 // tmp_item->setText(1, basic_header_->isTriggered() ? tr("yes") : tr("no"));
155 // tmp_item = new QTreeWidgetItem(root_item);
156 // // tmp_item ->setTextAlignment(1, Qt::AlignRight);
157 // tmp_item->setText(0, tr("Recording"));
158 // tmp_item->setText(1, QString::number(basic_header_->getRecordDuration() *
159 // basic_header_->getNumberRecords()));
160 // tmp_item->setText(2, tr("seconds"));
161
162 // file
163 root_item = new QTreeWidgetItem(info_tree_widget_);
164 root_item->setText(0, tr("File"));
165 // root_item->setIcon(0, QIcon(":/images/ic_folder_open_black_24dp.png"));
166 tmp_item = new QTreeWidgetItem(root_item);
167 tmp_item->setText(0, tr("Size"));
168 QFileInfo file_info (basic_header_->getFilePath());
169 tmp_item->setText(1, QString::number(file_info.size() / 1024).append(tr(" KB")));
170 tmp_item->setText(0, tr("File Size"));
171
172 // events
173 root_item = new QTreeWidgetItem(info_tree_widget_);
174 root_item->setText(0, tr("Events"));
175 // root_item->setIcon(0, QIcon(":/images/ic_font_download_black_24dp.png"));
176 tmp_item = new QTreeWidgetItem(root_item);
177 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
178 tmp_item->setText(0, tr("Number"));
179 tmp_item->setText(1, QString::number(basic_header_->getNumberEvents()).append(tr(" events")));
180 tmp_item = new QTreeWidgetItem(root_item);
181 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
182 tmp_item->setText(0, tr("Sample Rate"));
183 tmp_item->setText(1, QString::number(basic_header_->getEventSamplerate()).append(tr(" Hz")));
184
185 //exclusively for XDF
186 if (basic_header_->getFileTypeString().startsWith("XDF", Qt::CaseInsensitive))
187 {
188 for (size_t i =0; i < XDFdata->streams.size(); i++)
189 {
190 // basic
191 root_item = new QTreeWidgetItem(info_tree_widget_);
192 // root_item->setText(0, "Stream "+QString::number(i + 1));//+1 for user's convenience (1 based instead 0 based)
193 root_item->setText(0, QString("Stream %1 (%2)").arg(QString::number(i+1)). //+1 for 1-based indexing
194 arg(QString::fromStdString(XDFdata->streams[i].info.name)));
195
196 // root_item->setIcon(0, QIcon(":/images/ic_flag_black_24dp.png"));
197
198 QDomDocument streamHeader;
199 streamHeader.setContent(QString::fromStdString(XDFdata->streams[i].streamHeader));
200 QDomElement rootElement = streamHeader.firstChildElement();
201
202 for (QDomNode n = rootElement.firstChild(); !n.isNull();)
203 {
204 tmp_item = new QTreeWidgetItem(root_item);
205 tmp_item->setText(0, n.nodeName());
206 if (!n.firstChild().isElement())
207 {
208 tmp_item->setText(1, n.toElement().text());
209 while (n.nextSibling().isNull() &&
210 n.parentNode() != rootElement)
211 {
212 n = n.parentNode();
213 tmp_item = tmp_item->parent();
214 root_item = tmp_item->parent();
215 }
216 n = n.nextSibling();
217 }
218 else
219 {
220 n = n.firstChild();
221 root_item = tmp_item;
222 }
223 }
224
225 QDomDocument streamFooter;
226 streamFooter.setContent(QString::fromStdString(XDFdata->streams[i].streamFooter));
227 rootElement = streamFooter.firstChildElement();
228 for (QDomNode n = rootElement.firstChild(); !n.isNull();)
229 {
230 tmp_item = new QTreeWidgetItem(root_item);
231 tmp_item->setText(0, n.nodeName());
232 if (!n.firstChild().isElement())
233 {
234 tmp_item->setText(1, n.toElement().text());
235 while (n.nextSibling().isNull() &&
236 n.parentNode() != rootElement)
237 {
238 n = n.parentNode();
239 tmp_item = tmp_item->parent();
240 root_item = tmp_item->parent();
241 }
242 n = n.nextSibling();
243 }
244 else
245 {
246 n = n.firstChild();
247 root_item = tmp_item;
248 }
249 }
250
251 }
252 } //XDF ends here
253 else
254 {
255 // patient
256 root_item = new QTreeWidgetItem(info_tree_widget_);
257 root_item->setText(0, tr("Patient"));
258 // root_item->setIcon(0, QIcon(":/images/patient_16x16.png"));
259 QMap<QString, QString> patient_info = basic_header_->getPatientInfo();
260 foreach (QString key, patient_info.keys())
261 {
262 tmp_item = new QTreeWidgetItem(root_item);
263 tmp_item->setText(0, key);
264 tmp_item->setText(1, patient_info[key]);
265 }
266
267 // channels
268 root_item = new QTreeWidgetItem(info_tree_widget_);
269 root_item->setText(0, tr("Channels"));
270 // root_item->setIcon(0, QIcon(":/images/channels_22x22.png"));
271
272 for (uint32 channel_nr = 0;
273 channel_nr < basic_header_->getNumberChannels();
274 channel_nr++)
275 {
276 QTreeWidgetItem* channel_item;
277 QSharedPointer<SignalChannel const> channel = basic_header_->getChannel (channel_nr);
278 channel_item = new QTreeWidgetItem(root_item);
279
280 // QRegExp rx("(^channel\\s*\\d+)");
281 // rx.setCaseSensitivity(Qt::CaseInsensitive);
282
283 // if (channel->getLabel().contains(rx))
284 // channel_item->setText(0, channel->getLabel());
285 // else
286 channel_item->setText(0, QString("Channel %1 (%2)").arg(channel_nr + 1) // +1 for 1-based indexing
287 .arg(channel->getLabel()));
288
289 // channel basic
290 tmp_item = new QTreeWidgetItem(channel_item);
291 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
292 tmp_item->setText(0, tr("Label"));
293 tmp_item->setText(1, channel->getLabel());
294 tmp_item = new QTreeWidgetItem(channel_item);
295
296 tmp_item->setText(0, tr("Sample Rate"));
297 float64 fs = channel->getSampleRate();
298 if (fs < 0.0)
299 fs = basic_header_->getSampleRate();
300 tmp_item->setText(1, QString::number(fs).append(tr(" Hz")));
301
302 tmp_item = new QTreeWidgetItem(channel_item);
303 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
304 tmp_item->setText(0, tr("Physical Dimension"));
305 tmp_item->setText(1, channel->getPhysicalDim());
306 tmp_item = new QTreeWidgetItem(channel_item);
307 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
308 tmp_item->setText(0, tr("Physical Maximum"));
309 tmp_item->setText(1, QString::number(channel->getPhysicalMaximum()));
310 tmp_item = new QTreeWidgetItem(channel_item);
311 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
312 tmp_item->setText(0, tr("Physical Minimum"));
313 tmp_item->setText(1, QString::number(channel->getPhysicalMinimum()));
314 tmp_item = new QTreeWidgetItem(channel_item);
315 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
316 tmp_item->setText(0, tr("Digital Maximum"));
317 tmp_item->setText(1, QString::number(channel->getDigitalMaximum()));
318 tmp_item = new QTreeWidgetItem(channel_item);
319 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
320 tmp_item->setText(0, tr("Digital Minimum"));
321 tmp_item->setText(1, QString::number(channel->getDigitalMinimum()));
322 tmp_item = new QTreeWidgetItem(channel_item);
323 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
324 tmp_item->setText(0, tr("Data Type"));
325 tmp_item->setText(1, channel->typeString());
326
327 // filter
328 QTreeWidgetItem* filter_item;
329 filter_item = new QTreeWidgetItem(channel_item);
330 filter_item->setText(0, tr("Filter"));
331 tmp_item = new QTreeWidgetItem(filter_item);
332 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
333 tmp_item->setText(0, tr("Highpass"));
334 //tmp_item->setText(1, isnan(channel->getHighpass()) ? tr("unknown") :
335 // (channel->getHighpass() < 0 ? "" :
336 // QString::number(channel->getHighpass())));
337 tmp_item->setText(2, tr("Hz"));
338 tmp_item = new QTreeWidgetItem(filter_item);
339 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
340 tmp_item->setText(0, tr("Lowpass"));
341 // tmp_item->setText(1, channel->getLowpass() ? tr("unknown") :
342 // (channel->getLowpass() < 0 ? "" :
343 // QString::number(channel->getLowpass())));
344 tmp_item->setText(2, tr("Hz"));
345 tmp_item = new QTreeWidgetItem(filter_item);
346 // tmp_item ->setTextAlignment(1, Qt::AlignRight);
347 tmp_item->setText(0, tr("Notch"));
348 //tmp_item->setText(1, isnan(channel->getNotch()) ? tr("unknown") : (channel->getNotch() ? tr("yes") : tr("no")));
349 }
350 }
351 info_tree_widget_->expandAll();
352 }
353
354 }
355