CustusX  2023.01.05-dev+develop.0da12
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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 /*
13  * cxMetricWidget.cpp
14  *
15  * \date Jul 5, 2011
16  * \author christiana
17  */
18 
19 #include <cxMetricWidget.h>
20 
21 #include <QTreeWidget>
22 #include <QTreeWidgetItem>
23 #include <QStringList>
24 #include <QVBoxLayout>
25 #include <QHeaderView>
26 
27 
28 #include "cxTypeConversions.h"
30 #include "cxTrackingService.h"
32 #include "cxVector3DWidget.h"
33 #include "cxTimeKeeper.h"
34 #include "cxTime.h"
35 #include "cxMetricManager.h"
36 #include "cxMetricUtilities.h"
37 #include "cxPatientModelService.h"
38 #include "cxVisServices.h"
39 
40 
41 namespace cx
42 {
43 
44 
45 //---------------------------------------------------------
46 //---------------------------------------------------------
47 //---------------------------------------------------------
48 
49 MetricWidget::MetricWidget(VisServicesPtr services, QWidget* parent) :
50  BaseWidget(parent, "metric_widget", "Metrics/3D ruler"),
51  mVerticalLayout(new QVBoxLayout(this)),
52  mTable(new QTableWidget(this)),
53  mServices(services)
54 {
55  // the delayed timer lowers the update rate of this widget,
56  // as is is seen to strangle the render speed when many metrics are present.
57  int lowUpdateRate = 100;
58  mLocalModified = false;
59  mDelayedUpdateTimer = new QTimer(this);
60  connect(mDelayedUpdateTimer, SIGNAL(timeout()), this, SLOT(delayedUpdate())); // this signal will be executed in the thread of THIS, i.e. the main thread.
61  mDelayedUpdateTimer->start(lowUpdateRate);
62  this->setToolTip("3D measurements");
63 
64  mModifiedCount = 0;
65  mPaintCount = 0;
66  mMetricManager.reset(new MetricManager(services->view(), services->patient(), services->tracking(), services->spaceProvider(), services->file()));
67  connect(mMetricManager.get(), SIGNAL(activeMetricChanged()), this, SLOT(setModified()));
68  connect(mMetricManager.get(), SIGNAL(metricsChanged()), this, SLOT(setModified()));
69 
70  //table widget
71  connect(mTable, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged()));
72  connect(mTable, SIGNAL(cellChanged(int, int)), this, SLOT(cellChangedSlot(int, int)));
73  connect(mTable, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
74 
75  this->setLayout(mVerticalLayout);
76 
77  mEditWidgets = new QStackedWidget;
78 
79  QActionGroup* group = new QActionGroup(this);
80  this->createActions(group);
81 
82 // QToolBar* toolBar = new QToolBar("actions", this);
83 // toolBar->addActions(group->actions());
84 
85  QWidget* toolBar = new QWidget(this);
86  QHBoxLayout* toolLayout = new QHBoxLayout(toolBar);
87  toolLayout->setMargin(0);
88  toolLayout->setSpacing(0);
89  QList<QAction*> actions = group->actions();
90  for (unsigned i=0; i<actions.size(); ++i)
91  {
92  if (actions[i]->isSeparator())
93  {
94  toolLayout->addSpacing(4);
95  QFrame* frame = new QFrame();
96  frame->setFrameStyle(QFrame::Sunken + QFrame::VLine);
97  toolLayout->addWidget(frame);
98  toolLayout->addSpacing(4);
99  }
100  else
101  {
103  button->setDefaultAction(actions[i]);
104  button->setIconSize(QSize(32,32));
105  toolLayout->addWidget(button);
106  }
107  }
108 
109  QHBoxLayout* buttonLayout = new QHBoxLayout;
110  buttonLayout->addWidget(toolBar);
111  buttonLayout->addStretch();
112 
113  //layout
114  mVerticalLayout->addLayout(buttonLayout);
115  mVerticalLayout->addWidget(mTable, 1);
116  mVerticalLayout->addWidget(mEditWidgets, 0);
117 }
118 
120 {}
121 
122 void MetricWidget::createActions(QActionGroup* group)
123 {
124  mPointMetricAction = this->createAction(group, ":/icons/metric_point.png", "Pt", "Create a new Point Metric", SLOT(addPointButtonClickedSlot()));
125  mFrameMetricAction = this->createAction(group, ":/icons/metric_frame.png", "Frame", "Create a new Frame Metric (position and orientation)", SLOT(addFrameButtonClickedSlot()));
126  mToolMetricAction = this->createAction(group, ":/icons/metric_tool.png", "Tool", "Create a new Tool Metric", SLOT(addToolButtonClickedSlot()));
127  this->createAction(group, ":/icons/metric_distance.png", "Dist", "Create a new Distance Metric", SLOT(addDistanceButtonClickedSlot()));
128  this->createAction(group, ":/icons/metric_angle.png", "Angle", "Create a new Angle Metric", SLOT(addAngleButtonClickedSlot()));
129  this->createAction(group, ":/icons/metric_plane.png", "Plane", "Create a new Plane Metric", SLOT(addPlaneButtonClickedSlot()));
130  this->createAction(group, ":/icons/metric_sphere.png", "Sphere", "Create a new Sphere Metric", SLOT(addSphereButtonClickedSlot()));
131  this->createAction(group, ":/icons/metric_torus.png", "Torus", "Create a new Torus Metric", SLOT(addDonutButtonClickedSlot()));
132  this->createAction(group, ":/icons/metric_custom.png", "Custom", "Create a new Custom Metric", SLOT(addCustomButtonClickedSlot()));
133  this->createAction(group, ":/icons/metric.png", "ROI", "Create a new Region of Interest Metric", SLOT(addROIButtonClickedSlot()));
134 
135  this->createAction(group, "", "", "", NULL)->setSeparator(true);
136  mRemoveAction = this->createAction(group, ":/icons/metric_remove.png", "Remove", "Remove currently selected metric", SLOT(removeButtonClickedSlot()));
137  mRemoveAction->setDisabled(true);
138  mLoadReferencePointsAction = this->createAction(group, ":/icons/metric_reference.png", "Import", "Import reference points from reference tool", SLOT(loadReferencePointsSlot()));
139  mLoadReferencePointsAction->setDisabled(true);
140  this->createAction(group, "", "", "", NULL)->setSeparator(true);
141  mExportFramesAction = this->createAction(group, ":/icons/save.png", "ExportFrames", "Export the metrics to a file", SLOT(exportMetricsButtonClickedSlot()));
142  mImportFramesAction = this->createAction(group, ":/icons/open.png", "ImportFrames", "Import metrics from a file (can be a patient file)", SLOT(importMetricsButtonClickedSlot()));
143 }
144 
145 //template<class T>
146 QAction* MetricWidget::createAction(QActionGroup* group, QString iconName, QString text, QString tip, const char* slot)
147 {
148  QAction* action = new QAction(QIcon(iconName), text, group);
149  action->setStatusTip(tip);
150  action->setToolTip(tip);
151  if (slot)
152  {
153  connect(action, SIGNAL(triggered()), this, slot);
154  }
155  return action;
156 }
157 
158 void MetricWidget::cellChangedSlot(int row, int col)
159 {
160  if (col==0) // data name changed
161  {
162  QTableWidgetItem* item = mTable->item(row,col);
163  DataPtr data = mServices->patient()->getData(item->data(Qt::UserRole).toString());
164  if (data)
165  data->setName(item->text());
166  }
167 
168 }
169 
170 void MetricWidget::cellClickedSlot(int row, int column)
171 {
172  if (row < 0 || column < 0)
173  return;
174 
175  QTableWidgetItem* item = mTable->item(row,column);
176  QString uid = item->data(Qt::UserRole).toString();
177  mMetricManager->moveToMetric(uid);
178 }
179 
181 {
182  QTableWidgetItem* item = mTable->currentItem();
183 
184  mMetricManager->setActiveUid(item->data(Qt::UserRole).toString());
185  mEditWidgets->setCurrentIndex(mTable->currentRow());
186 
187  mMetricManager->setSelection(this->getSelectedUids());
188 
189  enablebuttons();
190 }
191 
192 void MetricWidget::showEvent(QShowEvent* event)
193 {
194  QWidget::showEvent(event);
195  this->setModified();
196 }
197 
198 void MetricWidget::hideEvent(QHideEvent* event)
199 {
200  QWidget::hideEvent(event);
201 }
202 
203 void MetricWidget::prePaintEvent()
204 {
205  // QTime timer;
206  // timer.start();
207  mPaintCount++;
208 
209  MetricUtilities utilities(mServices);
210 
211  std::vector<MetricBasePtr> newMetrics = utilities.createMetricWrappers();
212 
213  bool rebuild = !this->checkEqual(newMetrics, mMetrics);
214  if (rebuild)
215  {
216  this->resetWrappersAndEditWidgets(newMetrics);
217  this->initializeTable();
218  }
219 
220  this->updateMetricWrappers();
221  this->updateTableContents();
222 
223  if (rebuild)
224  {
225  this->expensizeColumnResize();
226  }
227 
228  this->enablebuttons();
229 // std::cout << QString("prepaint, mod=%1, paint=%2, elapsed=%3ms").arg(mModifiedCount).arg(mPaintCount).arg(timer.elapsed()) << std::endl;
230 // std::cout << QString("prepaint, mod=%1, paint=%2").arg(mModifiedCount).arg(mPaintCount) << std::endl;
231 }
232 
233 void MetricWidget::expensizeColumnResize()
234 {
235  int valueColumn = 1;
236  mTable->resizeColumnToContents(valueColumn);
237 }
238 
239 void MetricWidget::initializeTable()
240 {
241  mTable->blockSignals(true);
242 
243  mTable->clear();
244 
245  mTable->setRowCount(mMetrics.size());
246  mTable->setColumnCount(4);
247  QStringList headerItems(QStringList() << "Name" << "Value" << "Arguments" << "Type");
248  mTable->setHorizontalHeaderLabels(headerItems);
249 // mTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); // dangerous: uses lots of painting time
250  mTable->setSelectionBehavior(QAbstractItemView::SelectRows);
251  mTable->verticalHeader()->hide();
252 
253  for (unsigned i = 0; i < mMetrics.size(); ++i)
254  {
255  MetricBasePtr current = mMetrics[i];
256 
257  for (unsigned j = 0; j < 4; ++j)
258  {
259  QTableWidgetItem* item = new QTableWidgetItem("empty");
260  item->setData(Qt::UserRole, current->getData()->getUid());
261  mTable->setItem(i, j, item);
262  }
263  }
264  mTable->blockSignals(false);
265 }
266 
267 void MetricWidget::updateMetricWrappers()
268 {
269  for (unsigned i = 0; i < mMetrics.size(); ++i)
270  {
271  mMetrics[i]->update();
272  }
273 }
274 
275 void MetricWidget::updateTableContents()
276 {
277  mTable->blockSignals(true);
278  // update contents:
279  QTime timer;
280  timer.start();
281 
282  for (unsigned i = 0; i < mMetrics.size(); ++i)
283  {
284  MetricBasePtr current = mMetrics[i];
285  if (!mTable->item(i,0))
286  {
287  std::cout << "no qitem for:: " << i << " " << current->getData()->getName() << std::endl;
288  continue;
289  }
290  QString name = current->getData()->getName();
291  QString value = current->getValue();
292  QString arguments = current->getArguments();
293  QString type = current->getType();
294 
295  mTable->item(i,0)->setText(name);
296  mTable->item(i,1)->setText(value);
297  mTable->item(i,2)->setText(arguments);
298  mTable->item(i,3)->setText(type);
299 
300  //highlight selected row
301  if (current->getData()->getUid() == mMetricManager->getActiveUid())
302  {
303  mTable->setCurrentCell(i,1);
304  mEditWidgets->setCurrentIndex(i);
305  }
306  }
307  mTable->blockSignals(false);
308 }
309 
311 {
312  mLocalModified = true;
313 // BaseWidget::setModified();
314  mModifiedCount++;
315 }
316 
318 {
319  if (!mLocalModified)
320  return;
322  mLocalModified = false;
323 
324 }
325 
326 void MetricWidget::resetWrappersAndEditWidgets(std::vector<MetricBasePtr> wrappers)
327 {
328  while (mEditWidgets->count())
329  {
330  mEditWidgets->removeWidget(mEditWidgets->widget(0));
331  }
332 
333  for (unsigned i=0; i<mMetrics.size(); ++i)
334  {
335  disconnect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
336  }
337 
338  mMetrics = wrappers;
339 
340  for (unsigned i=0; i<mMetrics.size(); ++i)
341  {
342  connect(mMetrics[i]->getData().get(), SIGNAL(transformChanged()), this, SLOT(setModified()));
343  }
344 
345  for (unsigned i=0; i<mMetrics.size(); ++i)
346  {
347  MetricBasePtr wrapper = mMetrics[i];
348  QGroupBox* groupBox = new QGroupBox(wrapper->getData()->getName(), this);
349  groupBox->setFlat(true);
350  QVBoxLayout* gbLayout = new QVBoxLayout(groupBox);
351  gbLayout->setMargin(4);
352  gbLayout->addWidget(wrapper->createWidget());
353  mEditWidgets->addWidget(groupBox);
354  }
355 
356  mEditWidgets->setCurrentIndex(-1);
357 }
358 
359 bool MetricWidget::checkEqual(const std::vector<MetricBasePtr>& a, const std::vector<MetricBasePtr>& b) const
360 {
361  if (a.size()!=b.size())
362  return false;
363 
364  for (unsigned i=0; i<a.size(); ++i)
365  {
366  if (a[i]->getData()!=b[i]->getData())
367  return false;
368  }
369 
370  return true;
371 }
372 
373 void MetricWidget::enablebuttons()
374 {
375  mRemoveAction->setEnabled(!mMetricManager->getActiveUid().isEmpty());
376  mLoadReferencePointsAction->setEnabled(mServices->tracking()->getReferenceTool() ? true : false);
377 }
378 
380 {
381  mMetricManager->loadReferencePointsSlot();
382 }
384 {
385  mMetricManager->addPointButtonClickedSlot();
386 }
388 {
389  mMetricManager->addFrameButtonClickedSlot();
390 }
392 {
393  mMetricManager->addToolButtonClickedSlot();
394 }
396 {
397  mMetricManager->addPlaneButtonClickedSlot();
398 }
400 {
401  mMetricManager->addAngleButtonClickedSlot();
402 }
404 {
405  mMetricManager->addDistanceButtonClickedSlot();
406 }
408 {
409  mMetricManager->addROIButtonClickedSlot();
410 }
412 {
413  mMetricManager->addSphereButtonClickedSlot();
414 }
416 {
417  mMetricManager->addDonutButtonClickedSlot();
418 }
420 {
421  mMetricManager->addCustomButtonClickedSlot();
422 }
423 
424 std::set<QString> MetricWidget::getSelectedUids()
425 {
426  QList<QTableWidgetItem*> selection = mTable->selectedItems();
427 
428  std::set<QString> selectedUids;
429  for (int i=0; i<selection.size(); ++i)
430  {
431  selectedUids.insert(selection[i]->data(Qt::UserRole).toString());
432  }
433  return selectedUids;
434 }
435 
437 {
438  int nextIndex = mTable->currentRow() + 1;
439  QString nextUid;
440  if (nextIndex < mTable->rowCount())
441  {
442  QTableWidgetItem* nextItem = mTable->item(nextIndex, 0);
443  nextUid = nextItem->data(Qt::UserRole).toString();
444  }
445 
446  mServices->patient()->removeData(mMetricManager->getActiveUid());
447 
448  if (!nextUid.isEmpty())
449  mMetricManager->setActiveUid(nextUid);
450 }
451 
453 {
454  QString suggestion = QString("%1/Logs/metrics_%2.xml")
455  .arg(mServices->patient()->getActivePatientFolder())
456  .arg(QDateTime::currentDateTime().toString(timestampSecondsFormat()));
457 
458  QString filename = QFileDialog::getSaveFileName(this,
459  "Select the file to export the metrics to",
460  suggestion);
461  if(!filename.isEmpty())
462  mMetricManager->exportMetricsToXMLFile(filename);
463 }
464 
466 {
467  QString suggestion = QString("%1/Logs/")
468  .arg(mServices->patient()->getActivePatientFolder());
469 
470  QString fileName = QFileDialog::getOpenFileName(this,
471  "Select the file to import metrics from (can be a patient file)",
472  suggestion,
473  "XML file (*.xml)");
474 
475  if(!fileName.isEmpty() && fileName.endsWith(".xml"))
476  mMetricManager->importMetricsFromXMLFile(fileName);
477 }
478 
479 
480 
481 }//end namespace cx
void addCustomButtonClickedSlot()
QAction * mFrameMetricAction
virtual void setModified()
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
void addROIButtonClickedSlot()
void removeButtonClickedSlot()
std::vector< MetricBasePtr > createMetricWrappers()
QString timestampSecondsFormat()
Definition: cxTime.cpp:18
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:88
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.