CustusX  2023.01.05-dev+develop.0da12
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  this->createAction("AddFilesForImportWithDialog", "Add files for import (Dialog)",
193  QIcon(),
194  QKeySequence(),
195  "Add files to be imported",
196  [=](){this->importDataSlot("AddFilesForImportWithDialogAction");});
197 }
198 
199 template <class T>
200 QAction* MainWindowActions::createAction(QString uid, QString text, QIcon icon,
201  QKeySequence shortcut, QString help,
202  T triggerSlot)
203 {
204  QAction* retval = new QAction(icon, text, this);
205  retval->setObjectName(uid);
206  retval->setShortcut(shortcut);
207  retval->setStatusTip(help);
208  connect(retval, &QAction::triggered, this, triggerSlot);
209  mActions[uid] = retval;
210  return retval;
211 }
212 
213 namespace
214 {
215 QString timestampFormatFolderFriendly()
216 {
217  return QString("yyyy-MM-dd_hh-mm");
218 }
219 }
220 
221 QWidget* MainWindowActions::parentWidget()
222 {
223  return dynamic_cast<QWidget*>(this->parent());
224 }
225 
226 
227 void MainWindowActions::newPatientSlot()
228 {
229  QString choosenDir = this->selectNewPatientFolder();
230  if(choosenDir.isEmpty())
231  return;
232 
233  // Update global patient number
234  int patientNumber = settings()->value("globalPatientNumber").toInt();
235  settings()->setValue("globalPatientNumber", ++patientNumber);
236 
237  mServices->session()->load(choosenDir);
238 }
239 
240 QString MainWindowActions::selectNewPatientFolder()
241 {
242  QString patientDatafolder = this->getExistingSessionFolder();
243 
244  QString timestamp = QDateTime::currentDateTime().toString(timestampFormatFolderFriendly());
245  QString filename = QString("%1_%2_%3.cx3")
246  .arg(timestamp)
247  .arg(profile()->getName())
248  .arg(settings()->value("globalPatientNumber").toString());
249 
250  QString choosenDir = patientDatafolder + "/" + filename;
251 
252  QFileDialog dialog(this->parentWidget(), tr("Select directory to save patient in"), patientDatafolder + "/");
253  dialog.setOption(QFileDialog::DontUseNativeDialog, true);
254  dialog.setOption(QFileDialog::ShowDirsOnly, true);
255  dialog.selectFile(filename);
256  if (!dialog.exec())
257  return QString();
258  choosenDir = dialog.selectedFiles().front();
259 
260  if (!choosenDir.endsWith(".cx3"))
261  choosenDir += QString(".cx3");
262 
263  return choosenDir;
264 }
265 
266 QString MainWindowActions::getExistingSessionFolder()
267 {
268  return profile()->getSessionRootFolder();
269 }
270 
271 void MainWindowActions::clearPatientSlot()
272 {
273  mServices->session()->clear();
274 }
275 
276 void MainWindowActions::savePatientFileSlot()
277 {
278  if (mServices->patient()->getActivePatientFolder().isEmpty())
279  {
280  reportWarning("No patient selected, select or create patient before saving!");
281  this->newPatientSlot();
282  return;
283  }
284 
285  mServices->session()->save();
286 }
287 
288 void MainWindowActions::loadPatientFileSlot()
289 {
290  QString patientDatafolder = this->getExistingSessionFolder();
291 
292  // Open file dialog
293  QString folder = QFileDialog::getExistingDirectory(this->parentWidget(), "Select patient", patientDatafolder, QFileDialog::ShowDirsOnly);
294  if (folder.isEmpty())
295  return;
296 
297  mServices->session()->load(folder);
298 }
299 
300 void MainWindowActions::loadPatientFileCopySlot()
301 {
302  QString patientDatafolder = profile()->getPatientTemplatePath();
303 
304  // Open file dialog
305  QString folder = QFileDialog::getExistingDirectory(this->parentWidget(), "Select template patient to copy", patientDatafolder, QFileDialog::ShowDirsOnly);
306  if (folder.isEmpty())
307  return;
308 
309  QString newFolder = this->selectNewPatientFolder();
310  if (newFolder.isEmpty())
311  return;
312 
313  if(!copyRecursively(folder, newFolder, true))
314  {
315  CX_LOG_WARNING() << "MainWindowActions::loadPatientFileCopySlot(): Cannot copy patient folder: " << folder;
316  return;
317  }
318 
319  mServices->session()->load(newFolder);
320 }
321 
322 void MainWindowActions::exportDataSlot()
323 {
324  this->savePatientFileSlot();
325 
326  ExportDataDialog* wizard = new ExportDataDialog(mServices->patient(), this->parentWidget());
327  wizard->exec(); //calling exec() makes the wizard dialog modal which prevents other user interaction with the system
328 }
329 
330 void MainWindowActions::importDataSlot(QString actionText)
331 {
332  this->savePatientFileSlot();
333 
334  QDockWidget* importDockWidget = findMainWindowChildWithObjectName<QDockWidget*>("import_widgetDockWidget");
335  if(!importDockWidget)
336  {
337  CX_LOG_ERROR() << "Cannot find DockWidget for ImportWidget";
338  return;
339  }
340 
341  importDockWidget->show();
342  importDockWidget->raise();
343 
344  QWidget* widget = findMainWindowChildWithObjectName<QWidget*>("import_widget");
345  if(!widget)
346  {
347  CX_LOG_ERROR() << "Cannot find ImportWidget";
348  return;
349  }
350 
351  bool actionFound = false;
352  QList<QAction*> actions = widget->actions();
353  foreach(QAction* action, actions)
354  {
355  if(action->text().contains(actionText))
356  {
357  actionFound = true;
358  action->trigger();
359  }
360  }
361  if(!actionFound)
362  CX_LOG_ERROR() << "MainWindowActions::importDataSlot, action not found: " << actionText;
363 }
364 
365 void MainWindowActions::shootScreen()
366 {
367  mScreenShotWriter->grabAllScreensToFile();
368 }
369 
370 void MainWindowActions::shootWindow()
371 {
372 // this->shootOneLayout(0);
373 
374  int index=0;
375  while (mServices->view()->getLayoutWidget(index))
376  this->shootOneLayout(index++);
377 }
378 
379 void MainWindowActions::shootOneLayout(int index)
380 {
381  QWidget* widget = mServices->view()->getLayoutWidget(index);
382  ViewCollectionWidget* vcWidget = dynamic_cast<ViewCollectionWidget*>(widget);
383 
384  ViewCollectionImageWriter grabber(vcWidget);
386  mScreenShotWriter->save(pm, QString("_layout%1").arg(index));
387 }
388 
389 void MainWindowActions::recordFullscreen()
390 {
391  QString path = mServices->patient()->generateFilePath("Screenshots", "mp4");
392 
393  if(vlc()->isRecording())
394  vlc()->stopRecording();
395  else
396  vlc()->startRecording(path);
397 }
398 
399 void MainWindowActions::updateRecordFullscreenActionSlot()
400 {
401  mRecordFullscreenStreamingAction->setCheckable(true);
402  mRecordFullscreenStreamingAction->blockSignals(true);
403  mRecordFullscreenStreamingAction->setChecked(vlc()->isRecording());
404  mRecordFullscreenStreamingAction->blockSignals(false);
405 
406  if(vlc()->isRecording())
407  {
408  mRecordFullscreenStreamingAction->setIcon(QIcon(":/icons/Video-icon_green.png"));
409  }
410  else
411  {
412  mRecordFullscreenStreamingAction->setIcon(QIcon(":/icons/Video-icon_gray.png"));
413  }
414 
415 }
416 
417 void MainWindowActions::onStartLogConsole()
418 {
419  QString fullname = DataLocations::findExecutableInStandardLocations("LogConsole");
420 // std::cout << "MainWindowActions::onStartLogConsole() " << fullname << std::endl;
421  mLocalVideoServerProcess.reset(new ProcessWrapper(QString("LogConsole")));
422  mLocalVideoServerProcess->launchWithRelativePath(fullname, QStringList());
423 }
424 
425 void MainWindowActions::toggleStreamingSlot()
426 {
427  if (mServices->video()->isConnected())
428  mServices->video()->closeConnection();
429  else
430  mServices->video()->openConnection();
431 }
432 
433 void MainWindowActions::updateStreamingActionSlot()
434 {
435  if (mServices->video()->isConnected())
436  {
437  mStartStreamingAction->setIcon(QIcon(":/icons/streaming_green.png"));
438  mStartStreamingAction->setText("Stop Streaming");
439  }
440  else
441  {
442  mStartStreamingAction->setIcon(QIcon(":/icons/streaming_red.png"));
443  mStartStreamingAction->setText("Start Streaming");
444  }
445 }
446 
447 void MainWindowActions::centerToImageCenterSlot()
448 {
449  mServices->view()->centerToImageCenterInActiveViewGroup();
450 }
451 
452 void MainWindowActions::centerToTooltipSlot()
453 {
454  NavigationPtr nav = mServices->view()->getNavigation();
455  nav->centerToTooltip();
456 }
457 
458 void MainWindowActions::togglePointPickerActionSlot()
459 {
460  ViewGroupDataPtr data = mServices->view()->getGroup(0);
461  ViewGroupData::Options options = data->getOptions();
462  options.mShowPointPickerProbe = !options.mShowPointPickerProbe;
463  data->setOptions(options);
464 }
465 void MainWindowActions::updatePointPickerActionSlot()
466 {
467  if (!mServices->view()->getGroup(0))
468  return;
469  bool show = mServices->view()->getGroup(0)->getOptions().mShowPointPickerProbe;
470  mShowPointPickerAction->setChecked(show);
471 }
472 
473 void MainWindowActions::updateTrackingActionSlot()
474 {
475  if (mServices->tracking()->getState() >= Tool::tsTRACKING)
476  {
477  mTrackingToolsAction->setIcon(QIcon(":/icons/polaris-green.png"));
478  mTrackingToolsAction->setText("Stop Tracking");
479  }
480  else
481  {
482  mTrackingToolsAction->setIcon(QIcon(":/icons/polaris-red.png"));
483  mTrackingToolsAction->setText("Start Tracking");
484  }
485 }
486 
487 void MainWindowActions::configureSlot()
488 {
489  mServices->tracking()->setState(Tool::tsCONFIGURED);
490 }
491 
492 void MainWindowActions::toggleTrackingSlot()
493 {
494  if (mServices->tracking()->getState() >= Tool::tsTRACKING)
495  mServices->tracking()->setState(Tool::tsINITIALIZED);
496  else
497  mServices->tracking()->setState(Tool::tsTRACKING);
498 }
499 
500 void MainWindowActions::onGotoDocumentation()
501 {
503  QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
504 }
505 
506 
507 
508 } // 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
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)
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.