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