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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019