Fraxinus  16.5.0-fx-rc1
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 std::vector<TimelineEvent> PlaybackWidget::createEvents()
246 {
247  typedef std::vector<TimelineEvent> TimelineEventVector;
248 
249  // find all valid regions (i.e. time sequences with tool navigation)
250  TimelineEventVector events;
251  TrackingService::ToolMap tools = trackingService()->getTools();
252  for (TrackingService::ToolMap::iterator iter=tools.begin(); iter!=tools.end(); ++iter)
253  {
254  if(!iter->second->hasType(Tool::TOOL_MANUAL))
255  {
256  TimelineEventVector current = convertHistoryToEvents(iter->second);
257  copy(current.begin(), current.end(), std::back_inserter(events));
258  }
259  }
260 
261  std::map<QString, DataPtr> data = patientService()->getData();
262  for (std::map<QString, DataPtr>::iterator iter=data.begin(); iter!=data.end(); ++iter)
263  {
264  QString desc("loaded " + iter->second->getName());
265  if (iter->second->getAcquisitionTime().isValid())
266  {
267  double acqTime = iter->second->getAcquisitionTime().toMSecsSinceEpoch();
268  events.push_back(TimelineEvent(desc, acqTime));
269  }
270 
271  RegistrationHistoryPtr reg = iter->second->get_rMd_History();
272  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
273  copy(current.begin(), current.end(), std::back_inserter(events));
274  }
275 
276  RegistrationHistoryPtr reg = patientService()->get_rMpr_History();
277  TimelineEventVector current = this->convertRegistrationHistoryToEvents(reg);
278  copy(current.begin(), current.end(), std::back_inserter(events));
279 
280  current = videoService()->getPlaybackEvents();
281  copy(current.begin(), current.end(), std::back_inserter(events));
282 
283 
284  return events;
285 }
286 
290 std::pair<double,double> PlaybackWidget::findTimeRange(std::vector<TimelineEvent> events)
291 {
292  if (events.empty())
293  {
294  double now = QDateTime::currentDateTime().toMSecsSinceEpoch();
295  return std::make_pair(now, now+1000);
296  }
297 
298  std::pair<double,double> timeRange(getMilliSecondsSinceEpoch(), 0);
299 // std::pair<double,double> timeRange(events[0].mStartTime, events[0].mEndTime);
300 
301  for (unsigned i=0; i<events.size(); ++i)
302  {
303  timeRange.first = std::min(timeRange.first, events[i].mStartTime);
304  timeRange.second = std::max(timeRange.second, events[i].mEndTime);
305 // std::cout << events[i].mDescription << std::endl;
306 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(events[i].mStartTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
307 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(events[i].mEndTime).toString(timestampMilliSecondsFormatNice()) << std::endl;
308 // std::cout << "===start " << events[i].mStartTime << std::endl;
309 // std::cout << "=== end " << events[i].mEndTime << std::endl;
310 // std::cout << "======" << std::endl;
311  }
312 // std::cout << "======" << std::endl;
313 // std::cout << "======" << std::endl;
314 // std::cout << "======" << std::endl;
315 
316  return timeRange;
317 }
318 
319 void PlaybackWidget::toolManagerInitializedSlot()
320 {
321  if (trackingService()->isPlaybackMode())
322  {
323  mOpenAction->setText("Close Playback");
324  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-green.png"));
325  }
326  else
327  {
328  mOpenAction->setText("Open Playback");
329  mOpenAction->setIcon(QIcon(":/icons/open_icon_library/button-red.png"));
330  mToolTimelineWidget->setEvents(std::vector<TimelineEvent>());
331  return;
332  }
333 
334  if (trackingService()->getState() < Tool::tsINITIALIZED)
335  return;
336 
337  std::vector<TimelineEvent> events = this->createEvents();
338  std::pair<double,double> range = this->findTimeRange(events);
339 // std::cout << "===start " << QDateTime::fromMSecsSinceEpoch(range.first).toString(timestampMilliSecondsFormatNice()) << std::endl;
340 // std::cout << "=== end " << QDateTime::fromMSecsSinceEpoch(range.second).toString(timestampMilliSecondsFormatNice()) << std::endl;
341  mTimer->initialize(QDateTime::fromMSecsSinceEpoch(range.first), range.second - range.first);
342 
343  //TODO merge into one initializer:
344  mToolTimelineWidget->setRange(range.first, range.second);
345  mToolTimelineWidget->setEvents(events);
346 
347  QString startDate = mTimer->getStartTime().toString("yyyy-MM-dd");
348  QString startTime = mTimer->getStartTime().toString("hh:mm");
349  QString endTime = mTimer->getStartTime().addMSecs(mTimer->getLength()).toString("hh:mm");
350 // QString length = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(mTimer->getLength()).toString("hh:mm:ss"));
351  QString length = this->convertMillisecsToNiceString(mTimer->getLength());
352  mStartTimeLabel->setText(
353  QString("Date:").leftJustified(15) +"" + startDate+"\n" +
354  QString("Time:").leftJustified(15) +"" + startTime + " - " + endTime + "\n" +
355  QString("Duration:").leftJustified(15)+"" + length);
356 
357  this->timeChangedSlot();
358 }
359 
363 //QString PlaybackWidget::stripLeadingZeros(QString time)
364 //{
365 // QStringList split = time.split(":");
366 // bool ok = false;
367 // while (!split.empty() && (split.front().toInt(&ok)==0) && ok)
368 // {
369 // split.pop_front();
370 // }
371 // return split.join(":");
372 //}
373 
377 QString PlaybackWidget::convertMillisecsToNiceString(qint64 length) const
378 {
379  QString retval;
380 
381  qint64 ms = length % 1000;
382  qint64 s = (length / 1000) % 60;
383  qint64 m = (length / (1000*60)) % 60;
384  qint64 h = (length / (1000*60*60));
385  QChar c = '0';
386 
387  retval = QString("%1:%2.%3").arg(m,2,10,c).arg(s,2,10,c).arg(ms,3,10,c);
388  if (h>0)
389  retval = QString("%1:%2").arg(h).arg(retval);
390 
391  return retval;
392 }
393 
394 void PlaybackWidget::speedChangedSlot()
395 {
396  double speed = mSpeedAdapter->getValue();
397  speed = pow(2,speed);
398  mTimer->setSpeed(speed);
399 }
400 
401 
402 void PlaybackWidget::timeChangedSlot()
403 {
404  QString color("green");
405  int fontSize = 4;
406  qint64 offset = mTimer->getOffset(); // SmStartTime.secsTo(QDateTime::currentDateTime());
407  QString format = QString("<font size=%1 color=%2><b>%3</b></font>").arg(fontSize).arg(color);
408 
409  QString currentTime = mTimer->getTime().toString("hh:mm:ss");
410 // QString currentOffset = this->stripLeadingZeros(QTime(0,0,0,0).addMSecs(offset).toString("hh:mm:ss.zzz"));
411  QString currentOffset = this->convertMillisecsToNiceString(offset);
412 
413  mLabel->setText(format.arg("Elapsed: "+currentOffset+" \tTime: " + currentTime));
414 
415  if (mTimer->isPlaying())
416  {
417  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-pause-3.png"));
418  mPlayAction->setText("Pause");
419  }
420  else
421  {
422  mPlayAction->setIcon(QIcon(":/icons/open_icon_library/media-playback-start-3.png"));
423  mPlayAction->setText("Play");
424  }
425 
426 // mTimeLineSlider->blockSignals(true);
428 // mTimeLineSlider->setRange(0, mTimer->getLength());
429 // mTimeLineSlider->setSingleStep(1);
430 //
431 // mTimeLineSlider->setValue(offset);
432 // mTimeLineSlider->setToolTip(QString("Current time"));
433 // mTimeLineSlider->blockSignals(false);
434 
435  mToolTimelineWidget->blockSignals(true);
436  mToolTimelineWidget->setPos(mTimer->getTime().toMSecsSinceEpoch());
437  mToolTimelineWidget->blockSignals(false);
438 }
439 
440 void PlaybackWidget::playSlot()
441 {
442  if (mTimer->isPlaying())
443  {
444  mTimer->pause();
445  }
446  else
447  {
448  mTimer->start();
449  }
450 }
451 //void PlaybackWidget::pauseSlot()
452 //{
453 // mTimer->pause();
454 //}
455 void PlaybackWidget::stopSlot()
456 {
457  mTimer->stop();
458 }
459 void PlaybackWidget::forwardSlot()
460 {
461  mTimer->forward(1000*mTimer->getSpeed());
462 }
463 void PlaybackWidget::rewindSlot()
464 {
465  mTimer->rewind(1000*mTimer->getSpeed());
466 }
467 
468 
469 } /* 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
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