CustusX  22.04-rc5
An IGT application
cxMainWindowActions.cpp
Go to the documentation of this file.
1 #include "cxMainWindowActions.h"
2 
3 #include <QtConcurrent>
4 #include <QWidget>
5 #include <QFileDialog>
6 #include <QAction>
7 #include <QScreen>
8 #include <QDesktopWidget>
9 #include <QMessageBox>
10 #include <QApplication>
11 #include <QDesktopServices>
12 #include <QDockWidget>
13 #include <QAction>
14 
15 #include "boost/bind.hpp"
16 #include "boost/function.hpp"
17 #include "cxReporter.h"
18 #include "cxLogger.h"
19 #include "cxConfig.h"
20 #include "cxDataLocations.h"
21 #include "cxProfile.h"
22 #include "cxLogicManager.h"
23 #include "cxTrackingService.h"
24 #include "cxSettings.h"
25 #include "cxVideoService.h"
26 #include "cxNavigation.h"
27 #include "cxImage.h"
28 #include "cxPatientModelService.h"
29 #include "cxViewService.h"
30 #include "cxViewGroupData.h"
32 #include "cxVisServices.h"
33 #include "cxVLCRecorder.h"
34 #include "cxImportDataDialog.h"
35 #include "cxExportDataDialog.h"
37 #include "cxViewCollectionWidget.h"
39 #include "cxFileHelpers.h"
40 #include "cxFileManagerService.h"
42 #include "cxApplication.h"
43 
44 namespace cx
45 {
46 
48  QObject(parent),
49  mServices(services),
50  mScreenShotWriter(ScreenShotImageWriter::create(services->patient()))
51 {
52  this->createActions();
53 }
54 
55 QAction* MainWindowActions::getAction(QString uid)
56 {
57  if (!mActions.count(uid))
58  CX_LOG_ERROR() << QString("Could not find action %1 in MainWindowActions object").arg(uid);
59  return mActions[uid];
60 }
61 
62 void MainWindowActions::createActions()
63 {
64  this->createAction("GotoDocumentation", "Web Documentation",
65  QIcon(":/icons/open_icon_library/applications-internet.png"),
66  QKeySequence(""), "",
67  &MainWindowActions::onGotoDocumentation);
68 
69  this->createAction("StartLogConsole", "Start Log Console",
70  QIcon(),
71  QKeySequence("Ctrl+D"), "Open Log Console as external application",
72  &MainWindowActions::onStartLogConsole);
73 
74  this->createAction("ShootScreen", "Shoot Screen",
75  QIcon(":/icons/screenshot-screen.png"),
76  QKeySequence("Ctrl+f"), "Save a screenshot to the patient folder.",
77  &MainWindowActions::shootScreen);
78 
79  this->createAction("ShootWindow", "Shoot Window",
80  QIcon(":/icons/screenshot-window.png"),
81  QKeySequence("Ctrl+Shift+f"), "Save an image of the application to the patient folder.",
82  &MainWindowActions::shootWindow);
83 
84  mRecordFullscreenStreamingAction = this->createAction("RecordFullscreen", "Record Fullscreen video with VLC (needs to be at least 7 sec to work)",
85  QIcon(),
86  QKeySequence("F8"), "Record a video of the full screen.",
87  &MainWindowActions::recordFullscreen);
88  connect(vlc(), &VLCRecorder::stateChanged, this, &MainWindowActions::updateRecordFullscreenActionSlot);
89  this->updateRecordFullscreenActionSlot();
90 
91  mShowPointPickerAction = this->createAction("ShowPointPicker", "Point Picker",
92  QIcon(":/icons/point_picker.png"),
93  QKeySequence(""), "Activate the 3D Point Picker Probe",
94  &MainWindowActions::togglePointPickerActionSlot);
95  mShowPointPickerAction->setCheckable(true);
96 
97  if (mServices->view()->getGroup(0))
98  connect(mServices->view()->getGroup(0).get(), &ViewGroupData::optionsChanged, this, &MainWindowActions::updatePointPickerActionSlot);
99  this->updatePointPickerActionSlot();
100 
101  mStartStreamingAction = this->createAction("StartStreaming", "Start Streaming",
102  QIcon(":/icons/open_icon_library/.png"),
103  QKeySequence("Ctrl+V"), "",
104  &MainWindowActions::toggleStreamingSlot);
105  connect(mServices->video().get(), &VideoService::connected, this, &MainWindowActions::updateStreamingActionSlot);
106  this->updateStreamingActionSlot();
107 
108  this->createAction("CenterToImageCenter", "Center Image",
109  QIcon(":/icons/center_image.png"),
110  QKeySequence(), "",
111  &MainWindowActions::centerToImageCenterSlot);
112 
113  this->createAction("CenterToTooltip", "Center Tool",
114  QIcon(":/icons/center_tool.png"),
115  QKeySequence(""), "",
116  &MainWindowActions::centerToTooltipSlot);
117 
118  this->createPatientActions();
119  this->createTrackingActions();
120 }
121 
122 void MainWindowActions::createTrackingActions()
123 {
124  this->createAction("ConfigureTools", "Tool configuration",
125  QIcon(), QKeySequence(), "",
126  &MainWindowActions::configureSlot);
127 
128  boost::function<void()> finit = boost::bind(&TrackingService::setState, mServices->tracking(), Tool::tsINITIALIZED);
129  this->createAction("InitializeTools", "Initialize",
130  QIcon(), QKeySequence(), "",
131  finit);
132 
133  mTrackingToolsAction = this->createAction("TrackingTools", "Start tracking",
134  QIcon(), QKeySequence("Ctrl+T"), "",
135  &MainWindowActions::toggleTrackingSlot);
136 
137  connect(mServices->tracking().get(), &TrackingService::stateChanged, this, &MainWindowActions::updateTrackingActionSlot);
138  this->updateTrackingActionSlot();
139 }
140 
141 void MainWindowActions::createPatientActions()
142 {
143  this->createAction("NewPatient", "New Patient",
144  QIcon(":/icons/open_icon_library/document-new-8.png"),
145  QKeySequence("Ctrl+N"),
146  "Create a new patient file",
147  &MainWindowActions::newPatientSlot);
148 
149  this->createAction("SaveFile", "Save Patient",
150  QIcon(":/icons/open_icon_library/document-save-5.png"),
151  QKeySequence("Ctrl+S"),
152  "Save patient file",
153  &MainWindowActions::savePatientFileSlot);
154 
155  this->createAction("LoadFile", "Load Patient",
156  QIcon(":/icons/open_icon_library/document-open-7.png"),
157  QKeySequence("Ctrl+L"),
158  "Load patient file",
159  &MainWindowActions::loadPatientFileSlot);
160 
161  this->createAction("LoadFileCopy", "Load from Patient template",
162  QIcon(":/icons/open_icon_library/document-open-7.png"),
163  QKeySequence(),
164  "Create a new patient based on a template",
165  &MainWindowActions::loadPatientFileCopySlot);
166 
167  this->createAction("ClearPatient", "Clear Patient",
168  QIcon(),
169  QKeySequence(),
170  "Clear the current patient from the application",
171  &MainWindowActions::clearPatientSlot);
172 
173  this->createAction("ExportPatient", "Export Patient",
174  QIcon(),
175  QKeySequence(),
176  "Export patient data to a folder",
177  &MainWindowActions::exportDataSlot);
178 
179  //Action "AddFilesForImport" was previously called "ImportData"
180  this->createAction("AddFilesForImport", "Add files for import",
181  QIcon(":/icons/open_icon_library/document-import-2.png"),
182  QKeySequence("Ctrl+I"),
183  "Add files to be imported",
184  [=](){this->importDataSlot("AddMoreFilesButtonClickedAction");});
185 
186  this->createAction("ImportSelectedData", "Import selected data",
187  QIcon(),
188  QKeySequence(),
189  "Import all selected data files",
190  [=](){this->importDataSlot("ImportButtonClickedAction");});
191 }
192 
193 template <class T>
194 QAction* MainWindowActions::createAction(QString uid, QString text, QIcon icon,
195  QKeySequence shortcut, QString help,
196  T triggerSlot)
197 {
198  QAction* retval = new QAction(icon, text, this);
199  retval->setObjectName(uid);
200  retval->setShortcut(shortcut);
201  retval->setStatusTip(help);
202  connect(retval, &QAction::triggered, this, triggerSlot);
203  mActions[uid] = retval;
204  return retval;
205 }
206 
207 namespace
208 {
209 QString timestampFormatFolderFriendly()
210 {
211  return QString("yyyy-MM-dd_hh-mm");
212 }
213 }
214 
215 QWidget* MainWindowActions::parentWidget()
216 {
217  return dynamic_cast<QWidget*>(this->parent());
218 }
219 
220 
221 void MainWindowActions::newPatientSlot()
222 {
223  QString choosenDir = this->selectNewPatientFolder();
224  if(choosenDir.isEmpty())
225  return;
226 
227  // Update global patient number
228  int patientNumber = settings()->value("globalPatientNumber").toInt();
229  settings()->setValue("globalPatientNumber", ++patientNumber);
230 
231  mServices->session()->load(choosenDir);
232 }
233 
234 QString MainWindowActions::selectNewPatientFolder()
235 {
236  QString patientDatafolder = this->getExistingSessionFolder();
237 
238  QString timestamp = QDateTime::currentDateTime().toString(timestampFormatFolderFriendly());
239  QString filename = QString("%1_%2_%3.cx3")
240  .arg(timestamp)
241  .arg(profile()->getName())
242  .arg(settings()->value("globalPatientNumber").toString());
243 
244  QString choosenDir = patientDatafolder + "/" + filename;
245 
246  QFileDialog dialog(this->parentWidget(), tr("Select directory to save patient in"), patientDatafolder + "/");
247  dialog.setOption(QFileDialog::DontUseNativeDialog, true);
248  dialog.setOption(QFileDialog::ShowDirsOnly, true);
249  dialog.selectFile(filename);
250  if (!dialog.exec())
251  return QString();
252  choosenDir = dialog.selectedFiles().front();
253 
254  if (!choosenDir.endsWith(".cx3"))
255  choosenDir += QString(".cx3");
256 
257  return choosenDir;
258 }
259 
260 QString MainWindowActions::getExistingSessionFolder()
261 {
262  return profile()->getSessionRootFolder();
263 }
264 
265 void MainWindowActions::clearPatientSlot()
266 {
267  mServices->session()->clear();
268 }
269 
270 void MainWindowActions::savePatientFileSlot()
271 {
272  if (mServices->patient()->getActivePatientFolder().isEmpty())
273  {
274  reportWarning("No patient selected, select or create patient before saving!");
275  this->newPatientSlot();
276  return;
277  }
278 
279  mServices->session()->save();
280 }
281 
282 void MainWindowActions::loadPatientFileSlot()
283 {
284  QString patientDatafolder = this->getExistingSessionFolder();
285 
286  // Open file dialog
287  QString folder = QFileDialog::getExistingDirectory(this->parentWidget(), "Select patient", patientDatafolder, QFileDialog::ShowDirsOnly);
288  if (folder.isEmpty())
289  return;
290 
291  mServices->session()->load(folder);
292 }
293 
294 void MainWindowActions::loadPatientFileCopySlot()
295 {
296  QString patientDatafolder = profile()->getPatientTemplatePath();
297 
298  // Open file dialog
299  QString folder = QFileDialog::getExistingDirectory(this->parentWidget(), "Select template patient to copy", patientDatafolder, QFileDialog::ShowDirsOnly);
300  if (folder.isEmpty())
301  return;
302 
303  QString newFolder = this->selectNewPatientFolder();
304  if (newFolder.isEmpty())
305  return;
306 
307  if(!copyRecursively(folder, newFolder, true))
308  {
309  CX_LOG_WARNING() << "MainWindowActions::loadPatientFileCopySlot(): Cannot copy patient folder: " << folder;
310  return;
311  }
312 
313  mServices->session()->load(newFolder);
314 }
315 
316 void MainWindowActions::exportDataSlot()
317 {
318  this->savePatientFileSlot();
319 
320  ExportDataDialog* wizard = new ExportDataDialog(mServices->patient(), this->parentWidget());
321  wizard->exec(); //calling exec() makes the wizard dialog modal which prevents other user interaction with the system
322 }
323 
324 void MainWindowActions::importDataSlot(QString actionText)
325 {
326  this->savePatientFileSlot();
327 
328  QDockWidget* importDockWidget = findMainWindowChildWithObjectName<QDockWidget*>("import_widgetDockWidget");
329  if(!importDockWidget)
330  {
331  CX_LOG_ERROR() << "Cannot find DockWidget for ImportWidget";
332  return;
333  }
334 
335  importDockWidget->show();
336  importDockWidget->raise();
337 
338  QWidget* widget = findMainWindowChildWithObjectName<QWidget*>("import_widget");
339  if(!widget)
340  {
341  CX_LOG_ERROR() << "Cannot find ImportWidget";
342  return;
343  }
344 
345  bool actionFound = false;
346  QList<QAction*> actions = widget->actions();
347  foreach(QAction* action, actions)
348  {
349  if(action->text().contains(actionText))
350  {
351  actionFound = true;
352  action->trigger();
353  }
354  }
355  if(!actionFound)
356  CX_LOG_ERROR() << "MainWindowActions::importDataSlot, action not found: " << actionText;
357 }
358 
359 void MainWindowActions::shootScreen()
360 {
361  mScreenShotWriter->grabAllScreensToFile();
362 }
363 
364 void MainWindowActions::shootWindow()
365 {
366 // this->shootOneLayout(0);
367 
368  int index=0;
369  while (mServices->view()->getLayoutWidget(index))
370  this->shootOneLayout(index++);
371 }
372 
373 void MainWindowActions::shootOneLayout(int index)
374 {
375  QWidget* widget = mServices->view()->getLayoutWidget(index);
376  ViewCollectionWidget* vcWidget = dynamic_cast<ViewCollectionWidget*>(widget);
377 
378  ViewCollectionImageWriter grabber(vcWidget);
380  mScreenShotWriter->save(pm, QString("_layout%1").arg(index));
381 }
382 
383 void MainWindowActions::recordFullscreen()
384 {
385  QString path = mServices->patient()->generateFilePath("Screenshots", "mp4");
386 
387  if(vlc()->isRecording())
388  vlc()->stopRecording();
389  else
390  vlc()->startRecording(path);
391 }
392 
393 void MainWindowActions::updateRecordFullscreenActionSlot()
394 {
395  mRecordFullscreenStreamingAction->setCheckable(true);
396  mRecordFullscreenStreamingAction->blockSignals(true);
397  mRecordFullscreenStreamingAction->setChecked(vlc()->isRecording());
398  mRecordFullscreenStreamingAction->blockSignals(false);
399 
400  if(vlc()->isRecording())
401  {
402  mRecordFullscreenStreamingAction->setIcon(QIcon(":/icons/Video-icon_green.png"));
403  }
404  else
405  {
406  mRecordFullscreenStreamingAction->setIcon(QIcon(":/icons/Video-icon_gray.png"));
407  }
408 
409 }
410 
411 void MainWindowActions::onStartLogConsole()
412 {
413  QString fullname = DataLocations::findExecutableInStandardLocations("LogConsole");
414 // std::cout << "MainWindowActions::onStartLogConsole() " << fullname << std::endl;
415  mLocalVideoServerProcess.reset(new ProcessWrapper(QString("LogConsole")));
416  mLocalVideoServerProcess->launchWithRelativePath(fullname, QStringList());
417 }
418 
419 void MainWindowActions::toggleStreamingSlot()
420 {
421  if (mServices->video()->isConnected())
422  mServices->video()->closeConnection();
423  else
424  mServices->video()->openConnection();
425 }
426 
427 void MainWindowActions::updateStreamingActionSlot()
428 {
429  if (mServices->video()->isConnected())
430  {
431  mStartStreamingAction->setIcon(QIcon(":/icons/streaming_green.png"));
432  mStartStreamingAction->setText("Stop Streaming");
433  }
434  else
435  {
436  mStartStreamingAction->setIcon(QIcon(":/icons/streaming_red.png"));
437  mStartStreamingAction->setText("Start Streaming");
438  }
439 }
440 
441 void MainWindowActions::centerToImageCenterSlot()
442 {
443  mServices->view()->centerToImageCenterInActiveViewGroup();
444 }
445 
446 void MainWindowActions::centerToTooltipSlot()
447 {
448  NavigationPtr nav = mServices->view()->getNavigation();
449  nav->centerToTooltip();
450 }
451 
452 void MainWindowActions::togglePointPickerActionSlot()
453 {
454  ViewGroupDataPtr data = mServices->view()->getGroup(0);
455  ViewGroupData::Options options = data->getOptions();
456  options.mShowPointPickerProbe = !options.mShowPointPickerProbe;
457  data->setOptions(options);
458 }
459 void MainWindowActions::updatePointPickerActionSlot()
460 {
461  if (!mServices->view()->getGroup(0))
462  return;
463  bool show = mServices->view()->getGroup(0)->getOptions().mShowPointPickerProbe;
464  mShowPointPickerAction->setChecked(show);
465 }
466 
467 void MainWindowActions::updateTrackingActionSlot()
468 {
469  if (mServices->tracking()->getState() >= Tool::tsTRACKING)
470  {
471  mTrackingToolsAction->setIcon(QIcon(":/icons/polaris-green.png"));
472  mTrackingToolsAction->setText("Stop Tracking");
473  }
474  else
475  {
476  mTrackingToolsAction->setIcon(QIcon(":/icons/polaris-red.png"));
477  mTrackingToolsAction->setText("Start Tracking");
478  }
479 }
480 
481 void MainWindowActions::configureSlot()
482 {
483  mServices->tracking()->setState(Tool::tsCONFIGURED);
484 }
485 
486 void MainWindowActions::toggleTrackingSlot()
487 {
488  if (mServices->tracking()->getState() >= Tool::tsTRACKING)
489  mServices->tracking()->setState(Tool::tsINITIALIZED);
490  else
491  mServices->tracking()->setState(Tool::tsTRACKING);
492 }
493 
494 void MainWindowActions::onGotoDocumentation()
495 {
497  QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
498 }
499 
500 
501 
502 } // namespace cx
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
boost::shared_ptr< class ViewGroupData > ViewGroupDataPtr
Definition: cxViewGroup.h:29
void connected(bool on)
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:755
static QString getWebsiteUserDocumentationURL()
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:66
void startRecording(QString saveFile)
bool copyRecursively(QString sourceDir, QString destinationDir, bool overWriteDirectory)
configured with basic info
Definition: cxTool.h:75
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:58
static QImage vtkImageData2QImage(vtkImageDataPtr input)
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
#define CX_LOG_ERROR
Definition: cxLogger.h:99
MainWindowActions(VisServicesPtr services, QWidget *parent)
virtual void setState(const Tool::State val)=0
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
connected to hardware, if any, ready to use
Definition: cxTool.h:76
QAction * getAction(QString uid)
#define CX_LOG_WARNING
Definition: cxLogger.h:98
static QString findExecutableInStandardLocations(QString filename)
look for an exe in the same folder as the executable or bundle.
emitting tracking data
Definition: cxTool.h:77
boost::shared_ptr< class Navigation > NavigationPtr
VLCRecorder * vlc()
Shortcut for accessing the vlc recorder.
Namespace for all CustusX production code.