NorMIT-nav  16.5
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("Replay current session");
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 void PlaybackWidget::toggleDetailsSlot()
132 {
133  settings()->setValue("playback/ShowDetails", !settings()->value("playback/ShowDetails", "true").toBool());
134  this->showDetails();
135 }
136 
137 void PlaybackWidget::showDetails()
138 {
139  bool on = settings()->value("playback/ShowDetails").toBool();
140 
141  mStartTimeLabel->setVisible(on);
142  mTotalLengthLabel->setVisible(on);
143 }
144 
145 void PlaybackWidget::timeLineWidgetValueChangedSlot()
146 {
147  mTimer->setTime(QDateTime::fromMSecsSinceEpoch(mToolTimelineWidget->getPos()));
148 }
149 
150 void PlaybackWidget::toggleOpenSlot()
151 {
152  if (trackingService()->isPlaybackMode())
153  {
154  mTimer->stop();
155  trackingService()->setPlaybackMode(PlaybackTimePtr());
156  videoService()->setPlaybackMode(PlaybackTimePtr());
157  }
158  else
159  {
160  trackingService()->setPlaybackMode(mTimer);
161  if (!trackingService()->isPlaybackMode())
162  {
163  reportError("trackingService is not in playback mode");
164  return;
165  }
166  videoService()->setPlaybackMode(mTimer);
167  report(QString("Started Playback with start time [%1] and end time [%2]")
168  .arg(mTimer->getStartTime().toString(timestampMilliSecondsFormatNice()))
169  .arg(mTimer->getStartTime().addMSecs(mTimer->getLength()).toString(timestampMilliSecondsFormatNice())));
170 
171  this->toolManagerInitializedSlot();
172  }
173 }
174 
175 QColor PlaybackWidget::generateRandomToolColor() const
176 {
177  std::vector<QColor> colors;
178  int s = 255;
179  int v = 192;
180  colors.push_back(QColor::fromHsv(110, s, v));
181  colors.push_back(QColor::fromHsv(80, s, v));
182  colors.push_back(QColor::fromHsv(140, s, v));
183  colors.push_back(QColor::fromHsv(95, s, v));
184  colors.push_back(QColor::fromHsv(125, s, v));
185 
186  static int gCounter = 0;
187  return colors[(gCounter++)%colors.size()];
188 }
189 
190 std::vector<TimelineEvent> PlaybackWidget::convertHistoryToEvents(ToolPtr tool)
191 {
192  std::vector<TimelineEvent> retval;
193  TimedTransformMapPtr history = tool->getPositionHistory();
194  if (!history || history->empty())
195  return retval;
196  double timeout = 200;
197  TimelineEvent currentEvent(tool->getName() + " visible", history->begin()->first);
198  currentEvent.mGroup = "tool";
199  currentEvent.mColor = this->generateRandomToolColor(); // QColor::fromHsv(110, 255, 192);
200 // std::cout << "first event start: " << currentEvent.mDescription << " " << currentEvent.mStartTime << " " << history->size() << std::endl;
201 
202  for(TimedTransformMap::iterator iter=history->begin(); iter!=history->end(); ++iter)
203  {
204  double current = iter->first;
205 
206  if (current - currentEvent.mEndTime > timeout)
207  {
208  retval.push_back(currentEvent);
209  currentEvent.mStartTime = currentEvent.mEndTime = current;
210  }
211  else
212  {
213  currentEvent.mEndTime = current;
214  }
215  }
216  if (!similar(currentEvent.mEndTime - currentEvent.mStartTime, 0))
217  retval.push_back(currentEvent);
218 
219  return retval;
220 }
221 
222 std::vector<TimelineEvent> PlaybackWidget::convertRegistrationHistoryToEvents(RegistrationHistoryPtr reg)
223 {
224  std::vector<TimelineEvent> events;
225 
226  std::vector<RegistrationTransform> tr = reg->getData();
227  for (unsigned i=0; i<tr.size(); ++i)
228  {
229  if (!tr[i].mTimestamp.isValid())
230  continue;
231 
232  QString text = QString("Registraton %1, fixed=%2").arg(tr[i].mType).arg(tr[i].mFixed);
233  if (!tr[i].mMoving.isEmpty())
234  text = QString("%1, moving=%2").arg(text).arg(tr[i].mMoving);
235 
236  events.push_back(TimelineEvent(text,
237  tr[i].mTimestamp.toMSecsSinceEpoch()));
238  }
239 
240 // std::vector<ParentSpace> ps = reg->getParentSpaces();
241 
242  return events;
243 }
244 
245 
246 bool PlaybackWidget::isInterestingTool(ToolPtr tool) const
247 {
248  return !tool->hasType(Tool::TOOL_MANUAL) && !tool->hasType(Tool::TOOL_REFERENCE);
249 }
250 
251 std::vector<TimelineEvent> PlaybackWidget::createEvents()
252 {
253  typedef std::vector<TimelineEvent> TimelineEventVector;
254 
255  // find all valid regions (i.e. time sequences with tool navigation)
256  TimelineEventVector events;
257  TrackingService::ToolMap tools = trackingService()->getTools();
258  for (TrackingService::ToolMap::iterator iter=tools.begin(); iter!=tools.end(); ++iter)
259  {
260  if(this->isInterestingTool(iter->second))
261  {
262  TimelineEventVector current = convertHistoryToEvents(iter->second);
263  copy(current.begin(), current.end(), std::back_inserter(events));
264  }
265  }
266 
267  std::map<QString, DataPtr> data = patientService()->getData();
268  for (std::map<QString, DataPtr>::iterator iter=data.begin(); iter!=data.end(); ++iter)
269  {
270  QString desc("loaded " + iter->second->getName());
271  if (iter->second->getAcquisitionTime().isValid())
272  {
273  double acqTime = iter->second->getAcquisitionTime().toMSecsSinceEpoch();
274  events.push_back(TimelineEvent(desc, acqTime));
275  }
276 
277  RegistrationHistoryPtr reg = iter->second->get_rMd_History();
278  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
279  copy(current.begin(), current.end(), std::back_inserter(events));
280  }
281 
282  RegistrationHistoryPtr reg = patientService()->get_rMpr_History();
283  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
284  copy(current.begin(), current.end(), std::back_inserter(events));
285 
286  current = videoService()->getPlaybackEvents();
287  copy(current.begin(), current.end(), std::back_inserter(events));
288 
289 
290  return events;
291 }
292 
296 std::pair<double,double> PlaybackWidget::findTimeRange(std::vector<TimelineEvent> events)
297 {
298  if (events.empty())
299  {
300  double now = QDateTime::currentDateTime().toMSecsSinceEpoch();
301  return std::make_pair(now, now+1000);
302  }
303 
304  std::pair<double,double> timeRange(getMilliSecondsSinceEpoch(), 0);
305 // std::pair<double,double> timeRange(events[0].mStartTime, events[0].mEndTime);
306 
307  for (unsigned i=0; i<events.size(); ++i)
308  {
309  timeRange.first = std::min(timeRange.first, events[i].mStartTime);
310  timeRange.second = std::max(timeRange.second, events[i].mEndTime);
311 // std::cout << events[i].mDescription << std::endl;
312 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(events[i].mStartTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
313 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(events[i].mEndTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
314 // std::cout << "===start " << events[i].mStartTime << std::endl;
315 // std::cout << "=== end " << events[i].mEndTime << std::endl;
316 // std::cout << "======" << std::endl;
317  }
318 // std::cout << "======" << std::endl;
319 // std::cout << "======" << std::endl;
320 // std::cout << "======" << std::endl;
321 
322  return timeRange;
323 }
324 
325 void PlaybackWidget::toolManagerInitializedSlot()
326 {
327  if (trackingService()->isPlaybackMode())
328  {
329  mOpenAction->setText("Close Playback");
330  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-green.png"));
331  }
332  else
333  {
334  mOpenAction->setText("Open Playback");
335  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-red.png"));
336  mToolTimelineWidget->setEvents(std::vector<TimelineEvent>());
337  return;
338  }
339 
340  if (trackingService()->getState() < Tool::tsINITIALIZED)
341  return;
342 
343  std::vector<TimelineEvent> events = this->createEvents();
344  std::pair<double,double> range = this->findTimeRange(events);
345 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(range.first).toString(timestampMilliSecondsFormatNice()) << std::endl;
346 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(range.second).toString(timestampMilliSecondsFormatNice()) << std::endl;
347  mTimer->initialize(QDateTime::fromMSecsSinceEpoch(range.first), range.second - range.first);
348 
349  //TODO merge into one initializer:
350  mToolTimelineWidget->setRange(range.first, range.second);
351  mToolTimelineWidget->setEvents(events);
352 
353  QString startDate = mTimer->getStartTime().toString("yyyy-MM-dd");
354  QString startTime = mTimer->getStartTime().toString("hh:mm");
355  QString endTime = mTimer->getStartTime().addMSecs(mTimer->getLength()).toString("hh:mm");
356 // QString length = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(mTimer->getLength()).toString("hh:mm:ss"));
357  QString length = this->convertMillisecsToNiceString(mTimer->getLength());
358  mStartTimeLabel->setText(
359  QString("Date:").leftJustified(15) +"" + startDate+"\n" +
360  QString("Time:").leftJustified(15) +"" + startTime + " - " + endTime + "\n" +
361  QString("Duration:").leftJustified(15)+"" + length);
362 
363  this->timeChangedSlot();
364 }
365 
369 //QString PlaybackWidget::stripLeadingZeros(QString time)
370 //{
371 // QStringList split = time.split(":");
372 // bool ok = false;
373 // while (!split.empty() && (split.front().toInt(&ok)==0) && ok)
374 // {
375 // split.pop_front();
376 // }
377 // return split.join(":");
378 //}
379 
383 QString PlaybackWidget::convertMillisecsToNiceString(qint64 length) const
384 {
385  QString retval;
386 
387  qint64 ms = length % 1000;
388  qint64 s = (length / 1000) % 60;
389  qint64 m = (length / (1000*60)) % 60;
390  qint64 h = (length / (1000*60*60));
391  QChar c = '0';
392 
393  retval = QString("%1:%2.%3").arg(m,2,10,c).arg(s,2,10,c).arg(ms,3,10,c);
394  if (h>0)
395  retval = QString("%1:%2").arg(h).arg(retval);
396 
397  return retval;
398 }
399 
400 void PlaybackWidget::speedChangedSlot()
401 {
402  double speed = mSpeedAdapter->getValue();
403  speed = pow(2,speed);
404  mTimer->setSpeed(speed);
405 }
406 
407 
408 void PlaybackWidget::timeChangedSlot()
409 {
410  QString color("green");
411  int fontSize = 4;
412  qint64 offset = mTimer->getOffset(); // SmStartTime.secsTo(QDateTime::currentDateTime());
413  QString format = QString("<font size=%1 color=%2><b>%3</b></font>").arg(fontSize).arg(color);
414 
415  QString currentTime = mTimer->getTime().toString("hh:mm:ss");
416 // QString currentOffset = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(offset).toString("hh:mm:ss.zzz"));
417  QString currentOffset = this->convertMillisecsToNiceString(offset);
418 
419  mLabel->setText(format.arg("Elapsed: "+currentOffset+" \tTime: " + currentTime));
420 
421  if (mTimer->isPlaying())
422  {
423  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-pause-3.png"));
424  mPlayAction->setText("Pause");
425  }
426  else
427  {
428  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-start-3.png"));
429  mPlayAction->setText("Play");
430  }
431 
432 // mTimeLineSlider->blockSignals(true);
434 // mTimeLineSlider->setRange(0, mTimer->getLength());
435 // mTimeLineSlider->setSingleStep(1);
436 //
437 // mTimeLineSlider->setValue(offset);
438 // mTimeLineSlider->setToolTip(QString("Current time"));
439 // mTimeLineSlider->blockSignals(false);
440 
441  mToolTimelineWidget->blockSignals(true);
442  mToolTimelineWidget->setPos(mTimer->getTime().toMSecsSinceEpoch());
443  mToolTimelineWidget->blockSignals(false);
444 }
445 
446 void PlaybackWidget::playSlot()
447 {
448  if (mTimer->isPlaying())
449  {
450  mTimer->pause();
451  }
452  else
453  {
454  mTimer->start();
455  }
456 }
457 //void PlaybackWidget::pauseSlot()
458 //{
459 // mTimer->pause();
460 //}
461 void PlaybackWidget::stopSlot()
462 {
463  mTimer->stop();
464 }
465 void PlaybackWidget::forwardSlot()
466 {
467  mTimer->forward(1000*mTimer->getSpeed());
468 }
469 void PlaybackWidget::rewindSlot()
470 {
471  mTimer->rewind(1000*mTimer->getSpeed());
472 }
473 
474 
475 } /* 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:87
Reference tool.
Definition: cxTool.h:105
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:149
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
void setPos(double pos)
boost::shared_ptr< class PlaybackTime > PlaybackTimePtr
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:79
double getPos() const
boost::shared_ptr< TimedTransformMap > TimedTransformMapPtr
Definition: cxTool.h:57
Representation of a mouse/keyboard-controlled virtual tool.
Definition: cxTool.h:106
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:42
connected to hardware, if any, ready to use
Definition: cxTool.h:97
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 Tool > ToolPtr