CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxPlaybackWidget.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 #include "cxPlaybackWidget.h"
34 
35 #include <QPainter>
36 #include <QToolTip>
37 #include <QMouseEvent>
38 #include <QLabel>
39 #include "cxTrackingService.h"
40 #include "cxHelperWidgets.h"
41 #include "cxTime.h"
42 #include "cxLogger.h"
43 
44 #include "cxTypeConversions.h"
45 #include "cxTimelineWidget.h"
46 #include "cxData.h"
48 #include "cxVideoService.h"
49 //#include "cxPlaybackUSAcquisitionVideo.h"
50 #include "cxSettings.h"
51 #include "cxPatientModelService.h"
52 
53 namespace cx
54 {
55 
57  BaseWidget(parent, "PlaybackWidget", "Playback")
58 {
59  mOpen = false;
60  this->setToolTip(this->defaultWhatsThis());
61 
62  mTimer.reset(new PlaybackTime());
63  mTimer->initialize(QDateTime::currentDateTime(), 100000);
64  connect(mTimer.get(), SIGNAL(changed()), SLOT(timeChangedSlot()));
65 
66  QVBoxLayout* topLayout = new QVBoxLayout(this);
67 
68  mStartTimeLabel = new QLabel;
69  topLayout->addWidget(mStartTimeLabel);
70  mTotalLengthLabel = new QLabel;
71  topLayout->addWidget(mTotalLengthLabel);
72  mLabel = new QLabel;
73  topLayout->addWidget(mLabel);
74 
75  mToolTimelineWidget = new TimelineWidget(this);
76  connect(mToolTimelineWidget, SIGNAL(positionChanged()), this, SLOT(timeLineWidgetValueChangedSlot()));
77  topLayout->addWidget(mToolTimelineWidget);
78 
79  // create buttons bar
80  QHBoxLayout* playButtonsLayout = new QHBoxLayout;
81  topLayout->addLayout(playButtonsLayout);
82 
83  mOpenAction = this->createAction(this,
84  QIcon(":/icons/open_icon_library/button-red.png"),
85  "Open Playback", "",
86  SLOT(toggleOpenSlot()),
87  playButtonsLayout);
88  this->createAction(this,
89  QIcon(":/icons/open_icon_library/media-seek-backward-3.png"),
90  "Rewind", "",
91  SLOT(rewindSlot()),
92  playButtonsLayout);
93  mPlayAction = this->createAction(this,
94  QIcon(":/icons/open_icon_library/media-playback-start-3.png"),
95  "Play", "",
96  SLOT(playSlot()),
97  playButtonsLayout);
98  this->createAction(this,
99  QIcon(":/icons/open_icon_library/media-seek-forward-3.png"),
100  "Forward", "",
101  SLOT(forwardSlot()),
102  playButtonsLayout);
103  this->createAction(this,
104  QIcon(":/icons/open_icon_library/media-playback-stop-3.png"),
105  "Stop", "",
106  SLOT(stopSlot()),
107  playButtonsLayout);
108  this->createAction(this,
109  QIcon(":/icons/open_icon_library/system-run-5.png"),
110  "Details", "Details",
111  SLOT(toggleDetailsSlot()),
112  playButtonsLayout);
113 
114  mSpeedAdapter = DoubleProperty::initialize(
115  "speed",
116  "Speed",
117  "Set speed of playback, 0 is normal speed.", 0, DoubleRange(-5,5,1),0);
118  connect(mSpeedAdapter.get(), SIGNAL(changed()), this, SLOT(speedChangedSlot()));
119  playButtonsLayout->addWidget(sscCreateDataWidget(this, mSpeedAdapter));
120 
121  playButtonsLayout->addStretch();
122 
123  topLayout->addStretch();
124  this->showDetails();
125 }
126 
128 {
129 }
130 
131 QString PlaybackWidget::defaultWhatsThis() const
132 {
133  return "<html>"
134  "<h3>Playback</h3>"
135  "<p>"
136  "Play back the whole operation."
137  "Play back contain all tool positions and recorded video "
138  "(video is only recorded during 3D acquisition)."
139  "<p>"
140  "Green indicates visible tools.<br>"
141  "Orange indicates recorded video."
142  "</p>"
143  "<b>Tip: </b> Press the red putton to start playback"
144  "</p>"
145  "</html>";
146 }
147 
148 void PlaybackWidget::toggleDetailsSlot()
149 {
150  settings()->setValue("playback/ShowDetails", !settings()->value("playback/ShowDetails", "true").toBool());
151  this->showDetails();
152 }
153 
154 void PlaybackWidget::showDetails()
155 {
156  bool on = settings()->value("playback/ShowDetails").toBool();
157 
158  mStartTimeLabel->setVisible(on);
159  mTotalLengthLabel->setVisible(on);
160 }
161 
162 void PlaybackWidget::timeLineWidgetValueChangedSlot()
163 {
164  mTimer->setTime(QDateTime::fromMSecsSinceEpoch(mToolTimelineWidget->getPos()));
165 }
166 
167 void PlaybackWidget::toggleOpenSlot()
168 {
169  if (trackingService()->isPlaybackMode())
170  {
171  mTimer->stop();
172  trackingService()->setPlaybackMode(PlaybackTimePtr());
173  videoService()->setPlaybackMode(PlaybackTimePtr());
174  }
175  else
176  {
177  trackingService()->setPlaybackMode(mTimer);
178  if (!trackingService()->isPlaybackMode())
179  {
180  reportError("trackingService is not in playback mode");
181  return;
182  }
183  videoService()->setPlaybackMode(mTimer);
184  report(QString("Started Playback with start time [%1] and end time [%2]")
185  .arg(mTimer->getStartTime().toString(timestampMilliSecondsFormatNice()))
186  .arg(mTimer->getStartTime().addMSecs(mTimer->getLength()).toString(timestampMilliSecondsFormatNice())));
187 
188  this->toolManagerInitializedSlot();
189  }
190 }
191 
192 QColor PlaybackWidget::generateRandomToolColor() const
193 {
194  std::vector<QColor> colors;
195  int s = 255;
196  int v = 192;
197  colors.push_back(QColor::fromHsv(110, s, v));
198  colors.push_back(QColor::fromHsv(80, s, v));
199  colors.push_back(QColor::fromHsv(140, s, v));
200  colors.push_back(QColor::fromHsv(95, s, v));
201  colors.push_back(QColor::fromHsv(125, s, v));
202 
203  static int gCounter = 0;
204  return colors[(gCounter++)%colors.size()];
205 }
206 
207 std::vector<TimelineEvent> PlaybackWidget::convertHistoryToEvents(ToolPtr tool)
208 {
209  std::vector<TimelineEvent> retval;
210  TimedTransformMapPtr history = tool->getPositionHistory();
211  if (!history || history->empty())
212  return retval;
213  double timeout = 200;
214  TimelineEvent currentEvent(tool->getName() + " visible", history->begin()->first);
215  currentEvent.mGroup = "tool";
216  currentEvent.mColor = this->generateRandomToolColor(); // QColor::fromHsv(110, 255, 192);
217 // std::cout << "first event start: " << currentEvent.mDescription << " " << currentEvent.mStartTime << " " << history->size() << std::endl;
218 
219  for(TimedTransformMap::iterator iter=history->begin(); iter!=history->end(); ++iter)
220  {
221  double current = iter->first;
222 
223  if (current - currentEvent.mEndTime > timeout)
224  {
225  retval.push_back(currentEvent);
226  currentEvent.mStartTime = currentEvent.mEndTime = current;
227  }
228  else
229  {
230  currentEvent.mEndTime = current;
231  }
232  }
233  if (!similar(currentEvent.mEndTime - currentEvent.mStartTime, 0))
234  retval.push_back(currentEvent);
235 
236  return retval;
237 }
238 
239 std::vector<TimelineEvent> PlaybackWidget::convertRegistrationHistoryToEvents(RegistrationHistoryPtr reg)
240 {
241  std::vector<TimelineEvent> events;
242 
243  std::vector<RegistrationTransform> tr = reg->getData();
244  for (unsigned i=0; i<tr.size(); ++i)
245  {
246  if (!tr[i].mTimestamp.isValid())
247  continue;
248 
249  QString text = QString("Registraton %1, fixed=%2").arg(tr[i].mType).arg(tr[i].mFixed);
250  if (!tr[i].mMoving.isEmpty())
251  text = QString("%1, moving=%2").arg(text).arg(tr[i].mMoving);
252 
253  events.push_back(TimelineEvent(text,
254  tr[i].mTimestamp.toMSecsSinceEpoch()));
255  }
256 
257  std::vector<ParentSpace> ps = reg->getParentSpaces();
258 
259  return events;
260 }
261 
262 std::vector<TimelineEvent> PlaybackWidget::createEvents()
263 {
264  typedef std::vector<TimelineEvent> TimelineEventVector;
265 
266  // find all valid regions (i.e. time sequences with tool navigation)
267  TimelineEventVector events;
268  TrackingService::ToolMap tools = trackingService()->getTools();
269  for (TrackingService::ToolMap::iterator iter=tools.begin(); iter!=tools.end(); ++iter)
270  {
271  if(!iter->second->hasType(Tool::TOOL_MANUAL))
272  {
273  TimelineEventVector current = convertHistoryToEvents(iter->second);
274  copy(current.begin(), current.end(), std::back_inserter(events));
275  }
276  }
277 
278  std::map<QString, DataPtr> data = patientService()->getData();
279  for (std::map<QString, DataPtr>::iterator iter=data.begin(); iter!=data.end(); ++iter)
280  {
281  QString desc("loaded " + iter->second->getName());
282  if (iter->second->getAcquisitionTime().isValid())
283  {
284  double acqTime = iter->second->getAcquisitionTime().toMSecsSinceEpoch();
285  events.push_back(TimelineEvent(desc, acqTime));
286  }
287 
288  RegistrationHistoryPtr reg = iter->second->get_rMd_History();
289  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
290  copy(current.begin(), current.end(), std::back_inserter(events));
291  }
292 
293  RegistrationHistoryPtr reg = patientService()->get_rMpr_History();
294  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
295  copy(current.begin(), current.end(), std::back_inserter(events));
296 
297  current = videoService()->getPlaybackEvents();
298  copy(current.begin(), current.end(), std::back_inserter(events));
299 
300 
301  return events;
302 }
303 
307 std::pair<double,double> PlaybackWidget::findTimeRange(std::vector<TimelineEvent> events)
308 {
309  if (events.empty())
310  {
311  double now = QDateTime::currentDateTime().toMSecsSinceEpoch();
312  return std::make_pair(now, now+1000);
313  }
314 
315  std::pair<double,double> timeRange(getMilliSecondsSinceEpoch(), 0);
316 // std::pair<double,double> timeRange(events[0].mStartTime, events[0].mEndTime);
317 
318  for (unsigned i=0; i<events.size(); ++i)
319  {
320  timeRange.first = std::min(timeRange.first, events[i].mStartTime);
321  timeRange.second = std::max(timeRange.second, events[i].mEndTime);
322 // std::cout << events[i].mDescription << std::endl;
323 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(events[i].mStartTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
324 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(events[i].mEndTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
325 // std::cout << "===start " << events[i].mStartTime << std::endl;
326 // std::cout << "=== end " << events[i].mEndTime << std::endl;
327 // std::cout << "======" << std::endl;
328  }
329 // std::cout << "======" << std::endl;
330 // std::cout << "======" << std::endl;
331 // std::cout << "======" << std::endl;
332 
333  return timeRange;
334 }
335 
336 void PlaybackWidget::toolManagerInitializedSlot()
337 {
338  if (trackingService()->isPlaybackMode())
339  {
340  mOpenAction->setText("Close Playback");
341  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-green.png"));
342  }
343  else
344  {
345  mOpenAction->setText("Open Playback");
346  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-red.png"));
347  mToolTimelineWidget->setEvents(std::vector<TimelineEvent>());
348  return;
349  }
350 
351  if (trackingService()->getState() < Tool::tsINITIALIZED)
352  return;
353 
354  std::vector<TimelineEvent> events = this->createEvents();
355  std::pair<double,double> range = this->findTimeRange(events);
356 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(range.first).toString(timestampMilliSecondsFormatNice()) << std::endl;
357 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(range.second).toString(timestampMilliSecondsFormatNice()) << std::endl;
358  mTimer->initialize(QDateTime::fromMSecsSinceEpoch(range.first), range.second - range.first);
359 
360  //TODO merge into one initializer:
361  mToolTimelineWidget->setRange(range.first, range.second);
362  mToolTimelineWidget->setEvents(events);
363 
364  QString startDate = mTimer->getStartTime().toString("yyyy-MM-dd");
365  QString startTime = mTimer->getStartTime().toString("hh:mm");
366  QString endTime = mTimer->getStartTime().addMSecs(mTimer->getLength()).toString("hh:mm");
367 // QString length = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(mTimer->getLength()).toString("hh:mm:ss"));
368  QString length = this->convertMillisecsToNiceString(mTimer->getLength());
369  mStartTimeLabel->setText(
370  QString("Date:").leftJustified(15) +"" + startDate+"\n" +
371  QString("Time:").leftJustified(15) +"" + startTime + " - " + endTime + "\n" +
372  QString("Duration:").leftJustified(15)+"" + length);
373 
374  this->timeChangedSlot();
375 }
376 
380 QString PlaybackWidget::stripLeadingZeros(QString time)
381 {
382  QStringList split = time.split(":");
383  bool ok = false;
384  while (!split.empty() && (split.front().toInt(&ok)==0) && ok)
385  {
386  split.pop_front();
387  }
388  return split.join(":");
389 }
390 
394 QString PlaybackWidget::convertMillisecsToNiceString(qint64 length) const
395 {
396  QString retval;
397 
398  qint64 ms = length % 1000;
399  qint64 s = (length / 1000) % 60;
400  qint64 m = (length / (1000*60)) % 60;
401  qint64 h = (length / (1000*60*60));
402  QChar c = '0';
403 
404  retval = QString("%1:%2.%3").arg(m,2,10,c).arg(s,2,10,c).arg(ms,3,10,c);
405  if (h>0)
406  retval = QString("%1:%2").arg(h).arg(retval);
407 
408  return retval;
409 }
410 
411 void PlaybackWidget::speedChangedSlot()
412 {
413  double speed = mSpeedAdapter->getValue();
414  speed = pow(2,speed);
415  mTimer->setSpeed(speed);
416 }
417 
418 
419 void PlaybackWidget::timeChangedSlot()
420 {
421  QString color("green");
422  int fontSize = 4;
423  qint64 offset = mTimer->getOffset(); // SmStartTime.secsTo(QDateTime::currentDateTime());
424  QString format = QString("<font size=%1 color=%2><b>%3</b></font>").arg(fontSize).arg(color);
425 
426  QString currentTime = mTimer->getTime().toString("hh:mm:ss");
427 // QString currentOffset = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(offset).toString("hh:mm:ss.zzz"));
428  QString currentOffset = this->convertMillisecsToNiceString(offset);
429 
430  mLabel->setText(format.arg("Elapsed: "+currentOffset+" \tTime: " + currentTime));
431 
432  if (mTimer->isPlaying())
433  {
434  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-pause-3.png"));
435  mPlayAction->setText("Pause");
436  }
437  else
438  {
439  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-start-3.png"));
440  mPlayAction->setText("Play");
441  }
442 
443 // mTimeLineSlider->blockSignals(true);
445 // mTimeLineSlider->setRange(0, mTimer->getLength());
446 // mTimeLineSlider->setSingleStep(1);
447 //
448 // mTimeLineSlider->setValue(offset);
449 // mTimeLineSlider->setToolTip(QString("Current time"));
450 // mTimeLineSlider->blockSignals(false);
451 
452  mToolTimelineWidget->blockSignals(true);
453  mToolTimelineWidget->setPos(mTimer->getTime().toMSecsSinceEpoch());
454  mToolTimelineWidget->blockSignals(false);
455 }
456 
457 void PlaybackWidget::playSlot()
458 {
459  if (mTimer->isPlaying())
460  {
461  mTimer->pause();
462  }
463  else
464  {
465  mTimer->start();
466  }
467 }
468 void PlaybackWidget::pauseSlot()
469 {
470  mTimer->pause();
471 }
472 void PlaybackWidget::stopSlot()
473 {
474  mTimer->stop();
475 }
476 void PlaybackWidget::forwardSlot()
477 {
478  mTimer->forward(1000*mTimer->getSpeed());
479 }
480 void PlaybackWidget::rewindSlot()
481 {
482  mTimer->rewind(1000*mTimer->getSpeed());
483 }
484 
485 
486 } /* namespace cx */
widget for displaying a timeline for events.
void reportError(QString msg)
Definition: cxLogger.cpp:92
boost::shared_ptr< class RegistrationHistory > RegistrationHistoryPtr
Definition: cxDataManager.h:57
PlaybackWidget(QWidget *parent)
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:53
double getMilliSecondsSinceEpoch()
Definition: cxTime.cpp:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:99
void setRange(double start, double stop)
QAction * createAction(QObject *parent, QIcon iconName, QString text, QString tip, T slot, QLayout *layout=NULL, QToolButton *button=new QToolButton())
Definition: cxBaseWidget.h:130
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
void setPos(double pos)
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:91
double getPos() const
boost::shared_ptr< TimedTransformMap > TimedTransformMapPtr
Definition: cxTool.h:54
Representation of a mouse/keyboard-controlled virtual tool.
Definition: cxTool.h:91
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:42
connected to hardware, if any, ready to use
Definition: cxTool.h:82
std::map< QString, ToolPtr > ToolMap
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:108
void report(QString msg)
Definition: cxLogger.cpp:90
cxLogicManager_EXPORT VideoServicePtr videoService()
cxLogicManager_EXPORT PatientModelServicePtr patientService()
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
RealScalar length() const
QWidget * sscCreateDataWidget(QWidget *parent, PropertyPtr data, QGridLayout *gridLayout, int row)
Create a widget capable of displaying the input data.
cxLogicManager_EXPORT TrackingServicePtr trackingService()
Controller for historic time, playback etc.
QString timestampMilliSecondsFormatNice()
Definition: cxTime.cpp:51
void setEvents(std::vector< TimelineEvent > events)
boost::shared_ptr< class PlaybackTime > PlaybackTimePtr
boost::shared_ptr< class Tool > ToolPtr