Main MRPT website > C++ reference for MRPT 1.9.9
WxSubsystem.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "gui-precomp.h" // Precompiled headers
11 
12 #include <mrpt/config.h>
13 
17 
18 #include <mrpt/system/os.h>
19 
20 #include <mrpt/gui/WxSubsystem.h>
21 #include <mrpt/gui/WxUtils.h>
22 
23 //#define WXSUBSYSTEM_VERBOSE
24 
25 // ------------------------------------------------------------------------
26 // Defined: Try to wait for all windows & the thread to exit cleanly.
27 // Undefined: Just to a std::this_thread::sleep_for(ms) and quit crossing our
28 // fingers.
29 //
30 // Problem with the "clean way" is: As of feb/2011, I get this error
31 // at the end:
32 // ** (MRPT:11711): CRITICAL **: giop_thread_request_push: assertion `tdata !=
33 // NULL' failed
34 // ------------------------------------------------------------------------
35 //#define WXSHUTDOWN_DO_IT_CLEAN
36 
37 #if MRPT_HAS_WXWIDGETS
38 
39 using namespace mrpt;
40 using namespace mrpt::gui;
41 using namespace mrpt::utils;
42 using namespace std;
43 
46 
47 std::queue<WxSubsystem::TRequestToWxMainThread*>*
49 std::mutex* WxSubsystem::cs_listPendingWxRequests = nullptr;
50 
52  nullptr;
53 bool isConsoleApp_value = true;
55 
57 
58 // Auxiliary class implementation:
61 {
63  {
64 #ifdef WXSUBSYSTEM_VERBOSE
65  printf("[~CAuxWxSubsystemShutdowner] Sending 999...\n");
66 #endif
67  // Shut down:
68  try
69  {
72  REQ->OPCODE = 999;
74 
75  // std::this_thread::sleep_for(100ms); // JL: I found no better way
76  // of doing this, sorry :-( See
77  // WxSubsystem::waitWxShutdownsIfNoWindows()
79  }
80  catch (...)
81  {
82  } // Just in case we got an out-of-mem error.
83  } // is console app.
84 
85 #ifdef WXSUBSYSTEM_VERBOSE
86  printf("[~CAuxWxSubsystemShutdowner] Deleting static objects.\n");
87 #endif
88  // This is the final point where all dynamic memory must be deleted:
89  // delete &WxSubsystem::GetWxMainThreadInstance(); // may cause crashes at
90  // app end...
93 }
94 
95 // ---------------------------------------------------------------------------------------
96 // Auxiliary dialog class for the "ask user to open a camera":
97 // ---------------------------------------------------------------------------------------
98 class CDialogAskUserForCamera : public wxDialog
99 {
100  public:
102 
103  static const long ID_BTN_OK;
104  static const long ID_BTN_CANCEL;
105 
107  : wxDialog(
108  nullptr, wxID_ANY, wxT("Select image source"), wxDefaultPosition,
109  wxDefaultSize, wxDEFAULT_DIALOG_STYLE, wxDialogNameStr)
110  {
111  wxFlexGridSizer* f1 = new wxFlexGridSizer(2, 1, 0, 0);
112  panel = new mrpt::gui::CPanelCameraSelection(this, wxID_ANY);
113  f1->Add(
114  panel, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
115 
116  wxFlexGridSizer* f2 = new wxFlexGridSizer(1, 2, 0, 0);
117  wxButton* btnOk = new wxButton(
118  this, ID_BTN_OK, wxT("Ok"), wxDefaultPosition, wxDefaultSize);
119  wxButton* btnCancel = new wxButton(
120  this, ID_BTN_CANCEL, wxT("Cancel"), wxDefaultPosition,
121  wxDefaultSize);
122  f1->Add(f2, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
123 
124  f2->Add(
125  btnOk, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
126  f2->Add(
127  btnCancel, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL,
128  5);
129 
130  Connect(
131  ID_BTN_OK, wxEVT_COMMAND_BUTTON_CLICKED,
132  (wxObjectEventFunction)&CDialogAskUserForCamera::OnBtnOk);
133  Connect(
134  ID_BTN_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
135  (wxObjectEventFunction)&CDialogAskUserForCamera::OnBtnCancel);
136 
137  SetSizer(f1);
138  Fit();
139 
140  btnOk->SetFocus(); // So the default params can be accepted by just
141  // pressing ENTER.
142  }
143 
145  void OnBtnOk(wxCommandEvent& event) { EndModal(wxID_OK); }
146  void OnBtnCancel(wxCommandEvent& event) { EndModal(wxID_CANCEL); }
147 };
148 
149 const long CDialogAskUserForCamera::ID_BTN_OK = wxNewId();
150 const long CDialogAskUserForCamera::ID_BTN_CANCEL = wxNewId();
151 
152 // ---------------------------------------------------------------------------------------
153 // The wx dummy frame:
154 // ---------------------------------------------------------------------------------------
155 BEGIN_EVENT_TABLE(WxSubsystem::CWXMainFrame, wxFrame)
156 
157 END_EVENT_TABLE()
158 
159 const long ID_TIMER_WX_PROCESS_REQUESTS = wxNewId();
160 
161 WxSubsystem::CWXMainFrame::CWXMainFrame(wxWindow* parent, wxWindowID id)
162 {
163  Create(
164  parent, id, _("MRPT-dummy frame window"), wxDefaultPosition,
165  wxSize(1, 1),
166  0, // wxDEFAULT_FRAME_STYLE,
167  _T("id"));
168 
169  if (oneInstance)
170  {
171  cerr << "[CWXMainFrame] More than one instance running!" << endl;
172  }
173  oneInstance = this;
174 
175  // ------------------------------------------------------------------------------------------
176  // Create a timer so requests from the main application thread can be
177  // processed regularly:
178  // ------------------------------------------------------------------------------------------
179  Connect(
180  ID_TIMER_WX_PROCESS_REQUESTS, wxEVT_TIMER,
181  (wxObjectEventFunction)&CWXMainFrame::OnTimerProcessRequests);
182  m_theTimer = new wxTimer(this, ID_TIMER_WX_PROCESS_REQUESTS);
183 
184  m_theTimer->Start(10, true); // One-shot
185 }
186 
188 {
189 #ifdef WXSUBSYSTEM_VERBOSE
190  cout << "[CWXMainFrame] Destructor." << endl;
191 #endif
192  delete m_theTimer;
193  oneInstance = nullptr;
194 
195  // Purge all pending requests:
197  while (nullptr != (msg = popPendingWxRequest())) delete[] msg;
198 }
199 
201 {
202  std::lock_guard<std::mutex> lock(cs_windowCount);
203  return ++m_windowCount;
204 }
205 
207 {
208  int ret;
209  {
210  std::lock_guard<std::mutex> lock(cs_windowCount);
211  ret = --m_windowCount;
212  }
213 
214  if (ret == 0)
215  {
216  // That was the last window... we should close the wx subsystem:
217  if (oneInstance)
218  {
219 #ifdef WXSHUTDOWN_DO_IT_CLEAN
220  CWXMainFrame* me =
221  (CWXMainFrame*)(oneInstance); // cast away the "volatile".
222  me->Close();
223 #endif
224 
225 #ifdef WXSUBSYSTEM_VERBOSE
226  cout << "[CWXMainFrame::notifyWindowDestruction] numWindows=0. "
227  "me->Close() called."
228  << endl;
229 #endif
230  }
231  }
232 
233  return ret;
234 }
235 
236 /** Thread-safe method to return the next pending request, or nullptr if there
237  * is none (After usage, FREE the memory!)
238  */
240 {
241  if (!cs_listPendingWxRequests)
242  {
243  cs_listPendingWxRequests = new std::mutex();
244  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
245  }
246 
247  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
248 
249  // Is empty?
250  if (listPendingWxRequests->empty()) return nullptr;
251 
252  TRequestToWxMainThread* ret = listPendingWxRequests->front();
253  listPendingWxRequests->pop(); // Remove from the queue
254 
255  return ret;
256 }
257 
258 /** Thread-safe method to insert a new pending request (The memory must be
259  * dinamically allocated with "new T[1]", will be freed by receiver.)
260  */
263 {
265  {
266 #ifdef WXSUBSYSTEM_VERBOSE
267  cout << "[WxSubsystem::pushPendingWxRequest] IGNORING request since "
268  "app seems already closed.\n";
269 #endif
270  delete[] data;
271  return; // wx subsystem already closed, ignore.
272  }
273 
274  if (!cs_listPendingWxRequests)
275  {
276  cs_listPendingWxRequests = new std::mutex();
277  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
278  }
279 
280  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
281  listPendingWxRequests->push(data);
282 }
283 
284 /** This method processes the pending requests from the main MRPT application
285  * thread.
286  * The requests may be to create a new window, close another one, change
287  * title, etc...
288  */
290 {
291  bool app_closed = false;
292  try
293  {
295 
296 #ifdef WXSUBSYSTEM_VERBOSE
297  cout << "[OnTimerProcessRequests] Entering" << endl;
298 #endif
299 
300  // For each pending request:
301  while (nullptr != (msg = popPendingWxRequest()))
302  {
303  // Process it:
304  switch (msg->OPCODE)
305  {
306  // CREATE NEW WINDOW
307  case 200:
308  if (msg->source2D)
309  {
310  CWindowDialog* wnd = new CWindowDialog(
311  msg->source2D, this, (wxWindowID)-1, msg->str,
312  wxSize(msg->x, msg->y));
313 
314  // Set the "m_hwnd" member of the window:
315  *((void**)msg->voidPtr) = (void*)wnd;
316 
317  // Signal to the constructor (still waiting) that the
318  // window is now ready so it can continue:
320 
321  wnd->Show();
322  }
323  break;
324  // UPDATE IMAGE
325  case 201:
326  if (msg->source2D)
327  {
328  CWindowDialog* wnd =
329  (CWindowDialog*)
330  msg->voidPtr; // msg->source2D->getWxObject();
331  if (!wnd) break;
332  wxImage* img = (wxImage*)msg->voidPtr2;
333  if (!img) break;
334 
335  wnd->m_image->AssignImage(
336  new wxBitmap(
337  *img)); // Memory will be freed by the object.
338 
339  if (wnd->m_image->GetSize().GetX() != img->GetWidth() &&
340  wnd->m_image->GetSize().GetY() != img->GetHeight())
341  {
342  wnd->m_image->SetSize(
343  img->GetWidth(), img->GetHeight());
344  wnd->m_image->SetMinSize(
345  wxSize(img->GetWidth(), img->GetHeight()));
346  wnd->m_image->SetMaxSize(
347  wxSize(img->GetWidth(), img->GetHeight()));
348  wnd->Fit();
349  // wnd->SetClientSize(img->GetWidth(),
350  // img->GetHeight());
351  }
352  delete img;
353  wnd->m_image->Refresh(false); // false: Do NOT erase
354  // background: avoid
355  // flickering
356  }
357  break;
358  // Set position
359  case 202:
360  if (msg->source2D)
361  {
362  CWindowDialog* wnd =
364  if (wnd)
365  wnd->SetSize(
366  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
367  }
368  break;
369  // Set size
370  case 203:
371  if (msg->source2D)
372  {
373  CWindowDialog* wnd =
375  if (wnd) wnd->SetClientSize(msg->x, msg->y);
376  }
377  break;
378  // Set window's title:
379  case 204:
380  if (msg->source2D)
381  {
382  CWindowDialog* wnd =
384  if (wnd) wnd->SetTitle(_U(msg->str.c_str()));
385  }
386  break;
387  // DESTROY EXISTING WINDOW:
388  case 299:
389  if (msg->source2D)
390  {
391  CWindowDialog* wnd =
393  if (wnd)
394  {
395  // delete wnd;
396  wnd->Close();
397  }
398  }
399  break;
400 
401  // CREATE NEW WINDOW
402  case 300:
403  if (msg->source3D)
404  {
405  C3DWindowDialog* wnd = new C3DWindowDialog(
406  msg->source3D, this, (wxWindowID)-1, msg->str,
407  wxSize(msg->x, msg->y));
408 
409  // Set the "m_hwnd" member of the window:
410  *((void**)msg->voidPtr) = (void*)wnd;
411 
412  // Signal to the constructor (still waiting) that the
413  // window is now ready so it can continue:
415 
416  wnd->Show();
417  }
418  break;
419  // Set position
420  case 302:
421  if (msg->source3D)
422  {
423  C3DWindowDialog* wnd =
425  if (wnd)
426  wnd->SetSize(
427  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
428  }
429  break;
430  // Set size
431  case 303:
432  if (msg->source3D)
433  {
434  C3DWindowDialog* wnd =
436  if (wnd) wnd->SetClientSize(msg->x, msg->y);
437  }
438  break;
439  // Set window's title:
440  case 304:
441  if (msg->source3D)
442  {
443  C3DWindowDialog* wnd =
445  if (wnd) wnd->SetTitle(_U(msg->str.c_str()));
446  }
447  break;
448  // FORCE REPAINT
449  case 350:
450  if (msg->source3D)
451  {
452  C3DWindowDialog* wnd =
454  if (wnd)
455  {
456  wnd->Refresh(false);
457  }
458  }
459  break;
460  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
461  // "x": enum of desired font. "y": unique index, "str": String.
462  case 360:
463  if (msg->source3D)
464  {
465  C3DWindowDialog* wnd =
467  if (wnd)
468  {
469  wnd->addTextMessage(
470  msg->vector_x[0], msg->vector_x[1], msg->str,
472  msg->vector_x[2], msg->vector_x[3],
473  msg->vector_x[4]),
474  size_t(msg->y),
476  }
477  }
478  break;
479  // Clear 2D text messages
480  case 361:
481  if (msg->source3D)
482  {
483  C3DWindowDialog* wnd =
485  if (wnd)
486  {
487  wnd->clearTextMessages();
488  }
489  }
490  break;
491  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
492  // "x": enum of desired font. "y": unique index, "str": String.
493  case 362:
494  if (msg->source3D)
495  {
496  C3DWindowDialog* wnd =
498  if (wnd)
499  {
500  wnd->addTextMessage(
501  msg->vector_x[0], msg->vector_x[1], msg->str,
503  msg->vector_x[2], msg->vector_x[3],
504  msg->vector_x[4]),
505  msg->plotName, msg->vector_x[5],
507  size_t(msg->y), msg->vector_x[6],
508  msg->vector_x[7], msg->vector_x[8] != 0,
510  msg->vector_x[9], msg->vector_x[10],
511  msg->vector_x[11]));
512  }
513  }
514  break;
515 
516  // DESTROY EXISTING WINDOW:
517  case 399:
518  if (msg->source3D)
519  {
520  C3DWindowDialog* wnd =
522  if (wnd)
523  {
524  // delete wnd;
525  wnd->Close();
526  }
527  }
528  break;
529 
530  // CREATE NEW WINDOW
531  case 400:
532  if (msg->sourcePlots)
533  {
535  msg->sourcePlots, this, (wxWindowID)-1, msg->str,
536  wxSize(msg->x, msg->y));
537 
538  // Set the "m_hwnd" member of the window:
539  *((void**)msg->voidPtr) = (void*)wnd;
540 
541  // Signal to the constructor (still waiting) that the
542  // window is now ready so it can continue:
544 
545  wnd->Show();
546  }
547  break;
548  // Set position
549  case 402:
550  if (msg->sourcePlots)
551  {
552  CWindowDialogPlots* wnd =
554  msg->sourcePlots->getWxObject();
555  if (wnd)
556  wnd->SetSize(
557  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
558  }
559  break;
560  // Set size
561  case 403:
562  if (msg->sourcePlots)
563  {
564  CWindowDialogPlots* wnd =
566  msg->sourcePlots->getWxObject();
567  if (wnd) wnd->SetClientSize(msg->x, msg->y);
568  }
569  break;
570  // Set window's title:
571  case 404:
572  if (msg->sourcePlots)
573  {
574  CWindowDialogPlots* wnd =
576  msg->sourcePlots->getWxObject();
577  if (wnd) wnd->SetTitle(_U(msg->str.c_str()));
578  }
579  break;
580  // Mouse pan
581  case 410:
582  if (msg->sourcePlots)
583  {
584  CWindowDialogPlots* wnd =
586  msg->sourcePlots->getWxObject();
587  if (wnd) wnd->m_plot->EnableMousePanZoom(msg->boolVal);
588  }
589  break;
590  // Aspect ratio
591  case 411:
592  if (msg->sourcePlots)
593  {
594  CWindowDialogPlots* wnd =
596  msg->sourcePlots->getWxObject();
597  if (wnd) wnd->m_plot->LockAspect(msg->boolVal);
598  }
599  break;
600 
601  // Zoom over a rectangle vectorx[0-1] & vectory[0-1]
602  case 412:
603  if (msg->sourcePlots)
604  {
605  CWindowDialogPlots* wnd =
607  msg->sourcePlots->getWxObject();
608  if (wnd)
609  {
610  if (msg->vector_x.size() == 2 &&
611  msg->vector_y.size() == 2)
612  {
613  wnd->m_plot->Fit(
614  msg->vector_x[0], msg->vector_x[1],
615  msg->vector_y[0], msg->vector_y[1]);
616  wnd->m_plot->LockAspect(msg->boolVal);
617  }
618  }
619  }
620  break;
621  // Axis fit, with aspect ratio fix to boolVal.
622  case 413:
623  if (msg->sourcePlots)
624  {
625  CWindowDialogPlots* wnd =
627  msg->sourcePlots->getWxObject();
628  if (wnd)
629  {
630  wnd->m_plot->LockAspect(msg->boolVal);
631  wnd->m_plot->Fit();
632  }
633  }
634  break;
635  // Clear all objects:
636  case 414:
637  if (msg->sourcePlots)
638  {
639  CWindowDialogPlots* wnd =
641  msg->sourcePlots->getWxObject();
642  if (wnd)
643  {
644  wnd->m_plot->DelAllLayers(true, true);
645  wnd->m_plot->AddLayer(new mpScaleX());
646  wnd->m_plot->AddLayer(new mpScaleY());
647  }
648  }
649  break;
650 
651  // Create/modify 2D plot
652  case 420:
653  if (msg->sourcePlots)
654  {
655  CWindowDialogPlots* wnd =
657  msg->sourcePlots->getWxObject();
658  if (wnd)
659  wnd->plot(
660  msg->vector_x, msg->vector_y, msg->str,
661  msg->plotName);
662  }
663  break;
664 
665  // Create/modify 2D ellipse
666  case 421:
667  if (msg->sourcePlots)
668  {
669  CWindowDialogPlots* wnd =
671  msg->sourcePlots->getWxObject();
672  if (wnd)
673  wnd->plotEllipse(
674  msg->vector_x, msg->vector_y, msg->str,
675  msg->plotName, msg->boolVal);
676  }
677  break;
678 
679  // Create/modify bitmap image
680  case 422:
681  if (msg->sourcePlots)
682  {
683  CWindowDialogPlots* wnd =
685  msg->sourcePlots->getWxObject();
686  if (wnd)
687  wnd->image(
688  msg->voidPtr2, msg->vector_x[0],
689  msg->vector_x[1], msg->vector_x[2],
690  msg->vector_x[3], msg->plotName);
691  }
692  break;
693 
694  // 440: Insert submenu in the popup menu. name=menu label, x=ID
695  case 440:
696  if (msg->sourcePlots)
697  {
698  CWindowDialogPlots* wnd =
700  msg->sourcePlots->getWxObject();
701  if (wnd)
702  {
703  const long MENUITEM_ID = wxNewId();
704  // Remember the association between this ID and the
705  // user ID:
706  wnd->m_ID2ID[MENUITEM_ID] = msg->x;
707 
708  wxMenu* popupMnu = wnd->m_plot->GetPopupMenu();
709  if (wnd->m_firstSubmenu)
710  {
711  wnd->m_firstSubmenu = false;
712  popupMnu->InsertSeparator(0);
713  }
714  wxMenuItem* mnuTarget = new wxMenuItem(
715  popupMnu, MENUITEM_ID,
716  _U(msg->plotName.c_str()), wxEmptyString,
717  wxITEM_NORMAL);
718  popupMnu->Insert(0, mnuTarget);
719 
720  wnd->Connect(
721  MENUITEM_ID, wxEVT_COMMAND_MENU_SELECTED,
722  (wxObjectEventFunction)&CWindowDialogPlots::
723  OnMenuSelected);
724  }
725  }
726  break;
727 
728  // DESTROY EXISTING WINDOW:
729  case 499:
730  if (msg->sourcePlots)
731  {
732  CWindowDialogPlots* wnd =
734  msg->sourcePlots->getWxObject();
735  if (wnd)
736  {
737  // delete wnd;
738  wnd->Close();
739  }
740  }
741  break;
742 
743  // CREATE NEW WINDOW
744  case 700:
745  if (msg->sourceCameraSelectDialog)
746  {
747  std::promise<void>* sem =
748  reinterpret_cast<std::promise<void>*>(msg->voidPtr);
749 
752  // Signal that the window is ready:
753  sem->set_value();
754 
755  // Show
756  const bool wasOk = (dlg->ShowModal() == wxID_OK);
757 
758  // send selection to caller:
759  std::promise<
761  promise = reinterpret_cast<std::promise<
763  msg->voidPtr2);
765 
766  // Parse selection as a config text block:
767  dlg->panel->writeConfigFromVideoSourcePanel(
768  "CONFIG", &ret.selectedConfig);
769  ret.accepted_by_user = wasOk;
770 
771  promise->set_value(ret);
772 
773  delete dlg;
774 
775  sem->set_value();
776  }
777  break;
778 
779  // wxSubsystem shutdown:
780  case 999:
781  {
782 #ifdef WXSUBSYSTEM_VERBOSE
783  cout << "[WxSubsystem:999] Shutdown" << endl;
784 #endif
785  app_closed = true; // Do NOT launch a timer again
787  ((WxSubsystem::
789  ->Close();
790 #ifdef WXSUBSYSTEM_VERBOSE
791  cout << "[WxSubsystem:999] Shutdown done" << endl;
792 #endif
793  }
794  break;
795 
796  } // end switch OPCODE
797 
798  // Free the memory:
799  delete[] msg;
800  } // end while
801  }
802  catch (...)
803  {
804  }
805 
806  if (!app_closed) m_theTimer->Start(10, true); // One-shot
807 }
808 
809 // ---------------------------------------------------------------------------------------
810 // MRPT Icons
811 // ---------------------------------------------------------------------------------------
812 const char* mrpt_default_icon_xpm[] = {"32 32 2 1",
813  " c None",
814  ". c #000000",
815  " ",
816  " ",
817  " ",
818  " ..... ..... ......... ",
819  " .... .... ... .... ",
820  " ..... .... ... ... ",
821  " . ... . ... ... ... ",
822  " . ... . ... ... ... ",
823  " . ... . ... ... ... ",
824  " . ... . ... ........ ",
825  " . ..... ... ... .... ",
826  " . ... ... ... .... ",
827  " . ... ... ... .... ",
828  " . .. ... ... .... ",
829  " ... . ..... ..... ..... ",
830  " ",
831  " ",
832  " ........ ........... ",
833  " ... .... .. ... .. ",
834  " ... ... . ... . ",
835  " ... ... ... ",
836  " ... ... ... ",
837  " ... ... ... ",
838  " ....... ... ",
839  " ... ... ",
840  " ... ... ",
841  " ... ... ",
842  " ... ... ",
843  " ..... ..... ",
844  " ",
845  " ",
846  " "};
847 
849 {
850 // To avoid an error in wx, always resize the icon to the expected size:
851 #ifdef MRPT_OS_WINDOWS
852  const wxSize iconsSize(
853  ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
854  return wxBitmap(
855  wxBitmap(mrpt_default_icon_xpm)
856  .ConvertToImage()
857  .Scale(iconsSize.x, iconsSize.y));
858 #else
859  return wxBitmap(mrpt_default_icon_xpm);
860 #endif
861 }
862 
863 // ---------------------------------------------------------------------------------------
864 // The wx app:
865 // ---------------------------------------------------------------------------------------
866 class CDisplayWindow_WXAPP : public wxApp
867 {
868  public:
869  virtual bool OnInit();
870  virtual int OnExit();
871 };
872 
874 {
875  // Starting in wxWidgets 2.9.0, we must reset numerics locale to "C",
876  // if we want numbers to use "." in all countries. The App::OnInit() is a
877  // perfect place to undo
878  // the default wxWidgets settings. (JL @ Sep-2009)
879  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
880 
881  wxInitAllImageHandlers();
882 
883  // cout << "[wxApp::OnInit] wxApplication OnInit called." << endl;
884 
885  // Create a dummy frame:
887  Frame->Hide();
888 
889  // We are ready!!
890  // cout << "[wxMainThread] Signaling semaphore." << endl;
892 
893  return true;
894 }
895 
896 // This will be called when all the windows / frames are closed.
898 {
899 #ifdef WXSUBSYSTEM_VERBOSE
900  cout << "[wxApp::OnExit] wxApplication OnExit called." << endl;
901 #endif
902 
903  std::lock_guard<std::mutex> lock(
904  WxSubsystem::GetWxMainThreadInstance().m_csWxMainThreadId);
905 
906  wxApp::OnExit();
907  CleanUp();
908  return 0;
909 }
910 
911 /** This method must be called in the destructor of the user class FROM THE MAIN
912  * THREAD, in order to wait for the shutdown of the wx thread if this was the
913  * last open window.
914  */
916 {
917 #ifndef WXSHUTDOWN_DO_IT_CLEAN
918 
919 #ifdef WXSUBSYSTEM_VERBOSE
920  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Doing a quick "
921  "std::this_thread::sleep_for(ms) and returning.\n";
922 #endif
923  std::this_thread::sleep_for(100ms);
924  return;
925 #else
926  // Just let know a global object that, at its destruction, it must ....
927  // Any open windows?
928  int nOpenWnds;
929  {
930  std::lock_guard<std::mutex> lock(CWXMainFrame::cs_windowCount);
931  nOpenWnds = CWXMainFrame::m_windowCount;
932  }
933 
934  if (!nOpenWnds && WxSubsystem::isConsoleApp())
935  {
936 #ifdef WXSUBSYSTEM_VERBOSE
937  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Waiting for "
938  "WxWidgets thread to shutdown...\n";
939 #endif
940 
941  // Then we must be shutting down in the wx thread (we are in the main
942  // MRPT application thread)...
943  // Wait until wx is safely shut down:
944  bool done = false;
945  int maxTimeout =
946 #ifdef _DEBUG
947  30000;
948 #else
949  5000;
950 #endif
951  if (m_done.wait_for(std::chrono::milliseconds(maxTimeout)) ==
952  std::future_status::timeout)
953  {
954  cerr << "[WxSubsystem::waitWxShutdownsIfNoWindows] Timeout waiting "
955  "for WxWidgets thread to shutdown!"
956  << endl;
957  }
958  }
959 #endif
960 }
961 
962 wxAppConsole* mrpt_wxCreateApp()
963 {
964  wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "your program");
965  return new CDisplayWindow_WXAPP;
966 }
967 
968 // DECLARE_APP(CDisplayWindow_WXAPP)
970 
971 // Aux. funcs used in WxSubsystem::wxMainThread
972 // --------------------------------------------------
973 int mrpt_wxEntryReal(int argc, char** argv)
974 {
975  // library initialization
976  if (!wxEntryStart(argc, argv))
977  {
978 #if wxUSE_LOG
979  // flush any log messages explaining why we failed
980  delete wxLog::SetActiveTarget(nullptr);
981 #endif
982  return -1;
983  }
984 
985  // if wxEntryStart succeeded, we must call wxEntryCleanup even if the code
986  // below returns or throws
987  try
988  {
989  // app initialization
990  if (!wxTheApp->CallOnInit())
991  return -1; // don't call OnExit() if OnInit() failed
992 
993  // app execution
994  int ret = wxTheApp->OnRun();
995 
996  {
997  wxLogNull logNo; // Skip any warning in this scope.
998 
999  wxTheApp->OnExit(); // This replaces the above callOnExit class
1000  wxEntryCleanup();
1001  }
1002 
1003  return ret;
1004  }
1005  catch (...)
1006  {
1007  wxTheApp->OnUnhandledException();
1008  wxEntryCleanup();
1009  return -1;
1010  }
1011 }
1012 
1013 /*---------------------------------------------------------------
1014  wxMainThread
1015  This will be the "MAIN" of wxWidgets: It starts an application
1016  object and does not end until all the windows are closed.
1017  Only for console apps, not for user GUI apps already with wx.
1018  ---------------------------------------------------------------*/
1020 {
1021  MRPT_START
1022 
1023  // Prepare wxWidgets:
1024  int argc = 1;
1025  static const char* dummy_prog_name = "./MRPT";
1026  char* argv[2] = {const_cast<char*>(dummy_prog_name), nullptr};
1027 
1028 #ifdef WXSUBSYSTEM_VERBOSE
1029  cout << "[wxMainThread] Starting..." << endl;
1030 #endif
1031 
1032  // Are we in a console or wxGUI application????
1033  wxAppConsole* app_gui = wxApp::GetInstance();
1034  if (!app_gui)
1035  {
1036 // We are NOT in a wx application (it's a console program)
1037 // ---------------------------------------------------------
1038 #ifdef WXSUBSYSTEM_VERBOSE
1039  cout << "[wxMainThread] I am in a console app" << endl;
1040 #endif
1041  // Start a new wx application object:
1042 
1043  // JLBC OCT2008: wxWidgets little hack to enable console/gui mixed
1044  // applications:
1045  wxApp::SetInitializerFunction(
1046  (wxAppInitializerFunction)mrpt_wxCreateApp);
1047  mrpt_wxEntryReal(argc, argv);
1048 
1049 #ifdef WXSUBSYSTEM_VERBOSE
1050  cout << "[wxMainThread] Finished" << endl;
1051 #endif
1052 
1053  // Now this thread is ready. The main thread is free to end now:
1055  }
1056  else
1057  {
1058 // We are ALREADY in a wx application:
1059 // ---------------------------------------------------------
1060 #ifdef WXSUBSYSTEM_VERBOSE
1061  cout << "[wxMainThread] I am in a GUI app" << endl;
1062 #endif
1063  wxWindow* topWin = static_cast<wxApp*>(app_gui)->GetTopWindow();
1064 
1065  WxSubsystem::CWXMainFrame* Frame =
1066  new WxSubsystem::CWXMainFrame(topWin);
1067  Frame->Hide();
1068 
1069 // We are ready!!
1070 #ifdef WXSUBSYSTEM_VERBOSE
1071  cout << "[wxMainThread] Signaling semaphore." << endl;
1072 #endif
1074  .m_semWxMainThreadReady.set_value();
1075  }
1076 
1077  MRPT_END
1078 }
1079 
1081 {
1082  // static TWxMainThreadData dat;
1083  // Create as dynamic memory, since it'll be deleted in
1084  // CAuxWxSubsystemShutdowner:
1085  static TWxMainThreadData* dat = nullptr;
1086  static bool first_creat = true;
1087  if (!dat && first_creat)
1088  {
1089  first_creat = false;
1090  dat = new TWxMainThreadData;
1091  }
1092  return *dat;
1093 }
1094 
1095 /*---------------------------------------------------------------
1096  createOneInstanceMainThread
1097  ---------------------------------------------------------------*/
1099 {
1102  std::lock_guard<std::mutex> lock(wxmtd.m_csWxMainThreadId);
1103 
1104  wxAppConsole* app_con = wxApp::GetInstance();
1105  if (app_con && wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1106  {
1107  // We are NOT in a console application: There is already a wxApp
1108  // instance running and it's not us.
1109  isConsoleApp_value = false;
1110  // cout << "[createOneInstanceMainThread] Mode: User GUI." << endl;
1112  {
1113  // Create our main hidden frame:
1114  wxWindow* topWin = static_cast<wxApp*>(app_con)->GetTopWindow();
1115 
1116  WxSubsystem::CWXMainFrame* Frame =
1117  new WxSubsystem::CWXMainFrame(topWin);
1118  // Frame->Show();
1119  // SetTopWindow(Frame);
1120  Frame->Hide();
1121  }
1122  }
1123  else
1124  {
1125  // cout << "[createOneInstanceMainThread] Mode: Console." << endl;
1126  isConsoleApp_value = true;
1127  if (wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1128  {
1129 #ifdef WXSUBSYSTEM_VERBOSE
1130  printf(
1131  "[WxSubsystem::createOneInstanceMainThread] Launching "
1132  "wxMainThread() thread...\n");
1133 #endif
1134  // Create a thread for message processing there:
1135  wxmtd.m_wxMainThreadId = std::thread(wxMainThread);
1136 
1137  int maxTimeout =
1138 #ifdef _DEBUG
1139  30000;
1140 #else
1141  5000;
1142 #endif
1143 
1144  // If we have an "MRPT_WXSUBSYS_TIMEOUT_MS" environment variable,
1145  // use that timeout instead:
1146  const char* envVal = getenv("MRPT_WXSUBSYS_TIMEOUT_MS");
1147  if (envVal) maxTimeout = atoi(envVal);
1148 
1149  if (wxmtd.m_semWxMainThreadReady.get_future().wait_for(
1150  std::chrono::milliseconds(maxTimeout)) ==
1151  std::future_status::timeout) // A few secs should be enough...
1152  {
1153  cerr << "[WxSubsystem::createOneInstanceMainThread] Timeout "
1154  "waiting wxApplication to start up!"
1155  << endl;
1156  return false;
1157  }
1158  }
1159  }
1160 
1161  return true; // OK
1162 }
1163 
1164 #endif // MRPT_HAS_WXWIDGETS
An auxiliary global object used just to launch a final request to the wxSubsystem for shutdown: ...
Definition: WxSubsystem.h:125
std::map< long, long > m_ID2ID
wxIDs to user IDs for submenus.
Definition: WxSubsystem.h:461
static void pushPendingWxRequest(TRequestToWxMainThread *data)
Thread-safe method to insert a new pending request (The memory must be dinamically allocated with "ne...
void * voidPtr
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:230
virtual bool OnInit()
static const long ID_BTN_CANCEL
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
static TRequestToWxMainThread * popPendingWxRequest()
Thread-safe method to return the next pending request, or nullptr if there is none (After usage...
mrpt::gui::CDisplayWindow3D * source3D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:214
The data structure for each inter-thread request:
Definition: WxSubsystem.h:193
#define _U(x)
Definition: WxSubsystem.h:506
static int notifyWindowCreation()
Atomically increments the number of windows created with the main frame as parent.
void AssignImage(wxBitmap *img)
Assigns this image.
void OnTimerProcessRequests(wxTimerEvent &event)
This method processes the pending requests from the main MRPT application thread. ...
static std::mutex * cs_listPendingWxRequests
Definition: WxSubsystem.h:324
std::thread m_wxMainThreadId
The thread ID of wxMainThread, or 0 if it is not running.
Definition: WxSubsystem.h:174
mrpt::gui::CPanelCameraSelection * panel
The wx dialog for gui::CDisplayWindowPlots.
Definition: WxSubsystem.h:441
static wxBitmap getMRPTDefaultIcon()
STL namespace.
const char * mrpt_default_icon_xpm[]
static volatile CWXMainFrame * oneInstance
Definition: WxSubsystem.h:157
std::mutex m_csWxMainThreadId
The critical section for accessing "m_wxMainThreadId".
Definition: WxSubsystem.h:179
static TWxMainThreadData & GetWxMainThreadInstance()
void OnBtnOk(wxCommandEvent &event)
int mrpt_wxEntryReal(int argc, char **argv)
int OPCODE
Valid codes are: For CDisplayWindow:
Definition: WxSubsystem.h:300
const long ID_TIMER_WX_PROCESS_REQUESTS
static std::queue< TRequestToWxMainThread * > * listPendingWxRequests
Do not access directly to this, use the thread-safe functions.
Definition: WxSubsystem.h:323
wxMRPTImageControl * m_image
Definition: WxSubsystem.h:378
static const long ID_BTN_OK
TOpenGLFont
Existing fonts for 2D texts in mrpt::opengl methods.
Definition: opengl_fonts.h:25
wxAppConsole * mrpt_wxCreateApp()
#define MRPT_END
void addTextMessage(const double x_frac, const double y_frac, const std::string &text, const mrpt::utils::TColorf &color, const size_t unique_index, const mrpt::opengl::TOpenGLFont font)
std::promise< void > m_semWxMainThreadReady
This is signaled when wxMainThread is ready.
Definition: WxSubsystem.h:176
GLint GLvoid * img
Definition: glext.h:3763
TOpenGLFontStyle
Different style for vectorized font rendering.
Definition: opengl_fonts.h:36
void * getWxObject()
Read-only access to the wxDialog object.
void plot(const mrpt::math::CVectorFloat &x, const mrpt::math::CVectorFloat &y, const std::string &lineFormat, const std::string &plotName)
Redirected from CDisplayWindowPlots::plot.
The main frame of the wxWidgets application.
Definition: WxSubsystem.h:136
The wx dialog for gui::CDisplayWindow.
Definition: WxSubsystem.h:332
void OnBtnCancel(wxCommandEvent &event)
bool sourceCameraSelectDialog
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:222
virtual int OnExit()
static void wxMainThread()
This will be the "MAIN" of wxWidgets: It starts an application object and does not end until all the ...
virtual ~CDialogAskUserForCamera()
void image(void *theWxImage, const float &x0, const float &y0, const float &w, const float &h, const std::string &plotName)
Redirected from CDisplayWindowPlots::image.
static void waitWxShutdownsIfNoWindows()
This method must be called in the destructor of the user class FROM THE MAIN THREAD, in order to wait for the shutdown of the wx thread if this was the last open window.
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
This class implements the GUI thread required for the wxWidgets-based GUI.
Definition: WxSubsystem.h:100
CDisplayWindow_WXAPP & wxGetApp()
void plotEllipse(const mrpt::math::CVectorFloat &x, const mrpt::math::CVectorFloat &y, const std::string &lineFormat, const std::string &plotName, bool showName=false)
Redirected from CDisplayWindowPlots::plotEllipse.
GLuint id
Definition: glext.h:3909
A RGB color - floats in the range [0,1].
Definition: TColor.h:78
static CAuxWxSubsystemShutdowner global_wxsubsystem_shutdown
Definition: WxSubsystem.h:132
A panel to select the camera input from all the formats supported by MRPT.
Definition: WxUtils.h:173
mrpt::gui::CDisplayWindowPlots * sourcePlots
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:218
static bool isConsoleApp()
Will be set to true at runtime if it&#39;s not detected a running wxApp instance.
Definition: WxSubsystem.cpp:54
mrpt::utils::CConfigFileMemory selectedConfig
Definition: WxUtils.h:298
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:16
mrpt::gui::CDisplayWindow * source2D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:210
bool isConsoleApp_value
Definition: WxSubsystem.cpp:53
std::string str
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:226
void notifySemThreadReady()
Called by wx main thread to signal the semaphore that the wx window is built and ready.
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:3546
static bool createOneInstanceMainThread()
Thread-safe method to create one single instance of the main wxWidgets thread: it will create the thr...
static int notifyWindowDestruction()
Atomically decrements the number of windows created with the main frame as parent.
bool m_firstSubmenu
to know whether to insert a separator the first time.
Definition: WxSubsystem.h:459



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019