CustusX  16.12
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 "cxTime.h"
56 #include "cxMetricManager.h"
57 #include "cxMetricUtilities.h"
58 
59 #include "cxPatientModelService.h"
60 
61 
62 namespace cx
63 {
64 
65 
66 //---------------------------------------------------------
67 //---------------------------------------------------------
68 //---------------------------------------------------------
69 
71  BaseWidget(parent, "metric_widget", "Metrics/3D ruler"),
72  mVerticalLayout(new QVBoxLayout(this)),
73  mTable(new QTableWidget(this)),
74  mPatientModelService(patientModelService),
75  mViewService(viewService)
76 {
77  // the delayed timer lowers the update rate of this widget,
78  // as is is seen to strangle the render speed when many metrics are present.
79  int lowUpdateRate = 100;
80  mLocalModified = false;
81  mDelayedUpdateTimer = new QTimer(this);
82  connect(mDelayedUpdateTimer, SIGNAL(timeout()), this, SLOT(delayedUpdate())); // this signal will be executed in the thread of THIS, i.e. the main thread.
83  mDelayedUpdateTimer->start(lowUpdateRate);
84  this->setToolTip("3D measurements");
85 
86  mModifiedCount = 0;
87  mPaintCount = 0;
88  mMetricManager.reset(new MetricManager());
89  connect(mMetricManager.get(), SIGNAL(activeMetricChanged()), this, SLOT(setModified()));
90  connect(mMetricManager.get(), SIGNAL(metricsChanged()), this, SLOT(setModified()));
91 
92  //table widget
93  connect(mTable, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged()));
94  connect(mTable, SIGNAL(cellChanged(int, int)), this, SLOT(cellChangedSlot(int, int)));
95  connect(mTable, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
96 
97  this->setLayout(mVerticalLayout);
98 
99  mEditWidgets = new QStackedWidget;
100 
101  QActionGroup* group = new QActionGroup(this);
102  this->createActions(group);
103 
104 // QToolBar* toolBar = new QToolBar("actions", this);
105 // toolBar->addActions(group->actions());
106 
107  QWidget* toolBar = new QWidget(this);
108  QHBoxLayout* toolLayout = new QHBoxLayout(toolBar);
109  toolLayout->setMargin(0);
110  toolLayout->setSpacing(0);
111  QList<QAction*> actions = group->actions();
112  for (unsigned i=0; i<actions.size(); ++i)
113  {
114  if (actions[i]->isSeparator())
115  {
116  toolLayout->addSpacing(4);
117  QFrame* frame = new QFrame();
118  frame->setFrameStyle(QFrame::Sunken + QFrame::VLine);
119  toolLayout->addWidget(frame);
120  toolLayout->addSpacing(4);
121  }
122  else
123  {
125  button->setDefaultAction(actions[i]);
126  button->setIconSize(QSize(32,32));
127  toolLayout->addWidget(button);
128  }
129  }
130 
131  QHBoxLayout* buttonLayout = new QHBoxLayout;
132  buttonLayout->addWidget(toolBar);
133  buttonLayout->addStretch();
134 
135  //layout
136  mVerticalLayout->addLayout(buttonLayout);
137  mVerticalLayout->addWidget(mTable, 1);
138  mVerticalLayout->addWidget(mEditWidgets, 0);
139 }
140 
142 {}
143 
144 void MetricWidget::createActions(QActionGroup* group)
145 {
146  mPointMetricAction = this->createAction(group, ":/icons/metric_point.png", "Pt", "Create a new Point Metric", SLOT(addPointButtonClickedSlot()));
147  mFrameMetricAction = this->createAction(group, ":/icons/metric_frame.png", "Frame", "Create a new Frame Metric (position and orientation)", SLOT(addFrameButtonClickedSlot()));
148  mToolMetricAction = this->createAction(group, ":/icons/metric_tool.png", "Tool", "Create a new Tool Metric", SLOT(addToolButtonClickedSlot()));
149  this->createAction(group, ":/icons/metric_distance.png", "Dist", "Create a new Distance Metric", SLOT(addDistanceButtonClickedSlot()));
150  this->createAction(group, ":/icons/metric_angle.png", "Angle", "Create a new Angle Metric", SLOT(addAngleButtonClickedSlot()));
151  this->createAction(group, ":/icons/metric_plane.png", "Plane", "Create a new Plane Metric", SLOT(addPlaneButtonClickedSlot()));
152  this->createAction(group, ":/icons/metric_sphere.png", "Sphere", "Create a new Sphere Metric", SLOT(addSphereButtonClickedSlot()));
153  this->createAction(group, ":/icons/metric_torus.png", "Torus", "Create a new Torus Metric", SLOT(addDonutButtonClickedSlot()));
154  this->createAction(group, ":/icons/metric_custom.png", "Custom", "Create a new Custom Metric", SLOT(addCustomButtonClickedSlot()));
155  this->createAction(group, ":/icons/metric.png", "ROI", "Create a new Region of Interest Metric", SLOT(addROIButtonClickedSlot()));
156 
157  this->createAction(group, "", "", "", NULL)->setSeparator(true);
158  mRemoveAction = this->createAction(group, ":/icons/metric_remove.png", "Remove", "Remove currently selected metric", SLOT(removeButtonClickedSlot()));
159  mRemoveAction->setDisabled(true);
160  mLoadReferencePointsAction = this->createAction(group, ":/icons/metric_reference.png", "Import", "Import reference points from reference tool", SLOT(loadReferencePointsSlot()));
161  mLoadReferencePointsAction->setDisabled(true);
162  this->createAction(group, "", "", "", NULL)->setSeparator(true);
163  mExportFramesAction = this->createAction(group, ":/icons/save.png", "ExportFrames", "Export metrics to file", SLOT(exportMetricsButtonClickedSlot()));
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 = patientService()->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(mViewService, mPatientModelService);
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(trackingService()->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  mPatientModelService->removeData(mMetricManager->getActiveUid());
468 
469  if (!nextUid.isEmpty())
470  mMetricManager->setActiveUid(nextUid);
471 }
472 
474 {
475  QString suggestion = QString("%1/Logs/metrics_%2.txt")
476  .arg(patientService()->getActivePatientFolder())
477  .arg(QDateTime::currentDateTime().toString(timestampSecondsFormat()));
478 
479  QString filename = QFileDialog::getSaveFileName(this,
480  "Create/select file to export metrics to",
481  suggestion);
482  if(!filename.isEmpty())
483  mMetricManager->exportMetricsToFile(filename);
484 }
485 
486 
487 
488 }//end namespace cx
void addCustomButtonClickedSlot()
MetricWidget(ViewServicePtr viewService, PatientModelServicePtr patientModelService, QWidget *parent)
QAction * mFrameMetricAction
virtual void setModified()
void addROIButtonClickedSlot()
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()