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