CustusX  16.5.0-rc9
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 
72  BaseWidget(parent, "MetricWidget", "Metrics/3D ruler"),
73  mVerticalLayout(new QVBoxLayout(this)),
74  mTable(new QTableWidget(this)),
75  mPatientModelService(patientModelService),
76  mViewService(viewService)
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  this->setToolTip("3D measurements");
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 
178 void MetricWidget::cellChangedSlot(int row, int col)
179 {
180  if (col==0) // data name changed
181  {
182  QTableWidgetItem* item = mTable->item(row,col);
183  DataPtr data = patientService()->getData(item->data(Qt::UserRole).toString());
184  if (data)
185  data->setName(item->text());
186  }
187 
188 }
189 
190 void MetricWidget::cellClickedSlot(int row, int column)
191 {
192  if (row < 0 || column < 0)
193  return;
194 
195  QTableWidgetItem* item = mTable->item(row,column);
196  QString uid = item->data(Qt::UserRole).toString();
197  mMetricManager->moveToMetric(uid);
198 }
199 
201 {
202  QTableWidgetItem* item = mTable->currentItem();
203 
204  mMetricManager->setActiveUid(item->data(Qt::UserRole).toString());
205  mEditWidgets->setCurrentIndex(mTable->currentRow());
206 
207  mMetricManager->setSelection(this->getSelectedUids());
208 
209  enablebuttons();
210 }
211 
212 void MetricWidget::showEvent(QShowEvent* event)
213 {
214  QWidget::showEvent(event);
215  this->setModified();
216 }
217 
218 void MetricWidget::hideEvent(QHideEvent* event)
219 {
220  QWidget::hideEvent(event);
221 }
222 
223 namespace
224 {
225 template<class T, class SUPER>
226 boost::shared_ptr<T> castTo(boost::shared_ptr<SUPER> data)
227 {
228  return boost::dynamic_pointer_cast<T>(data);
229 }
230 
231 template<class T, class SUPER>
232 bool isType(boost::shared_ptr<SUPER> data)
233 {
234  return (castTo<T>(data) ? true : false);
235 }
236 
237 template<class WRAPPER, class METRIC, class SUPER>
238 boost::shared_ptr<WRAPPER> createMetricWrapperOfType(cx::ViewServicePtr viewService, cx::PatientModelServicePtr patientModelService, boost::shared_ptr<SUPER> data)
239 {
240  return boost::shared_ptr<WRAPPER>(new WRAPPER(viewService, patientModelService, castTo<METRIC>(data)));
241 }
242 }
243 
244 MetricBasePtr MetricWidget::createMetricWrapper(cx::ViewServicePtr viewService, cx::PatientModelServicePtr patientModelService, DataPtr data)
245 {
246  if (isType<PointMetric>(data))
247  return createMetricWrapperOfType<PointMetricWrapper, PointMetric>(viewService, patientModelService, data);
248  if (isType<DistanceMetric>(data))
249  return createMetricWrapperOfType<DistanceMetricWrapper, DistanceMetric>(viewService, patientModelService, data);
250  if (isType<AngleMetric>(data))
251  return createMetricWrapperOfType<AngleMetricWrapper, AngleMetric>(viewService, patientModelService, data);
252  if (isType<FrameMetric>(data))
253  return createMetricWrapperOfType<FrameMetricWrapper, FrameMetric>(viewService, patientModelService, data);
254  if (isType<ToolMetric>(data))
255  return createMetricWrapperOfType<ToolMetricWrapper, ToolMetric>(viewService, patientModelService, data);
256  if (isType<PlaneMetric>(data))
257  return createMetricWrapperOfType<PlaneMetricWrapper, PlaneMetric>(viewService, patientModelService, data);
258  if (isType<DonutMetric>(data))
259  return createMetricWrapperOfType<DonutMetricWrapper, DonutMetric>(viewService, patientModelService, data);
260  if (isType<SphereMetric>(data))
261  return createMetricWrapperOfType<SphereMetricWrapper, SphereMetric>(viewService, patientModelService, data);
262 
263  return MetricBasePtr();
264 }
265 
269 std::vector<MetricBasePtr> MetricWidget::createMetricWrappers(cx::ViewServicePtr viewService, cx::PatientModelServicePtr patientModelService)
270 {
271  std::vector<MetricBasePtr> retval;
272  std::map<QString, DataPtr> all = patientService()->getData();
273  for (std::map<QString, DataPtr>::iterator iter=all.begin(); iter!=all.end(); ++iter)
274  {
275  MetricBasePtr wrapper = this->createMetricWrapper(viewService, patientModelService, iter->second);
276  if (wrapper)
277  {
278  retval.push_back(wrapper);
279  }
280  }
281  return retval;
282 }
283 
284 
285 void MetricWidget::prePaintEvent()
286 {
287 // QTime timer;
288 // timer.start();
289  mPaintCount++;
290  std::vector<MetricBasePtr> newMetrics = this->createMetricWrappers(mViewService, mPatientModelService);
291 
292  bool rebuild = !this->checkEqual(newMetrics, mMetrics);
293  if (rebuild)
294  {
295  this->resetWrappersAndEditWidgets(newMetrics);
296  this->initializeTable();
297  }
298 
299  this->updateMetricWrappers();
300  this->updateTableContents();
301 
302  if (rebuild)
303  {
304  this->expensizeColumnResize();
305  }
306 
307  this->enablebuttons();
308 // std::cout << QString("prepaint, mod=%1, paint=%2, elapsed=%3ms").arg(mModifiedCount).arg(mPaintCount).arg(timer.elapsed()) << std::endl;
309 // std::cout << QString("prepaint, mod=%1, paint=%2").arg(mModifiedCount).arg(mPaintCount) << std::endl;
310 }
311 
312 void MetricWidget::expensizeColumnResize()
313 {
314  int valueColumn = 1;
315  mTable->resizeColumnToContents(valueColumn);
316 }
317 
318 void MetricWidget::initializeTable()
319 {
320  mTable->blockSignals(true);
321 
322  mTable->clear();
323 
324  mTable->setRowCount(mMetrics.size());
325  mTable->setColumnCount(4);
326  QStringList headerItems(QStringList() << "Name" << "Value" << "Arguments" << "Type");
327  mTable->setHorizontalHeaderLabels(headerItems);
328 // mTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); // dangerous: uses lots of painting time
329  mTable->setSelectionBehavior(QAbstractItemView::SelectRows);
330  mTable->verticalHeader()->hide();
331 
332  for (unsigned i = 0; i < mMetrics.size(); ++i)
333  {
334  MetricBasePtr current = mMetrics[i];
335 
336  for (unsigned j = 0; j < 4; ++j)
337  {
338  QTableWidgetItem* item = new QTableWidgetItem("empty");
339  item->setData(Qt::UserRole, current->getData()->getUid());
340  mTable->setItem(i, j, item);
341  }
342  }
343  mTable->blockSignals(false);
344 }
345 
346 void MetricWidget::updateMetricWrappers()
347 {
348  for (unsigned i = 0; i < mMetrics.size(); ++i)
349  {
350  mMetrics[i]->update();
351  }
352 }
353 
354 void MetricWidget::updateTableContents()
355 {
356  mTable->blockSignals(true);
357  // update contents:
358  QTime timer;
359  timer.start();
360  for (unsigned i = 0; i < mMetrics.size(); ++i)
361  {
362  MetricBasePtr current = mMetrics[i];
363  QString name = current->getData()->getName();
364  QString value = current->getValue();
365  QString arguments = current->getArguments();
366  QString type = current->getType();
367  }
368 
369  for (unsigned i = 0; i < mMetrics.size(); ++i)
370  {
371  MetricBasePtr current = mMetrics[i];
372  if (!mTable->item(i,0))
373  {
374  std::cout << "no qitem for:: " << i << " " << current->getData()->getName() << std::endl;
375  continue;
376  }
377  QString name = current->getData()->getName();
378  QString value = current->getValue();
379  QString arguments = current->getArguments();
380  QString type = current->getType();
381 
382  mTable->item(i,0)->setText(name);
383  mTable->item(i,1)->setText(value);
384  mTable->item(i,2)->setText(arguments);
385  mTable->item(i,3)->setText(type);
386 
387  //highlight selected row
388  if (current->getData()->getUid() == mMetricManager->getActiveUid())
389  {
390  mTable->setCurrentCell(i,1);
391  mEditWidgets->setCurrentIndex(i);
392  }
393  }
394  mTable->blockSignals(false);
395 }
396 
398 {
399  mLocalModified = true;
400 // BaseWidget::setModified();
401  mModifiedCount++;
402 }
403 
405 {
406  if (!mLocalModified)
407  return;
409  mLocalModified = false;
410 
411 }
412 
413 void MetricWidget::resetWrappersAndEditWidgets(std::vector<MetricBasePtr> wrappers)
414 {
415  while (mEditWidgets->count())
416  {
417  mEditWidgets->removeWidget(mEditWidgets->widget(0));
418  }
419 
420  for (unsigned i=0; i<mMetrics.size(); ++i)
421  {
422  disconnect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
423  }
424 
425  mMetrics = wrappers;
426 
427  for (unsigned i=0; i<mMetrics.size(); ++i)
428  {
429  connect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
430  }
431 
432  for (unsigned i=0; i<mMetrics.size(); ++i)
433  {
434  MetricBasePtr wrapper = mMetrics[i];
435  QGroupBox* groupBox = new QGroupBox(wrapper->getData()->getName(), this);
436  groupBox->setFlat(true);
437  QVBoxLayout* gbLayout = new QVBoxLayout(groupBox);
438  gbLayout->setMargin(4);
439  gbLayout->addWidget(wrapper->createWidget());
440  mEditWidgets->addWidget(groupBox);
441  }
442 
443  mEditWidgets->setCurrentIndex(-1);
444 }
445 
446 bool MetricWidget::checkEqual(const std::vector<MetricBasePtr>& a, const std::vector<MetricBasePtr>& b) const
447 {
448  if (a.size()!=b.size())
449  return false;
450 
451  for (unsigned i=0; i<a.size(); ++i)
452  {
453  if (a[i]->getData()!=b[i]->getData())
454  return false;
455  }
456 
457  return true;
458 }
459 
460 void MetricWidget::enablebuttons()
461 {
462  mRemoveAction->setEnabled(!mMetricManager->getActiveUid().isEmpty());
463  mLoadReferencePointsAction->setEnabled(trackingService()->getReferenceTool() ? true : false);
464 }
465 
467 {
468  mMetricManager->loadReferencePointsSlot();
469 }
471 {
472  mMetricManager->addPointButtonClickedSlot();
473 }
475 {
476  mMetricManager->addFrameButtonClickedSlot();
477 }
479 {
480  mMetricManager->addToolButtonClickedSlot();
481 }
483 {
484  mMetricManager->addPlaneButtonClickedSlot();
485 }
487 {
488  mMetricManager->addAngleButtonClickedSlot();
489 }
491 {
492  mMetricManager->addDistanceButtonClickedSlot();
493 }
495 {
496  mMetricManager->addSphereButtonClickedSlot();
497 }
499 {
500  mMetricManager->addDonutButtonClickedSlot();
501 }
502 
503 std::set<QString> MetricWidget::getSelectedUids()
504 {
505  QList<QTableWidgetItem*> selection = mTable->selectedItems();
506 
507  std::set<QString> selectedUids;
508  for (int i=0; i<selection.size(); ++i)
509  {
510  selectedUids.insert(selection[i]->data(Qt::UserRole).toString());
511  }
512  return selectedUids;
513 }
514 
516 {
517  int nextIndex = mTable->currentRow() + 1;
518  QString nextUid;
519  if (nextIndex < mTable->rowCount())
520  {
521  QTableWidgetItem* nextItem = mTable->item(nextIndex, 0);
522  nextUid = nextItem->data(Qt::UserRole).toString();
523  }
524 
525  mPatientModelService->removeData(mMetricManager->getActiveUid());
526 
527  if (!nextUid.isEmpty())
528  mMetricManager->setActiveUid(nextUid);
529 }
530 
532 {
533  QString suggestion = QString("%1/Logs/metrics_%2.txt")
534  .arg(patientService()->getActivePatientFolder())
535  .arg(QDateTime::currentDateTime().toString(timestampSecondsFormat()));
536 
537  QString filename = QFileDialog::getSaveFileName(this,
538  "Create/select file to export metrics to",
539  suggestion);
540  if(!filename.isEmpty())
541  mMetricManager->exportMetricsToFile(filename);
542 }
543 
544 
545 
546 }//end namespace cx
MetricWidget(ViewServicePtr viewService, PatientModelServicePtr patientModelService, QWidget *parent)
QAction * mFrameMetricAction
virtual void setModified()
PatientModelServicePtr mPatientModelService
void removeButtonClickedSlot()
boost::shared_ptr< class ViewService > ViewServicePtr
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)
ViewServicePtr mViewService
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
cxLogicManager_EXPORT ViewServicePtr viewService()
void addFrameButtonClickedSlot()
cxLogicManager_EXPORT PatientModelServicePtr patientService()
void addToolButtonClickedSlot()
void addPlaneButtonClickedSlot()
void loadReferencePointsSlot()
cxLogicManager_EXPORT TrackingServicePtr trackingService()
virtual void cellClickedSlot(int row, int column)
QAction * mPointMetricAction
void addDistanceButtonClickedSlot()
QAction * mToolMetricAction
void addSphereButtonClickedSlot()