CustusX  22.04-rc5
An IGT application
cxRegistrationHistoryWidget.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 
13 
14 #include <QTreeWidget>
15 #include <QTreeWidgetItem>
16 #include <QStringList>
17 #include <QVBoxLayout>
18 #include "cxImage.h"
19 
20 #include "cxTime.h"
21 #include "cxTypeConversions.h"
22 #include "cxPatientModelService.h"
23 #include "cxLogger.h"
24 #include "cxTrackingService.h"
25 
26 namespace cx
27 {
28 RegistrationHistoryWidget::RegistrationHistoryWidget(RegServicesPtr servicesPtr, QWidget* parent, bool compact) :
29  BaseWidget(parent, "registration_history_widget", "Registration History"),
30  mServices(servicesPtr)
31 {
32  this->setWhatsThis(this->defaultWhatsThis());
33  this->setToolTip(this->defaultWhatsThis());
34 
35  mGroup = new QFrame;
36  mTextEdit = NULL;
37 
38  QVBoxLayout* toptopLayout = new QVBoxLayout(this);
39  QHBoxLayout* topLayout = new QHBoxLayout;
40  this->createControls(topLayout);
41 
42  toptopLayout->addWidget(mGroup);
43  mGroup->setLayout(topLayout);
44  if (!compact)
45  {
46  mTextEdit = new QTextEdit;
47  mTextEdit->setLineWrapMode(QTextEdit::NoWrap);
48 // mTextEdit->setVisible(true);
49  toptopLayout->addWidget(mTextEdit, 1);
50  toptopLayout->addStretch();
51  topLayout->addStretch();
52  }else
53  {
54  toptopLayout->setContentsMargins(QMargins(0,0,0,0));
55  topLayout->setContentsMargins(QMargins(0,0,0,0));
56  }
57 
58 }
59 
61 {
62 }
63 
65 {
66  return "<html>"
67  "<h3>Registration history.</h3>"
68  "<p>"
69  "Use the registration history to rewind the system to previous time. When history is rewinded, "
70  "all registrations performed after the active time is ignored by the system."
71  "</p>"
72  "<p>"
73  "<b>NB:</b> While a previous time is active, <em>no new registrations or adding of data</em> should be performed. "
74  "This will lead to undefined behaviour!</b>"
75  "</p>"
76  "</html>";
77 }
78 
79 void RegistrationHistoryWidget::createControls(QHBoxLayout* layout)
80 {
81  mRemoveAction = this->createAction(layout, ":/icons/open_icon_library/dialog-close.png", "Remove",
82  "Delete all registrations after the active time", SLOT(removeSlot()));
83 
84  mBehindLabel = new QLabel(this);
85  mBehindLabel->setToolTip("Number of registrations before the active time");
86  layout->addWidget(mBehindLabel);
87 
88  mRewindAction = this->createAction(layout, ":/icons/open_icon_library/arrow-left-3.png", "Rewind",
89  "One step back in registration history, changing active time.\nThis enables looking at a previous system state,\nbut take care to not add more registrations while this state.",
90  SLOT(rewindSlot()));
91 
92  mForwardAction = this->createAction(layout, ":/icons/open_icon_library/arrow-right-3.png",
93  "Rewind", "One step forward in registration history", SLOT(forwardSlot()));
94 
95  mInFrontLabel = new QLabel(this);
96  mInFrontLabel->setToolTip("Number of registrations after active time");
97  layout->addWidget(mInFrontLabel);
98 
99  mFastForwardAction = this->createAction(layout,
100  ":/icons/open_icon_library/arrow-right-double-3.png", "Fast Forward",
101  "Step to latest registration", SLOT(fastForwardSlot()));
102 }
103 
105 {
106  QWidget::showEvent(event);
107 
108  this->reconnectSlot();
109  connect(mServices->patient().get(), &PatientModelService::dataAddedOrRemoved, this, &RegistrationHistoryWidget::reconnectSlot);
110  connect(mServices->patient().get(), &PatientModelService::dataAddedOrRemoved, this, &RegistrationHistoryWidget::setModified);
111 
112  setModified();
113 }
114 
115 void RegistrationHistoryWidget::hideEvent(QCloseEvent* event)
116 {
117  QWidget::closeEvent(event);
118 
119  for (unsigned i = 0; i < mHistories.size(); ++i)
120  {
121  disconnect(mHistories[i].get(), &RegistrationHistory::currentChanged, this, &RegistrationHistoryWidget::setModified);
122  }
123  disconnect(mServices->patient().get(), &PatientModelService::dataAddedOrRemoved, this, &RegistrationHistoryWidget::reconnectSlot);
124  disconnect(mServices->patient().get(), &PatientModelService::dataAddedOrRemoved, this, &RegistrationHistoryWidget::setModified);
125 }
126 
128 {
129  for (unsigned i = 0; i < mHistories.size(); ++i)
130  {
131  disconnect(mHistories[i].get(), &RegistrationHistory::currentChanged, this, &RegistrationHistoryWidget::setModified);
132  }
133 
134  mHistories = this->getAllRegistrationHistories();
135 
136  for (unsigned i = 0; i < mHistories.size(); ++i)
137  {
139  }
140 }
141 
145 RegistrationHistoryWidget::TimeMap RegistrationHistoryWidget::generateRegistrationTimes()
146 {
147  TimeMap retval;
148 
149  std::vector<RegistrationHistoryPtr> allHistories = this->getAllRegistrationHistories();
150 
151  retval.insert(TimeMapPair(QDateTime(QDate(2000, 1, 1)),"initial"));
152 
153  for (unsigned i = 0; i < allHistories.size(); ++i)
154  {
155  std::vector<RegistrationTransform> current = allHistories[i]->getData();
156  for (unsigned j = 0; j < current.size(); ++j)
157  {
158  retval.insert(TimeMapPair(current[j].mTimestamp, QString("%1 [fixed=%2, moving=%3]").arg(current[j].mType).arg(current[j].mFixed).arg(
159  current[j].mMoving)));
160  }
161  std::vector<ParentSpace> frames = allHistories[i]->getParentSpaces();
162  for (unsigned j = 0; j < frames.size(); ++j)
163  {
164  if(retval.find(frames[j].mTimestamp) == retval.end())//Only print each frame one time
165  retval.insert(TimeMapPair(frames[j].mTimestamp, QString("%1 [spaceUid=%2]").arg(frames[j].mType).arg(frames[j].mUid)));
166  }
167  }
168 
169  retval.erase(QDateTime());
170 
171  return retval;
172 }
173 
174 RegistrationHistoryWidget::TimeMap::iterator RegistrationHistoryWidget::findActiveRegistration(TimeMap& times)
175 {
176  QDateTime activeTime = this->getActiveTime();
177 
178  if (!activeTime.isValid())
179  return times.end();
180 
181  RegistrationHistoryWidget::TimeMapIterators activeRegistrations = findActiveRegistrations(times);
182  if (!activeRegistrations.empty())
183  return activeRegistrations.back();
184  return times.end();
185 }
186 
187 RegistrationHistoryWidget::TimeMapIterators RegistrationHistoryWidget::findActiveRegistrations(TimeMap& times)
188 {
189  RegistrationHistoryWidget::TimeMapIterators retval;
190 
191  QDateTime activeTime = this->getActiveTime();
192 
193  if (!activeTime.isValid())
194  {
195  retval.push_back(times.end());
196  return retval;
197  }
198 
199  for (TimeMap::iterator iter = times.begin(); iter != times.end(); ++iter)
200  {
201  if(!retval.empty() && iter->first == retval[0]->first)
202  retval.push_back(iter);//Return all registrations at the same point in time
203  else if (retval.empty() && iter->first >= activeTime)
204  retval.push_back(iter);
205  }
206 
207  if(retval.empty())
208  retval.push_back(times.end());
209  return retval;
210 }
211 
212 QDateTime RegistrationHistoryWidget::getActiveTime()
213 {
214  std::vector<RegistrationHistoryPtr> raw = getAllRegistrationHistories();
215 
216  for (unsigned i = 0; i < raw.size(); ++i)
217  {
218  if (raw[i]->isNull())
219  continue;
220  return raw[i]->getActiveTime();
221  }
222 
223  return QDateTime();
224 }
225 
226 void RegistrationHistoryWidget::setActiveTime(QDateTime active)
227 {
228  std::vector<RegistrationHistoryPtr> raw = getAllRegistrationHistories();
229  for (unsigned i = 0; i < raw.size(); ++i)
230  {
231  raw[i]->setActiveTime(active);
232  }
233 }
234 
238 std::vector<RegistrationHistoryPtr> RegistrationHistoryWidget::getAllRegistrationHistories()
239 {
240  std::vector<RegistrationHistoryPtr> retval;
241  retval.push_back(mServices->patient()->get_rMpr_History());
242 
243  std::map<QString, DataPtr> data = mServices->patient()->getDatas();
244  for (std::map<QString, DataPtr>::iterator iter = data.begin(); iter != data.end(); ++iter)
245  {
246  retval.push_back(iter->second->get_rMd_History());
247  }
248 
249  return retval;
250 }
251 
255 {
256  debugDump();
257 
258  QDateTime active = this->getActiveTime();
259  if (!active.isValid()) // if invalid: we are already at head
260  return;
261 
262  report(
263  "Removing all registration performed later than " + active.toString(timestampSecondsFormatNice()) + ".");
264 
265  std::vector<RegistrationHistoryPtr> raw = getAllRegistrationHistories();
266  for (unsigned i = 0; i < raw.size(); ++i)
267  {
268  raw[i]->removeNewerThan(active);
269  }
270 
271  debugDump();
272 }
273 
274 std::vector<RegistrationTransform> RegistrationHistoryWidget::mergeHistory(const std::vector<RegistrationHistoryPtr>& allHistories)
275 {
276  std::vector<RegistrationTransform> history;
277  for (unsigned i = 0; i < allHistories.size(); ++i)
278  {
279  std::vector<RegistrationTransform> current = allHistories[i]->getData();
280  std::copy(current.begin(), current.end(), std::back_inserter(history));
281  }
282  std::sort(history.begin(), history.end());
283 
284  return history;
285 }
286 
291 {
292 
293  TimeMap registrationTimes = this->generateRegistrationTimes();
294 
295  if (registrationTimes.size() <= 1)
296  return;
297 
298  // current points to the timestamp currently in use. end() is current time.
299 // std::map<QDateTime, QString>::iterator current = this->findActiveRegistrations(times);
300  TimeMapIterators activeRegistrations = this->findActiveRegistrations(registrationTimes);
301  TimeMap::iterator activeRegistration = registrationTimes.end();
302  if (!activeRegistrations.empty())
303  activeRegistration = activeRegistrations.front();
304 
305  if (activeRegistration == registrationTimes.begin())
306  return;
307 
308  if (activeRegistration == registrationTimes.end())
309  --activeRegistration; // ignore the last entry
310 
311  --activeRegistration;
312  report(
313  "Rewind: Setting registration time to " + activeRegistration->first.toString(timestampSecondsFormatNice()) + ", ["
314  + activeRegistration->second + "]");
315  this->setActiveTime(activeRegistration->first);
316 }
317 
318 QString RegistrationHistoryWidget::debugDump()
319 {
320  TimeMap times = this->generateRegistrationTimes();
321  bool addedBreak = false;
322  std::stringstream ss;
323  ss << "<html>";
324  ss << "<p><i>";
325  if (!this->getActiveTime().isValid())
326  ss << "Active time: Current \n";
327  else
328  ss << "Active time: " << this->getActiveTime().toString(timestampSecondsFormatNice()) << "\n";
329  ss << "</i></p>";
330 
331  ss << "<p><span style=\"color:blue\">";
332  for (TimeMap::iterator iter = times.begin(); iter != times.end(); ++iter)
333  {
334  if (iter->first > this->getActiveTime() && !addedBreak && this->getActiveTime().isValid())
335  {
336  ss << "</span> <span style=\"color:gray\">";
337  addedBreak = true;
338  }
339  //else
340  {
341  ss << "<br />";
342  }
343  ss << iter->first.toString(timestampSecondsFormatNice()) << "\t" << iter->second;
344  }
345  ss << "</span></p>";
346 
347  return qstring_cast(ss.str());
348 }
349 
350 template<class T>
351 QAction* RegistrationHistoryWidget::createAction(QLayout* layout, QString iconName, QString text, QString tip, T slot)
352 {
353  QAction* action = new QAction(QIcon(iconName), text, this);
354  action->setToolTip(tip);
355  connect(action, SIGNAL(triggered()), this, slot);
356 
357  QToolButton* button = new QToolButton();
358  button->setDefaultAction(action);
359  layout->addWidget(button);
360 
361  return action;
362 }
363 
367 {
368  TimeMap registrationTimes = this->generateRegistrationTimes();
369 
370  if (registrationTimes.empty())
371  return;
372 
373  // current points to the timestamp currently in use. end() is current time.
374  TimeMapIterators activeRegistrations = this->findActiveRegistrations(registrationTimes);
375  TimeMap::iterator activeRegistration = registrationTimes.end();
376  if (!activeRegistrations.empty())
377  activeRegistration = activeRegistrations.back();
378 
379  if (activeRegistration == registrationTimes.end()) // already at end, ignore
380  return;
381  ++activeRegistration;
382 
383  if (activeRegistration == registrationTimes.end() || registrationTimes.rbegin()->first == activeRegistration->first) // if at end or at the last position, interpret as end
384  {
385  report("Forward: Setting registration time to current, [" + registrationTimes.rbegin()->second + "]");
386  this->setActiveTime(QDateTime());
387  }
388  else
389  {
390  report("Forward: Setting registration time to " + activeRegistration->first.toString(timestampSecondsFormatNice()) + ", ["+ activeRegistration->second + "]");
391  this->setActiveTime(activeRegistration->first);
392  }
393 }
394 
399 {
400  std::vector<RegistrationHistoryPtr> raw = getAllRegistrationHistories();
401  report("Fast Forward: Setting registration time to current.");
402 
403  for (unsigned i = 0; i < raw.size(); ++i)
404  {
405  raw[i]->setActiveTime(QDateTime());
406  }
407 }
408 
410 {
411  TimeMap times = this->generateRegistrationTimes();
412  std::map<QDateTime, QString>::iterator current = this->findActiveRegistration(times);
413  size_t behind = std::min<int>(distance(times.begin(), current), times.size() - 1);
414  size_t infront = times.size() - 1 - behind;
415 
416  mRewindAction->setText("Rewind (" + qstring_cast(behind) + ")");
417  mForwardAction->setText("Forward (" + qstring_cast(infront) + ")");
418 
419  mBehindLabel->setText("(" + qstring_cast(behind) + ")");
420  mInFrontLabel->setText("(" + qstring_cast(infront) + ")");
421 
422  mRewindAction->setEnabled(behind > 0);
423  mRemoveAction->setEnabled(infront != 0);
424  mForwardAction->setEnabled(infront != 0);
425  mFastForwardAction->setEnabled(infront != 0);
426 
427  QString color;
428  if (infront == 0)
429  {
430  color = "";
431  }
432  else
433  {
434  color = QString("QFrame {background: qradialgradient(cx:0.5, cy:0.5, radius: 0.5, fx:0.5, fy:0.5, stop:0 rgb(255,30,0), stop:0.8 rgb(255,50,0), stop:1 transparent) }");
435  color += QString("QLabel {background-color: transparent }");
436  }
437 
438  mGroup->setStyleSheet(color);
439 
440  if (mTextEdit)
441  mTextEdit->setText(debugDump());
442 }
443 
444 } //end namespace cx
QString qstring_cast(const T &val)
QString timestampSecondsFormatNice()
Definition: cxTime.cpp:26
virtual void hideEvent(QCloseEvent *event)
disconnects stuff
boost::shared_ptr< class RegServices > RegServicesPtr
Definition: cxRegServices.h:20
virtual void showEvent(QShowEvent *event)
updates internal info before showing the widget
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
void report(QString msg)
Definition: cxLogger.cpp:69
Namespace for all CustusX production code.