CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 "cxFrameMetricWrapper.h"
56 #include "cxToolMetricWrapper.h"
57 #include "cxTime.h"
58 #include "cxMetricManager.h"
59 
60 #include "cxPatientModelService.h"
61 
62 
63 namespace cx
64 {
65 
66 
67 //---------------------------------------------------------
68 //---------------------------------------------------------
69 //---------------------------------------------------------
70 
71 MetricWidget::MetricWidget(VisualizationServicePtr visualizationService, PatientModelServicePtr patientModelService, QWidget* parent) :
72  BaseWidget(parent, "MetricWidget", "Metrics/3D ruler"),
73  mVerticalLayout(new QVBoxLayout(this)),
74  mTable(new QTableWidget(this)),
75  mPatientModelService(patientModelService),
76  mVisualizationService(visualizationService)
77 {
78  // the delayed timer lowers the update rate of this widget,
79  // as is is seen to strangle the render speed when many metrics are present.
80  int lowUpdateRate = 100;
81  mLocalModified = false;
82  mDelayedUpdateTimer = new QTimer(this);
83  connect(mDelayedUpdateTimer, SIGNAL(timeout()), this, SLOT(delayedUpdate())); // this signal will be executed in the thread of THIS, i.e. the main thread.
84  mDelayedUpdateTimer->start(lowUpdateRate);
85 
86 
87  mModifiedCount = 0;
88  mPaintCount = 0;
89  mMetricManager.reset(new MetricManager());
90  connect(mMetricManager.get(), SIGNAL(activeMetricChanged()), this, SLOT(setModified()));
91  connect(mMetricManager.get(), SIGNAL(metricsChanged()), this, SLOT(setModified()));
92 
93  //table widget
94  connect(mTable, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged()));
95  connect(mTable, SIGNAL(cellChanged(int, int)), this, SLOT(cellChangedSlot(int, int)));
96  connect(mTable, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
97 
98  this->setLayout(mVerticalLayout);
99 
100  mEditWidgets = new QStackedWidget;
101 
102  QActionGroup* group = new QActionGroup(this);
103  this->createActions(group);
104 
105 // QToolBar* toolBar = new QToolBar("actions", this);
106 // toolBar->addActions(group->actions());
107 
108  QWidget* toolBar = new QWidget(this);
109  QHBoxLayout* toolLayout = new QHBoxLayout(toolBar);
110  toolLayout->setMargin(0);
111  toolLayout->setSpacing(0);
112  QList<QAction*> actions = group->actions();
113  for (unsigned i=0; i<actions.size(); ++i)
114  {
115  if (actions[i]->isSeparator())
116  {
117  toolLayout->addSpacing(4);
118  QFrame* frame = new QFrame();
119  frame->setFrameStyle(QFrame::Sunken + QFrame::VLine);
120  toolLayout->addWidget(frame);
121  toolLayout->addSpacing(4);
122  }
123  else
124  {
126  button->setDefaultAction(actions[i]);
127  button->setIconSize(QSize(32,32));
128  toolLayout->addWidget(button);
129  }
130  }
131 
132  QHBoxLayout* buttonLayout = new QHBoxLayout;
133  buttonLayout->addWidget(toolBar);
134  buttonLayout->addStretch();
135 
136  //layout
137  mVerticalLayout->addLayout(buttonLayout);
138  mVerticalLayout->addWidget(mTable, 1);
139  mVerticalLayout->addWidget(mEditWidgets, 0);
140 }
141 
143 {}
144 
145 void MetricWidget::createActions(QActionGroup* group)
146 {
147  mPointMetricAction = this->createAction(group, ":/icons/metric_point.png", "Pt", "Create a new Point Metric", SLOT(addPointButtonClickedSlot()));
148  mFrameMetricAction = this->createAction(group, ":/icons/metric_frame.png", "Frame", "Create a new Frame Metric (position and orientation)", SLOT(addFrameButtonClickedSlot()));
149  mToolMetricAction = this->createAction(group, ":/icons/metric_tool.png", "Tool", "Create a new Tool Metric", SLOT(addToolButtonClickedSlot()));
150  this->createAction(group, ":/icons/metric_distance.png", "Dist", "Create a new Distance Metric", SLOT(addDistanceButtonClickedSlot()));
151  this->createAction(group, ":/icons/metric_angle.png", "Angle", "Create a new Angle Metric", SLOT(addAngleButtonClickedSlot()));
152  this->createAction(group, ":/icons/metric_plane.png", "Plane", "Create a new Plane Metric", SLOT(addPlaneButtonClickedSlot()));
153  this->createAction(group, ":/icons/metric_sphere.png", "Sphere", "Create a new SphereMetric", SLOT(addSphereButtonClickedSlot()));
154  this->createAction(group, ":/icons/metric_torus.png", "Torus", "Create a new Torus Metric", SLOT(addDonutButtonClickedSlot()));
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 metrics to file", SLOT(exportMetricsButtonClickedSlot()));
163 }
164 
165 //template<class T>
166 QAction* MetricWidget::createAction(QActionGroup* group, QString iconName, QString text, QString tip, const char* slot)
167 {
168  QAction* action = new QAction(QIcon(iconName), text, group);
169  action->setStatusTip(tip);
170  action->setToolTip(tip);
171  if (slot)
172  {
173  connect(action, SIGNAL(triggered()), this, slot);
174  }
175  return action;
176 }
177 
179 {
180  return "<html>"
181  "<h3>Utility for sampling points in 3D</h3>"
182  "<p>Lets you sample points in 3D and get the distance between sampled points.</p>"
183  "<p><i></i></p>"
184  "</html>";
185 }
186 
187 void MetricWidget::cellChangedSlot(int row, int col)
188 {
189  if (col==0) // data name changed
190  {
191  QTableWidgetItem* item = mTable->item(row,col);
192  DataPtr data = patientService()->getData(item->data(Qt::UserRole).toString());
193  if (data)
194  data->setName(item->text());
195  }
196 
197 }
198 
199 void MetricWidget::cellClickedSlot(int row, int column)
200 {
201  if (row < 0 || column < 0)
202  return;
203 
204  QTableWidgetItem* item = mTable->item(row,column);
205  QString uid = item->data(Qt::UserRole).toString();
206  mMetricManager->moveToMetric(uid);
207 }
208 
210 {
211  QTableWidgetItem* item = mTable->currentItem();
212 
213  mMetricManager->setActiveUid(item->data(Qt::UserRole).toString());
214  mEditWidgets->setCurrentIndex(mTable->currentRow());
215 
216  mMetricManager->setSelection(this->getSelectedUids());
217 
218  enablebuttons();
219 }
220 
221 void MetricWidget::showEvent(QShowEvent* event)
222 {
223  QWidget::showEvent(event);
224  this->setModified();
225 }
226 
227 void MetricWidget::hideEvent(QHideEvent* event)
228 {
229  QWidget::hideEvent(event);
230 }
231 
232 namespace
233 {
234 template<class T, class SUPER>
235 boost::shared_ptr<T> castTo(boost::shared_ptr<SUPER> data)
236 {
237  return boost::dynamic_pointer_cast<T>(data);
238 }
239 
240 template<class T, class SUPER>
241 bool isType(boost::shared_ptr<SUPER> data)
242 {
243  return (castTo<T>(data) ? true : false);
244 }
245 
246 template<class WRAPPER, class METRIC, class SUPER>
247 boost::shared_ptr<WRAPPER> createMetricWrapperOfType(cx::VisualizationServicePtr visualizationService, cx::PatientModelServicePtr patientModelService, boost::shared_ptr<SUPER> data)
248 {
249  return boost::shared_ptr<WRAPPER>(new WRAPPER(visualizationService, patientModelService, castTo<METRIC>(data)));
250 }
251 }
252 
253 MetricBasePtr MetricWidget::createMetricWrapper(cx::VisualizationServicePtr visualizationService, cx::PatientModelServicePtr patientModelService, DataPtr data)
254 {
255  if (isType<PointMetric>(data))
256  return createMetricWrapperOfType<PointMetricWrapper, PointMetric>(visualizationService, patientModelService, data);
257  if (isType<DistanceMetric>(data))
258  return createMetricWrapperOfType<DistanceMetricWrapper, DistanceMetric>(visualizationService, patientModelService, data);
259  if (isType<AngleMetric>(data))
260  return createMetricWrapperOfType<AngleMetricWrapper, AngleMetric>(visualizationService, patientModelService, data);
261  if (isType<FrameMetric>(data))
262  return createMetricWrapperOfType<FrameMetricWrapper, FrameMetric>(visualizationService, patientModelService, data);
263  if (isType<ToolMetric>(data))
264  return createMetricWrapperOfType<ToolMetricWrapper, ToolMetric>(visualizationService, patientModelService, data);
265  if (isType<PlaneMetric>(data))
266  return createMetricWrapperOfType<PlaneMetricWrapper, PlaneMetric>(visualizationService, patientModelService, data);
267  if (isType<DonutMetric>(data))
268  return createMetricWrapperOfType<DonutMetricWrapper, DonutMetric>(visualizationService, patientModelService, data);
269  if (isType<SphereMetric>(data))
270  return createMetricWrapperOfType<SphereMetricWrapper, SphereMetric>(visualizationService, patientModelService, data);
271 
272  return MetricBasePtr();
273 }
274 
278 std::vector<MetricBasePtr> MetricWidget::createMetricWrappers(cx::VisualizationServicePtr visualizationService, cx::PatientModelServicePtr patientModelService)
279 {
280  std::vector<MetricBasePtr> retval;
281  std::map<QString, DataPtr> all = patientService()->getData();
282  for (std::map<QString, DataPtr>::iterator iter=all.begin(); iter!=all.end(); ++iter)
283  {
284  MetricBasePtr wrapper = this->createMetricWrapper(visualizationService, patientModelService, iter->second);
285  if (wrapper)
286  {
287  retval.push_back(wrapper);
288  }
289  }
290  return retval;
291 }
292 
293 
294 void MetricWidget::prePaintEvent()
295 {
296 // QTime timer;
297 // timer.start();
298  mPaintCount++;
299  std::vector<MetricBasePtr> newMetrics = this->createMetricWrappers(mVisualizationService, mPatientModelService);
300 
301  bool rebuild = !this->checkEqual(newMetrics, mMetrics);
302  if (rebuild)
303  {
304  this->resetWrappersAndEditWidgets(newMetrics);
305  this->initializeTable();
306  }
307 
308  this->updateMetricWrappers();
309  this->updateTableContents();
310 
311  if (rebuild)
312  {
313  this->expensizeColumnResize();
314  }
315 
316  this->enablebuttons();
317 // std::cout << QString("prepaint, mod=%1, paint=%2, elapsed=%3ms").arg(mModifiedCount).arg(mPaintCount).arg(timer.elapsed()) << std::endl;
318 // std::cout << QString("prepaint, mod=%1, paint=%2").arg(mModifiedCount).arg(mPaintCount) << std::endl;
319 }
320 
321 void MetricWidget::expensizeColumnResize()
322 {
323  int valueColumn = 1;
324  mTable->resizeColumnToContents(valueColumn);
325 }
326 
327 void MetricWidget::initializeTable()
328 {
329  mTable->blockSignals(true);
330 
331  mTable->clear();
332 
333  mTable->setRowCount(mMetrics.size());
334  mTable->setColumnCount(4);
335  QStringList headerItems(QStringList() << "Name" << "Value" << "Arguments" << "Type");
336  mTable->setHorizontalHeaderLabels(headerItems);
337 // mTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); // dangerous: uses lots of painting time
338  mTable->setSelectionBehavior(QAbstractItemView::SelectRows);
339  mTable->verticalHeader()->hide();
340 
341  for (unsigned i = 0; i < mMetrics.size(); ++i)
342  {
343  MetricBasePtr current = mMetrics[i];
344 
345  for (unsigned j = 0; j < 4; ++j)
346  {
347  QTableWidgetItem* item = new QTableWidgetItem("empty");
348  item->setData(Qt::UserRole, current->getData()->getUid());
349  mTable->setItem(i, j, item);
350  }
351  }
352  mTable->blockSignals(false);
353 }
354 
355 void MetricWidget::updateMetricWrappers()
356 {
357  for (unsigned i = 0; i < mMetrics.size(); ++i)
358  {
359  mMetrics[i]->update();
360  }
361 }
362 
363 void MetricWidget::updateTableContents()
364 {
365  mTable->blockSignals(true);
366  // update contents:
367  QTime timer;
368  timer.start();
369  for (unsigned i = 0; i < mMetrics.size(); ++i)
370  {
371  MetricBasePtr current = mMetrics[i];
372  QString name = current->getData()->getName();
373  QString value = current->getValue();
374  QString arguments = current->getArguments();
375  QString type = current->getType();
376  }
377 
378  for (unsigned i = 0; i < mMetrics.size(); ++i)
379  {
380  MetricBasePtr current = mMetrics[i];
381  if (!mTable->item(i,0))
382  {
383  std::cout << "no qitem for:: " << i << " " << current->getData()->getName() << std::endl;
384  continue;
385  }
386  QString name = current->getData()->getName();
387  QString value = current->getValue();
388  QString arguments = current->getArguments();
389  QString type = current->getType();
390 
391  mTable->item(i,0)->setText(name);
392  mTable->item(i,1)->setText(value);
393  mTable->item(i,2)->setText(arguments);
394  mTable->item(i,3)->setText(type);
395 
396  //highlight selected row
397  if (current->getData()->getUid() == mMetricManager->getActiveUid())
398  {
399  mTable->setCurrentCell(i,1);
400  mEditWidgets->setCurrentIndex(i);
401  }
402  }
403  mTable->blockSignals(false);
404 }
405 
407 {
408  mLocalModified = true;
409 // BaseWidget::setModified();
410  mModifiedCount++;
411 }
412 
414 {
415  if (!mLocalModified)
416  return;
418  mLocalModified = false;
419 
420 }
421 
422 void MetricWidget::resetWrappersAndEditWidgets(std::vector<MetricBasePtr> wrappers)
423 {
424  while (mEditWidgets->count())
425  {
426  mEditWidgets->removeWidget(mEditWidgets->widget(0));
427  }
428 
429  for (unsigned i=0; i<mMetrics.size(); ++i)
430  {
431  disconnect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
432  }
433 
434  mMetrics = wrappers;
435 
436  for (unsigned i=0; i<mMetrics.size(); ++i)
437  {
438  connect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
439  }
440 
441  for (unsigned i=0; i<mMetrics.size(); ++i)
442  {
443  MetricBasePtr wrapper = mMetrics[i];
444  QGroupBox* groupBox = new QGroupBox(wrapper->getData()->getName(), this);
445  groupBox->setFlat(true);
446  QVBoxLayout* gbLayout = new QVBoxLayout(groupBox);
447  gbLayout->setMargin(4);
448  gbLayout->addWidget(wrapper->createWidget());
449  mEditWidgets->addWidget(groupBox);
450  }
451 
452  mEditWidgets->setCurrentIndex(-1);
453 }
454 
455 bool MetricWidget::checkEqual(const std::vector<MetricBasePtr>& a, const std::vector<MetricBasePtr>& b) const
456 {
457  if (a.size()!=b.size())
458  return false;
459 
460  for (unsigned i=0; i<a.size(); ++i)
461  {
462  if (a[i]->getData()!=b[i]->getData())
463  return false;
464  }
465 
466  return true;
467 }
468 
469 void MetricWidget::enablebuttons()
470 {
471  mRemoveAction->setEnabled(!mMetricManager->getActiveUid().isEmpty());
472  mLoadReferencePointsAction->setEnabled(trackingService()->getReferenceTool() ? true : false);
473 }
474 
476 {
477  mMetricManager->loadReferencePointsSlot();
478 }
480 {
481  mMetricManager->addPointButtonClickedSlot();
482 }
484 {
485  mMetricManager->addFrameButtonClickedSlot();
486 }
488 {
489  mMetricManager->addToolButtonClickedSlot();
490 }
492 {
493  mMetricManager->addPlaneButtonClickedSlot();
494 }
496 {
497  mMetricManager->addAngleButtonClickedSlot();
498 }
500 {
501  mMetricManager->addDistanceButtonClickedSlot();
502 }
504 {
505  mMetricManager->addSphereButtonClickedSlot();
506 }
508 {
509  mMetricManager->addDonutButtonClickedSlot();
510 }
511 
512 std::set<QString> MetricWidget::getSelectedUids()
513 {
514  QList<QTableWidgetItem*> selection = mTable->selectedItems();
515 
516  std::set<QString> selectedUids;
517  for (int i=0; i<selection.size(); ++i)
518  {
519  selectedUids.insert(selection[i]->data(Qt::UserRole).toString());
520  }
521  return selectedUids;
522 }
523 
525 {
526  int nextIndex = mTable->currentRow() + 1;
527  QString nextUid;
528  if (nextIndex < mTable->rowCount())
529  {
530  QTableWidgetItem* nextItem = mTable->item(nextIndex, 0);
531  nextUid = nextItem->data(Qt::UserRole).toString();
532  }
533 
534  mPatientModelService->removeData(mMetricManager->getActiveUid());
535 
536  if (!nextUid.isEmpty())
537  mMetricManager->setActiveUid(nextUid);
538 }
539 
541 {
542  QString suggestion = QString("%1/Logs/metrics_%2.txt")
543  .arg(patientService()->getActivePatientFolder())
544  .arg(QDateTime::currentDateTime().toString(timestampSecondsFormat()));
545 
546  QString filename = QFileDialog::getSaveFileName(this,
547  "Create/select file to export metrics to",
548  suggestion);
549  if(!filename.isEmpty())
550  mMetricManager->exportMetricsToFile(filename);
551 }
552 
553 
554 
555 }//end namespace cx
QAction * mFrameMetricAction
virtual void setModified()
PatientModelServicePtr mPatientModelService
void removeButtonClickedSlot()
QString timestampSecondsFormat()
Definition: cxTime.cpp:39
QAction * mExportFramesAction
VisualizationServicePtr mVisualizationService
boost::shared_ptr< class Data > DataPtr
boost::shared_ptr< class MetricBase > MetricBasePtr
boost::shared_ptr< class VisualizationService > VisualizationServicePtr
Definition: cxRegServices.h:43
void addPointButtonClickedSlot()
void cellChangedSlot(int row, int col)
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
void addDonutButtonClickedSlot()
void addAngleButtonClickedSlot()
void exportMetricsButtonClickedSlot()
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:108
void addFrameButtonClickedSlot()
cxLogicManager_EXPORT PatientModelServicePtr patientService()
void addToolButtonClickedSlot()
MetricWidget(VisualizationServicePtr visualizationService, PatientModelServicePtr patientModelService, QWidget *parent)
void addPlaneButtonClickedSlot()
void loadReferencePointsSlot()
cxLogicManager_EXPORT TrackingServicePtr trackingService()
virtual QString defaultWhatsThis() const
Returns a short description of what this widget will do for you.
virtual void cellClickedSlot(int row, int column)
QAction * mPointMetricAction
void addDistanceButtonClickedSlot()
QAction * mToolMetricAction
void addSphereButtonClickedSlot()