Fraxinus  17.12-rc1
An IGT application
cxMetricWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 /*
34  * cxMetricWidget.cpp
35  *
36  * \date Jul 5, 2011
37  * \author christiana
38  */
39 
40 #include <cxMetricWidget.h>
41 
42 #include <QTreeWidget>
43 #include <QTreeWidgetItem>
44 #include <QStringList>
45 #include <QVBoxLayout>
46 #include <QHeaderView>
47 
48 
49 #include "cxTypeConversions.h"
51 #include "cxTrackingService.h"
53 #include "cxVector3DWidget.h"
54 #include "cxTimeKeeper.h"
55 #include "cxTime.h"
56 #include "cxMetricManager.h"
57 #include "cxMetricUtilities.h"
58 #include "cxPatientModelService.h"
59 #include "cxVisServices.h"
60 
61 
62 namespace cx
63 {
64 
65 
66 //---------------------------------------------------------
67 //---------------------------------------------------------
68 //---------------------------------------------------------
69 
70 MetricWidget::MetricWidget(VisServicesPtr services, QWidget* parent) :
71  BaseWidget(parent, "metric_widget", "Metrics/3D ruler"),
72  mVerticalLayout(new QVBoxLayout(this)),
73  mTable(new QTableWidget(this)),
74  mServices(services)
75 {
76  // the delayed timer lowers the update rate of this widget,
77  // as is is seen to strangle the render speed when many metrics are present.
78  int lowUpdateRate = 100;
79  mLocalModified = false;
80  mDelayedUpdateTimer = new QTimer(this);
81  connect(mDelayedUpdateTimer, SIGNAL(timeout()), this, SLOT(delayedUpdate())); // this signal will be executed in the thread of THIS, i.e. the main thread.
82  mDelayedUpdateTimer->start(lowUpdateRate);
83  this->setToolTip("3D measurements");
84 
85  mModifiedCount = 0;
86  mPaintCount = 0;
87  mMetricManager.reset(new MetricManager(services->view(), services->patient(), services->tracking(), services->spaceProvider()));
88  connect(mMetricManager.get(), SIGNAL(activeMetricChanged()), this, SLOT(setModified()));
89  connect(mMetricManager.get(), SIGNAL(metricsChanged()), this, SLOT(setModified()));
90 
91  //table widget
92  connect(mTable, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged()));
93  connect(mTable, SIGNAL(cellChanged(int, int)), this, SLOT(cellChangedSlot(int, int)));
94  connect(mTable, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
95 
96  this->setLayout(mVerticalLayout);
97 
98  mEditWidgets = new QStackedWidget;
99 
100  QActionGroup* group = new QActionGroup(this);
101  this->createActions(group);
102 
103 // QToolBar* toolBar = new QToolBar("actions", this);
104 // toolBar->addActions(group->actions());
105 
106  QWidget* toolBar = new QWidget(this);
107  QHBoxLayout* toolLayout = new QHBoxLayout(toolBar);
108  toolLayout->setMargin(0);
109  toolLayout->setSpacing(0);
110  QList<QAction*> actions = group->actions();
111  for (unsigned i=0; i<actions.size(); ++i)
112  {
113  if (actions[i]->isSeparator())
114  {
115  toolLayout->addSpacing(4);
116  QFrame* frame = new QFrame();
117  frame->setFrameStyle(QFrame::Sunken + QFrame::VLine);
118  toolLayout->addWidget(frame);
119  toolLayout->addSpacing(4);
120  }
121  else
122  {
124  button->setDefaultAction(actions[i]);
125  button->setIconSize(QSize(32,32));
126  toolLayout->addWidget(button);
127  }
128  }
129 
130  QHBoxLayout* buttonLayout = new QHBoxLayout;
131  buttonLayout->addWidget(toolBar);
132  buttonLayout->addStretch();
133 
134  //layout
135  mVerticalLayout->addLayout(buttonLayout);
136  mVerticalLayout->addWidget(mTable, 1);
137  mVerticalLayout->addWidget(mEditWidgets, 0);
138 }
139 
141 {}
142 
143 void MetricWidget::createActions(QActionGroup* group)
144 {
145  mPointMetricAction = this->createAction(group, ":/icons/metric_point.png", "Pt", "Create a new Point Metric", SLOT(addPointButtonClickedSlot()));
146  mFrameMetricAction = this->createAction(group, ":/icons/metric_frame.png", "Frame", "Create a new Frame Metric (position and orientation)", SLOT(addFrameButtonClickedSlot()));
147  mToolMetricAction = this->createAction(group, ":/icons/metric_tool.png", "Tool", "Create a new Tool Metric", SLOT(addToolButtonClickedSlot()));
148  this->createAction(group, ":/icons/metric_distance.png", "Dist", "Create a new Distance Metric", SLOT(addDistanceButtonClickedSlot()));
149  this->createAction(group, ":/icons/metric_angle.png", "Angle", "Create a new Angle Metric", SLOT(addAngleButtonClickedSlot()));
150  this->createAction(group, ":/icons/metric_plane.png", "Plane", "Create a new Plane Metric", SLOT(addPlaneButtonClickedSlot()));
151  this->createAction(group, ":/icons/metric_sphere.png", "Sphere", "Create a new Sphere Metric", SLOT(addSphereButtonClickedSlot()));
152  this->createAction(group, ":/icons/metric_torus.png", "Torus", "Create a new Torus Metric", SLOT(addDonutButtonClickedSlot()));
153  this->createAction(group, ":/icons/metric_custom.png", "Custom", "Create a new Custom Metric", SLOT(addCustomButtonClickedSlot()));
154  this->createAction(group, ":/icons/metric.png", "ROI", "Create a new Region of Interest Metric", SLOT(addROIButtonClickedSlot()));
155 
156  this->createAction(group, "", "", "", NULL)->setSeparator(true);
157  mRemoveAction = this->createAction(group, ":/icons/metric_remove.png", "Remove", "Remove currently selected metric", SLOT(removeButtonClickedSlot()));
158  mRemoveAction->setDisabled(true);
159  mLoadReferencePointsAction = this->createAction(group, ":/icons/metric_reference.png", "Import", "Import reference points from reference tool", SLOT(loadReferencePointsSlot()));
160  mLoadReferencePointsAction->setDisabled(true);
161  this->createAction(group, "", "", "", NULL)->setSeparator(true);
162  mExportFramesAction = this->createAction(group, ":/icons/save.png", "ExportFrames", "Export the metrics to a file", SLOT(exportMetricsButtonClickedSlot()));
163  mImportFramesAction = this->createAction(group, ":/icons/open.png", "ImportFrames", "Import metrics from a file (can be a patient file)", SLOT(importMetricsButtonClickedSlot()));
164 }
165 
166 //template<class T>
167 QAction* MetricWidget::createAction(QActionGroup* group, QString iconName, QString text, QString tip, const char* slot)
168 {
169  QAction* action = new QAction(QIcon(iconName), text, group);
170  action->setStatusTip(tip);
171  action->setToolTip(tip);
172  if (slot)
173  {
174  connect(action, SIGNAL(triggered()), this, slot);
175  }
176  return action;
177 }
178 
179 void MetricWidget::cellChangedSlot(int row, int col)
180 {
181  if (col==0) // data name changed
182  {
183  QTableWidgetItem* item = mTable->item(row,col);
184  DataPtr data = mServices->patient()->getData(item->data(Qt::UserRole).toString());
185  if (data)
186  data->setName(item->text());
187  }
188 
189 }
190 
191 void MetricWidget::cellClickedSlot(int row, int column)
192 {
193  if (row < 0 || column < 0)
194  return;
195 
196  QTableWidgetItem* item = mTable->item(row,column);
197  QString uid = item->data(Qt::UserRole).toString();
198  mMetricManager->moveToMetric(uid);
199 }
200 
202 {
203  QTableWidgetItem* item = mTable->currentItem();
204 
205  mMetricManager->setActiveUid(item->data(Qt::UserRole).toString());
206  mEditWidgets->setCurrentIndex(mTable->currentRow());
207 
208  mMetricManager->setSelection(this->getSelectedUids());
209 
210  enablebuttons();
211 }
212 
213 void MetricWidget::showEvent(QShowEvent* event)
214 {
215  QWidget::showEvent(event);
216  this->setModified();
217 }
218 
219 void MetricWidget::hideEvent(QHideEvent* event)
220 {
221  QWidget::hideEvent(event);
222 }
223 
224 void MetricWidget::prePaintEvent()
225 {
226  // QTime timer;
227  // timer.start();
228  mPaintCount++;
229 
230  MetricUtilities utilities(mServices);
231 
232  std::vector<MetricBasePtr> newMetrics = utilities.createMetricWrappers();
233 
234  bool rebuild = !this->checkEqual(newMetrics, mMetrics);
235  if (rebuild)
236  {
237  this->resetWrappersAndEditWidgets(newMetrics);
238  this->initializeTable();
239  }
240 
241  this->updateMetricWrappers();
242  this->updateTableContents();
243 
244  if (rebuild)
245  {
246  this->expensizeColumnResize();
247  }
248 
249  this->enablebuttons();
250 // std::cout << QString("prepaint, mod=%1, paint=%2, elapsed=%3ms").arg(mModifiedCount).arg(mPaintCount).arg(timer.elapsed()) << std::endl;
251 // std::cout << QString("prepaint, mod=%1, paint=%2").arg(mModifiedCount).arg(mPaintCount) << std::endl;
252 }
253 
254 void MetricWidget::expensizeColumnResize()
255 {
256  int valueColumn = 1;
257  mTable->resizeColumnToContents(valueColumn);
258 }
259 
260 void MetricWidget::initializeTable()
261 {
262  mTable->blockSignals(true);
263 
264  mTable->clear();
265 
266  mTable->setRowCount(mMetrics.size());
267  mTable->setColumnCount(4);
268  QStringList headerItems(QStringList() << "Name" << "Value" << "Arguments" << "Type");
269  mTable->setHorizontalHeaderLabels(headerItems);
270 // mTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); // dangerous: uses lots of painting time
271  mTable->setSelectionBehavior(QAbstractItemView::SelectRows);
272  mTable->verticalHeader()->hide();
273 
274  for (unsigned i = 0; i < mMetrics.size(); ++i)
275  {
276  MetricBasePtr current = mMetrics[i];
277 
278  for (unsigned j = 0; j < 4; ++j)
279  {
280  QTableWidgetItem* item = new QTableWidgetItem("empty");
281  item->setData(Qt::UserRole, current->getData()->getUid());
282  mTable->setItem(i, j, item);
283  }
284  }
285  mTable->blockSignals(false);
286 }
287 
288 void MetricWidget::updateMetricWrappers()
289 {
290  for (unsigned i = 0; i < mMetrics.size(); ++i)
291  {
292  mMetrics[i]->update();
293  }
294 }
295 
296 void MetricWidget::updateTableContents()
297 {
298  mTable->blockSignals(true);
299  // update contents:
300  QTime timer;
301  timer.start();
302 
303  for (unsigned i = 0; i < mMetrics.size(); ++i)
304  {
305  MetricBasePtr current = mMetrics[i];
306  if (!mTable->item(i,0))
307  {
308  std::cout << "no qitem for:: " << i << " " << current->getData()->getName() << std::endl;
309  continue;
310  }
311  QString name = current->getData()->getName();
312  QString value = current->getValue();
313  QString arguments = current->getArguments();
314  QString type = current->getType();
315 
316  mTable->item(i,0)->setText(name);
317  mTable->item(i,1)->setText(value);
318  mTable->item(i,2)->setText(arguments);
319  mTable->item(i,3)->setText(type);
320 
321  //highlight selected row
322  if (current->getData()->getUid() == mMetricManager->getActiveUid())
323  {
324  mTable->setCurrentCell(i,1);
325  mEditWidgets->setCurrentIndex(i);
326  }
327  }
328  mTable->blockSignals(false);
329 }
330 
332 {
333  mLocalModified = true;
334 // BaseWidget::setModified();
335  mModifiedCount++;
336 }
337 
339 {
340  if (!mLocalModified)
341  return;
343  mLocalModified = false;
344 
345 }
346 
347 void MetricWidget::resetWrappersAndEditWidgets(std::vector<MetricBasePtr> wrappers)
348 {
349  while (mEditWidgets->count())
350  {
351  mEditWidgets->removeWidget(mEditWidgets->widget(0));
352  }
353 
354  for (unsigned i=0; i<mMetrics.size(); ++i)
355  {
356  disconnect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
357  }
358 
359  mMetrics = wrappers;
360 
361  for (unsigned i=0; i<mMetrics.size(); ++i)
362  {
363  connect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
364  }
365 
366  for (unsigned i=0; i<mMetrics.size(); ++i)
367  {
368  MetricBasePtr wrapper = mMetrics[i];
369  QGroupBox* groupBox = new QGroupBox(wrapper->getData()->getName(), this);
370  groupBox->setFlat(true);
371  QVBoxLayout* gbLayout = new QVBoxLayout(groupBox);
372  gbLayout->setMargin(4);
373  gbLayout->addWidget(wrapper->createWidget());
374  mEditWidgets->addWidget(groupBox);
375  }
376 
377  mEditWidgets->setCurrentIndex(-1);
378 }
379 
380 bool MetricWidget::checkEqual(const std::vector<MetricBasePtr>& a, const std::vector<MetricBasePtr>& b) const
381 {
382  if (a.size()!=b.size())
383  return false;
384 
385  for (unsigned i=0; i<a.size(); ++i)
386  {
387  if (a[i]->getData()!=b[i]->getData())
388  return false;
389  }
390 
391  return true;
392 }
393 
394 void MetricWidget::enablebuttons()
395 {
396  mRemoveAction->setEnabled(!mMetricManager->getActiveUid().isEmpty());
397  mLoadReferencePointsAction->setEnabled(mServices->tracking()->getReferenceTool() ? true : false);
398 }
399 
401 {
402  mMetricManager->loadReferencePointsSlot();
403 }
405 {
406  mMetricManager->addPointButtonClickedSlot();
407 }
409 {
410  mMetricManager->addFrameButtonClickedSlot();
411 }
413 {
414  mMetricManager->addToolButtonClickedSlot();
415 }
417 {
418  mMetricManager->addPlaneButtonClickedSlot();
419 }
421 {
422  mMetricManager->addAngleButtonClickedSlot();
423 }
425 {
426  mMetricManager->addDistanceButtonClickedSlot();
427 }
429 {
430  mMetricManager->addROIButtonClickedSlot();
431 }
433 {
434  mMetricManager->addSphereButtonClickedSlot();
435 }
437 {
438  mMetricManager->addDonutButtonClickedSlot();
439 }
441 {
442  mMetricManager->addCustomButtonClickedSlot();
443 }
444 
445 std::set<QString> MetricWidget::getSelectedUids()
446 {
447  QList<QTableWidgetItem*> selection = mTable->selectedItems();
448 
449  std::set<QString> selectedUids;
450  for (int i=0; i<selection.size(); ++i)
451  {
452  selectedUids.insert(selection[i]->data(Qt::UserRole).toString());
453  }
454  return selectedUids;
455 }
456 
458 {
459  int nextIndex = mTable->currentRow() + 1;
460  QString nextUid;
461  if (nextIndex < mTable->rowCount())
462  {
463  QTableWidgetItem* nextItem = mTable->item(nextIndex, 0);
464  nextUid = nextItem->data(Qt::UserRole).toString();
465  }
466 
467  mServices->patient()->removeData(mMetricManager->getActiveUid());
468 
469  if (!nextUid.isEmpty())
470  mMetricManager->setActiveUid(nextUid);
471 }
472 
474 {
475  QString suggestion = QString("%1/Logs/metrics_%2.xml")
476  .arg(mServices->patient()->getActivePatientFolder())
477  .arg(QDateTime::currentDateTime().toString(timestampSecondsFormat()));
478 
479  QString filename = QFileDialog::getSaveFileName(this,
480  "Select the file to export the metrics to",
481  suggestion);
482  if(!filename.isEmpty())
483  mMetricManager->exportMetricsToXMLFile(filename);
484 }
485 
487 {
488  QString suggestion = QString("%1/Logs/")
489  .arg(mServices->patient()->getActivePatientFolder());
490 
491  QString fileName = QFileDialog::getOpenFileName(this,
492  "Select the file to import metrics from (can be a patient file)",
493  suggestion,
494  "XML or MNI Tag file (*.xml *.tag)");
495 
496  if(!fileName.isEmpty() && fileName.endsWith(".xml"))
497  mMetricManager->importMetricsFromXMLFile(fileName);
498  else if(!fileName.isEmpty() && fileName.endsWith(".tag"))
499  mMetricManager->importMetricsFromMNITagFile(fileName);
500 }
501 
502 
503 
504 }//end namespace cx
void addCustomButtonClickedSlot()
QAction * mFrameMetricAction
virtual void setModified()
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:61
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:755
void addROIButtonClickedSlot()
void removeButtonClickedSlot()
std::vector< MetricBasePtr > createMetricWrappers()
QString timestampSecondsFormat()
Definition: cxTime.cpp:39
QAction * mExportFramesAction
boost::shared_ptr< class Data > DataPtr
boost::shared_ptr< class MetricBase > MetricBasePtr
void addPointButtonClickedSlot()
void cellChangedSlot(int row, int col)
void importMetricsButtonClickedSlot()
void addDonutButtonClickedSlot()
void addAngleButtonClickedSlot()
void exportMetricsButtonClickedSlot()
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:109
void addFrameButtonClickedSlot()
void addToolButtonClickedSlot()
void addPlaneButtonClickedSlot()
void loadReferencePointsSlot()
QAction * mImportFramesAction
VisServicesPtr mServices
virtual void cellClickedSlot(int row, int column)
MetricWidget(VisServicesPtr services, QWidget *parent)
QAction * mPointMetricAction
void addDistanceButtonClickedSlot()
QAction * mToolMetricAction
void addSphereButtonClickedSlot()
Namespace for all CustusX production code.