Package Gnumed :: Package wxpython :: Module gmProviderInboxWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmProviderInboxWidgets

  1  """GNUmed provider inbox handling widgets. 
  2  """ 
  3  #================================================================ 
  4  __version__ = "$Revision: 1.48 $" 
  5  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  6   
  7  import sys, logging 
  8   
  9   
 10  import wx 
 11   
 12   
 13  if __name__ == '__main__': 
 14          sys.path.insert(0, '../../') 
 15  from Gnumed.pycommon import gmI18N, gmDispatcher, gmTools, gmCfg, gmPG2, gmExceptions 
 16  from Gnumed.business import gmPerson, gmSurgery 
 17  from Gnumed.wxpython import gmGuiHelpers, gmListWidgets, gmPlugin, gmRegetMixin, gmPhraseWheel 
 18  from Gnumed.wxpython import gmEditArea, gmAuthWidgets, gmPatSearchWidgets, gmVaccWidgets, gmCfgWidgets 
 19  from Gnumed.wxGladeWidgets import wxgProviderInboxPnl, wxgTextExpansionEditAreaPnl 
 20   
 21   
 22  _log = logging.getLogger('gm.ui') 
 23  _log.info(__version__) 
 24   
 25  _indicator = { 
 26          -1: '', 
 27          0: '', 
 28          1: '*!!*' 
 29  } 
 30  #============================================================ 
31 -class cTextExpansionEditAreaPnl(wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl):
32
33 - def __init__(self, *args, **kwds):
34 35 try: 36 self.__keyword = kwds['keyword'] 37 del kwds['keyword'] 38 except KeyError: 39 self.__keyword = None 40 41 wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl.__init__(self, *args, **kwds) 42 43 self.__init_ui() 44 self.__register_interests()
45 #--------------------------------------------------------
46 - def save(self):
47 if not self.__valid_for_save(): 48 return False 49 50 if self.__keyword is None: 51 result = gmPG2.add_text_expansion ( 52 keyword = self._TCTRL_keyword.GetValue().strip(), 53 expansion = self._TCTRL_expansion.GetValue(), 54 public = self._RBTN_public.GetValue() 55 ) 56 else: 57 gmPG2.edit_text_expansion ( 58 keyword = self._TCTRL_keyword.GetValue().strip(), 59 expansion = self._TCTRL_expansion.GetValue() 60 ) 61 result = True 62 63 return result
64 #--------------------------------------------------------
65 - def refresh(self):
66 self.__init_ui()
67 # if self.__keyword is not None: 68 # self._TCTRL_expansion.SetValue(u'') 69 #-------------------------------------------------------- 70 # event handling 71 #--------------------------------------------------------
72 - def __register_interests(self):
73 self._TCTRL_keyword.Bind(wx.EVT_TEXT, self._on_keyword_modified)
74 #--------------------------------------------------------
75 - def _on_keyword_modified(self, evt):
76 if self._TCTRL_keyword.GetValue().strip() == u'': 77 self._TCTRL_expansion.Enable(False) 78 else: 79 self._TCTRL_expansion.Enable(True)
80 #-------------------------------------------------------- 81 # internal API 82 #--------------------------------------------------------
83 - def __valid_for_save(self):
84 85 kwd = self._TCTRL_keyword.GetValue().strip() 86 if kwd == u'': 87 self._TCTRL_keyword.SetBackgroundColour('pink') 88 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without keyword.'), beep = True) 89 return False 90 self._TCTRL_keyword.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 91 92 if self._TCTRL_expansion.GetValue().strip() == u'': 93 self._TCTRL_expansion.SetBackgroundColour('pink') 94 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without expansion text.'), beep = True) 95 return False 96 self._TCTRL_expansion.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 97 98 return True
99 #--------------------------------------------------------
100 - def __init_ui(self, keyword=None):
101 102 if keyword is not None: 103 self.__keyword = keyword 104 105 if self.__keyword is None: 106 self._TCTRL_keyword.SetValue(u'') 107 self._TCTRL_keyword.Enable(True) 108 self._TCTRL_expansion.SetValue(u'') 109 self._TCTRL_expansion.Enable(False) 110 self._RBTN_public.Enable(True) 111 self._RBTN_private.Enable(True) 112 self._RBTN_public.SetValue(1) 113 else: 114 expansion = gmPG2.expand_keyword(keyword = self.__keyword) 115 self._TCTRL_keyword.SetValue(self.__keyword) 116 self._TCTRL_keyword.Enable(False) 117 self._TCTRL_expansion.SetValue(gmTools.coalesce(expansion, u'')) 118 self._TCTRL_expansion.Enable(True) 119 self._RBTN_public.Enable(False) 120 self._RBTN_private.Enable(False)
121 #============================================================
122 -def configure_keyword_text_expansion(parent=None):
123 124 if parent is None: 125 parent = wx.GetApp().GetTopWindow() 126 127 #---------------------- 128 def delete(keyword=None): 129 gmPG2.delete_text_expansion(keyword = keyword) 130 return True
131 #---------------------- 132 def edit(keyword=None): 133 # add new keyword 134 ea = cTextExpansionEditAreaPnl(parent, -1, keyword=keyword) 135 dlg = gmEditArea.cGenericEditAreaDlg(parent, -1, edit_area = ea) 136 dlg.SetTitle ( 137 gmTools.coalesce(keyword, _('Adding text expansion'), _('Editing text expansion "%s"')) 138 ) 139 if dlg.ShowModal() == wx.ID_OK: 140 return True 141 142 return False 143 #---------------------- 144 def refresh(lctrl=None): 145 kwds = [ [ 146 r[0], 147 gmTools.bool2subst(r[1], gmTools.u_checkmark_thick, u''), 148 gmTools.bool2subst(r[2], gmTools.u_checkmark_thick, u''), 149 r[3] 150 ] for r in gmPG2.get_text_expansion_keywords() 151 ] 152 data = [ r[0] for r in gmPG2.get_text_expansion_keywords() ] 153 lctrl.set_string_items(kwds) 154 lctrl.set_data(data) 155 #---------------------- 156 157 gmListWidgets.get_choices_from_list ( 158 parent = parent, 159 msg = _('\nSelect the keyword you want to edit !\n'), 160 caption = _('Editing keyword-based text expansions ...'), 161 columns = [_('Keyword'), _('Public'), _('Private'), _('Owner')], 162 single_selection = True, 163 edit_callback = edit, 164 new_callback = edit, 165 delete_callback = delete, 166 refresh_callback = refresh 167 ) 168 #============================================================
169 -def configure_fallback_primary_provider(parent=None):
170 171 if parent is None: 172 parent = wx.GetApp().GetTopWindow() 173 174 staff = gmPerson.get_staff_list() 175 choices = [ [ 176 s[u'short_alias'], 177 u'%s%s %s' % ( 178 gmTools.coalesce(s['title'], u'', u'%s '), 179 s['firstnames'], 180 s['lastnames'] 181 ), 182 s['role'], 183 gmTools.coalesce(s['comment'], u'') 184 ] 185 for s in staff 186 if s['is_active'] is True 187 ] 188 data = [ s['pk_staff'] for s in staff if s['is_active'] is True ] 189 190 gmCfgWidgets.configure_string_from_list_option ( 191 parent = parent, 192 message = _( 193 '\n' 194 'Please select the provider to fall back to in case\n' 195 'no primary provider is configured for a patient.\n' 196 ), 197 option = 'patient.fallback_primary_provider', 198 bias = 'user', 199 default_value = None, 200 choices = choices, 201 columns = [_('Alias'), _('Provider'), _('Role'), _('Comment')], 202 data = data, 203 caption = _('Configuring fallback primary provider') 204 )
205 #============================================================
206 -class cProviderPhraseWheel(gmPhraseWheel.cPhraseWheel):
207
208 - def __init__(self, *args, **kwargs):
209 210 gmPhraseWheel.cPhraseWheel.__init__ ( 211 self, 212 *args, 213 **kwargs 214 ) 215 self.matcher = gmPerson.cMatchProvider_Provider() 216 self.SetToolTipString(_('Select a healthcare provider.')) 217 self.selection_only = True
218 #============================================================ 219 # practice related widgets 220 #============================================================
221 -def show_audit_trail(parent=None):
222 223 if parent is None: 224 parent = wx.GetApp().GetTopWindow() 225 226 #----------------------------------- 227 def refresh(lctrl): 228 229 cmd = u'SELECT * FROM audit.v_audit_trail ORDER BY audit_when_ts' 230 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 231 232 233 234 lctrl.set_string_items ( 235 [ [ 236 r['event_when'], 237 r['event_by'], 238 u'%s %s %s' % ( 239 gmTools.coalesce(r['row_version_before'], gmTools.u_diameter), 240 gmTools.u_right_arrow, 241 gmTools.coalesce(r['row_version_after'], gmTools.u_diameter) 242 ), 243 r['event_table'], 244 r['event'], 245 r['pk_audit'] 246 ] for r in rows ] 247 )
248 #lctrl.set_selections(selections = sels) 249 #----------------------------------- 250 gmListWidgets.get_choices_from_list ( 251 parent = parent, 252 msg = u'', 253 caption = _('GNUmed database audit log ...'), 254 columns = [ _('When'), _('Who'), _('Revisions'), _('Table'), _('Event'), '#' ], 255 single_selection = True, 256 refresh_callback = refresh 257 ) 258 259 #============================================================ 260 # FIXME: this should be moved elsewhere !
261 -def configure_workplace_plugins(parent=None):
262 263 if parent is None: 264 parent = wx.GetApp().GetTopWindow() 265 266 #----------------------------------- 267 def delete(workplace): 268 269 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 270 if workplace == curr_workplace: 271 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active workplace.'), beep = True) 272 return False 273 274 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 275 parent, 276 -1, 277 caption = _('Deleting workplace ...'), 278 question = _('Are you sure you want to delete this workplace ?\n\n "%s"\n') % workplace, 279 show_checkbox = True, 280 checkbox_msg = _('delete configuration, too'), 281 checkbox_tooltip = _( 282 'Check this if you want to delete all configuration items\n' 283 'for this workplace along with the workplace itself.' 284 ), 285 button_defs = [ 286 {'label': _('Delete'), 'tooltip': _('Yes, delete this workplace.'), 'default': True}, 287 {'label': _('Do NOT delete'), 'tooltip': _('No, do NOT delete this workplace'), 'default': False} 288 ] 289 ) 290 291 decision = dlg.ShowModal() 292 if decision != wx.ID_YES: 293 dlg.Destroy() 294 return False 295 296 include_cfg = dlg.checkbox_is_checked() 297 dlg.Destroy() 298 299 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('delete workplace')) 300 if not dbo_conn: 301 return False 302 303 gmSurgery.delete_workplace(workplace = workplace, conn = dbo_conn, delete_config = include_cfg) 304 return True
305 #----------------------------------- 306 def edit(workplace=None): 307 308 available_plugins = gmPlugin.get_installed_plugins(plugin_dir='gui') 309 310 dbcfg = gmCfg.cCfgSQL() 311 312 if workplace is None: 313 dlg = wx.TextEntryDialog ( 314 parent = parent, 315 message = _('Enter a descriptive name for the new workplace:'), 316 caption = _('Configuring GNUmed workplaces ...'), 317 defaultValue = u'', 318 style = wx.OK | wx.CENTRE 319 ) 320 dlg.ShowModal() 321 workplace = dlg.GetValue().strip() 322 if workplace == u'': 323 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...')) 324 return False 325 curr_plugins = [] 326 choices = available_plugins 327 else: 328 curr_plugins = gmTools.coalesce(dbcfg.get2 ( 329 option = u'horstspace.notebook.plugin_load_order', 330 workplace = workplace, 331 bias = 'workplace' 332 ), [] 333 ) 334 choices = curr_plugins[:] 335 for p in available_plugins: 336 if p not in choices: 337 choices.append(p) 338 339 sels = range(len(curr_plugins)) 340 new_plugins = gmListWidgets.get_choices_from_list ( 341 parent = parent, 342 msg = _( 343 '\n' 344 'Select the plugin(s) to be loaded the next time\n' 345 'the client is restarted under the workplace:\n' 346 '\n' 347 ' [%s]' 348 '\n' 349 ) % workplace, 350 caption = _('Configuring GNUmed workplaces ...'), 351 choices = choices, 352 selections = sels, 353 columns = [_('Plugins')], 354 single_selection = False 355 ) 356 357 if new_plugins == curr_plugins: 358 return True 359 360 if new_plugins is None: 361 return True 362 363 dbcfg.set ( 364 option = u'horstspace.notebook.plugin_load_order', 365 value = new_plugins, 366 workplace = workplace 367 ) 368 369 return True 370 #----------------------------------- 371 def refresh(lctrl): 372 workplaces = gmSurgery.gmCurrentPractice().workplaces 373 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 374 try: 375 sels = [workplaces.index(curr_workplace)] 376 except ValueError: 377 sels = [] 378 379 lctrl.set_string_items(workplaces) 380 lctrl.set_selections(selections = sels) 381 #----------------------------------- 382 gmListWidgets.get_choices_from_list ( 383 parent = parent, 384 msg = _( 385 '\nSelect the workplace to configure below.\n' 386 '\n' 387 'The currently active workplace is preselected.\n' 388 ), 389 caption = _('Configuring GNUmed workplaces ...'), 390 columns = [_('Workplace')], 391 single_selection = True, 392 refresh_callback = refresh, 393 edit_callback = edit, 394 new_callback = edit, 395 delete_callback = delete 396 ) 397 #============================================================
398 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
399 400 _item_handlers = {} 401 _patient_msg_types = ['clinical.review docs', 'clinical.review results', 'clinical.review vaccs'] 402 #--------------------------------------------------------
403 - def __init__(self, *args, **kwds):
404 405 wxgProviderInboxPnl.wxgProviderInboxPnl.__init__(self, *args, **kwds) 406 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 407 408 self.provider = gmPerson.gmCurrentProvider() 409 self.filter_mode = 'all' 410 self.__init_ui() 411 412 cProviderInboxPnl._item_handlers['clinical.review docs'] = self._goto_doc_review 413 cProviderInboxPnl._item_handlers['clinical.review results'] = self._goto_measurements_review 414 cProviderInboxPnl._item_handlers['clinical.review vaccs'] = self._goto_vaccination_review 415 416 self.__register_interests()
417 #-------------------------------------------------------- 418 # reget-on-paint API 419 #--------------------------------------------------------
420 - def _populate_with_data(self):
421 self.__populate_inbox() 422 return True
423 #-------------------------------------------------------- 424 # internal helpers 425 #--------------------------------------------------------
426 - def __register_interests(self):
427 gmDispatcher.connect(signal = u'message_inbox_generic_mod_db', receiver = self._on_message_inbox_mod_db) 428 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_message_inbox_mod_db) 429 # FIXME: listen for results insertion/deletion 430 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_message_inbox_mod_db) 431 # FIXME: listen for doc insertion/deletion 432 # FIXME: listen for doc reviews 433 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
434 #--------------------------------------------------------
435 - def __init_ui(self):
436 self._LCTRL_provider_inbox.set_columns([u'', _('date'), _('category'), _('type'), _('message')]) 437 438 msg = _('\n Inbox of %(title)s %(lname)s.\n') % { 439 'title': gmTools.coalesce ( 440 self.provider['title'], 441 gmPerson.map_gender2salutation(self.provider['gender']) 442 ), 443 'lname': self.provider['lastnames'] 444 } 445 446 self._msg_welcome.SetLabel(msg) 447 448 if gmPerson.gmCurrentPatient().connected: 449 self._RBTN_active_patient.Enable()
450 #--------------------------------------------------------
451 - def __populate_inbox(self):
452 453 """Fill UI with data.""" 454 self.__msgs = self.provider.inbox.messages 455 456 if self.filter_mode == 'active': 457 if gmPerson.gmCurrentPatient().connected: 458 curr_pat_id = gmPerson.gmCurrentPatient().ID 459 self.__msgs = [ m for m in self.__msgs if m['pk_patient'] == curr_pat_id ] 460 else: 461 self.__msgs = [] 462 463 items = [ 464 [ 465 _indicator[m['importance']], 466 m['received_when'].strftime('%Y-%m-%d'), 467 m['l10n_category'], 468 m['l10n_type'], 469 m['comment'] 470 ] for m in self.__msgs 471 ] 472 self._LCTRL_provider_inbox.set_string_items(items = items) 473 self._LCTRL_provider_inbox.set_data(data = self.__msgs) 474 self._LCTRL_provider_inbox.set_column_widths()
475 #-------------------------------------------------------- 476 # event handlers 477 #--------------------------------------------------------
478 - def _on_post_patient_selection(self):
479 wx.CallAfter(self._schedule_data_reget) 480 wx.CallAfter(self._RBTN_active_patient.Enable)
481 #--------------------------------------------------------
482 - def _on_message_inbox_mod_db(self, *args, **kwargs):
483 wx.CallAfter(self._schedule_data_reget) 484 gmDispatcher.send(signal = u'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
485 #--------------------------------------------------------
486 - def _lst_item_activated(self, evt):
487 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 488 if msg is None: 489 return 490 491 handler_key = '%s.%s' % (msg['category'], msg['type']) 492 try: 493 handle_item = cProviderInboxPnl._item_handlers[handler_key] 494 except KeyError: 495 gmGuiHelpers.gm_show_warning ( 496 _( 497 """No double-click action pre-programmed into 498 GNUmed for message category and type: 499 500 [%s] 501 """ 502 ) % handler_key, 503 _('handling provider inbox item') 504 ) 505 return False 506 507 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']): 508 _log.error('item handler returned "false"') 509 _log.error('handler key: [%s]', handler_key) 510 _log.error('message: %s', str(msg)) 511 return False 512 513 return True
514 #--------------------------------------------------------
515 - def _lst_item_focused(self, evt):
516 pass
517 #--------------------------------------------------------
518 - def _lst_item_selected(self, evt):
519 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 520 if msg is None: 521 return 522 523 if msg['data'] is None: 524 tmp = _('Message: %s') % msg['comment'] 525 else: 526 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data']) 527 528 self._TXT_inbox_item_comment.SetValue(tmp)
529 #--------------------------------------------------------
530 - def _lst_item_right_clicked(self, evt):
531 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 532 if tmp is None: 533 return 534 self.__focussed_msg = tmp 535 536 # build menu 537 menu = wx.Menu(title = _('Inbox Message menu')) 538 # - delete message 539 if not self.__focussed_msg['is_virtual']: 540 ID = wx.NewId() 541 menu.AppendItem(wx.MenuItem(menu, ID, _('delete message'))) 542 wx.EVT_MENU(menu, ID, self._on_delete_focussed_msg) 543 544 # show menu 545 self.PopupMenu(menu, wx.DefaultPosition) 546 menu.Destroy()
547 #--------------------------------------------------------
549 self.filter_mode = 'all' 550 self._TXT_inbox_item_comment.SetValue(u'') 551 self.__populate_inbox()
552 #--------------------------------------------------------
554 self.filter_mode = 'active' 555 self._TXT_inbox_item_comment.SetValue(u'') 556 self.__populate_inbox()
557 #-------------------------------------------------------- 558 # item handlers 559 #--------------------------------------------------------
560 - def _on_delete_focussed_msg(self, evt):
561 if self.__focussed_msg['is_virtual']: 562 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True) 563 return False 564 565 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_message_inbox']): 566 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.')) 567 return False 568 return True
569 #--------------------------------------------------------
570 - def _goto_doc_review(self, pk_context=None, pk_patient=None):
571 wx.BeginBusyCursor() 572 573 try: 574 pat = gmPerson.cIdentity(aPK_obj = pk_patient) 575 except gmExceptions.ConstructorError: 576 wx.EndBusyCursor() 577 _log.exception('patient [%s] not found', pk_patient) 578 gmGuiHelpers.gm_show_error ( 579 _('Supposedly there are unreviewed documents\n' 580 'for patient [%s]. However, I cannot find\n' 581 'that patient in the GNUmed database.' 582 ) % pk_patient, 583 _('handling provider inbox item') 584 ) 585 return False 586 587 success = gmPatSearchWidgets.set_active_patient(patient = pat) 588 589 wx.EndBusyCursor() 590 591 if not success: 592 gmGuiHelpers.gm_show_error ( 593 _('Supposedly there are unreviewed documents\n' 594 'for patient [%s]. However, I cannot find\n' 595 'that patient in the GNUmed database.' 596 ) % pk_patient, 597 _('handling provider inbox item') 598 ) 599 return False 600 601 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmShowMedDocs', sort_mode = 'review') 602 return True
603 #--------------------------------------------------------
604 - def _goto_measurements_review(self, pk_context=None, pk_patient=None):
605 wx.BeginBusyCursor() 606 success = gmPatSearchWidgets.set_active_patient(patient=gmPerson.cIdentity(aPK_obj=pk_patient)) 607 wx.EndBusyCursor() 608 if not success: 609 gmGuiHelpers.gm_show_error ( 610 _('Supposedly there are unreviewed results\n' 611 'for patient [%s]. However, I cannot find\n' 612 'that patient in the GNUmed database.' 613 ) % pk_patient, 614 _('handling provider inbox item') 615 ) 616 return False 617 618 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin') 619 return True
620 #--------------------------------------------------------
621 - def _goto_vaccination_review(self, pk_context=None, pk_patient=None):
622 wx.BeginBusyCursor() 623 success = gmPatSearchWidgets.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = pk_patient)) 624 wx.EndBusyCursor() 625 if not success: 626 gmGuiHelpers.gm_show_error ( 627 _('Supposedly there are conflicting vaccinations\n' 628 'for patient [%s]. However, I cannot find\n' 629 'that patient in the GNUmed database.' 630 ) % pk_patient, 631 _('handling provider inbox item') 632 ) 633 return False 634 635 wx.CallAfter(gmVaccWidgets.manage_vaccinations) 636 637 return True
638 #============================================================ 639 if __name__ == '__main__': 640 641 gmI18N.activate_locale() 642 gmI18N.install_domain(domain = 'gnumed') 643
644 - def test_configure_wp_plugins():
645 app = wx.PyWidgetTester(size = (400, 300)) 646 configure_workplace_plugins()
647
648 - def test_message_inbox():
649 app = wx.PyWidgetTester(size = (800, 600)) 650 app.SetWidget(cProviderInboxPnl, -1) 651 app.MainLoop()
652 653 if len(sys.argv) > 1 and sys.argv[1] == 'test': 654 #test_configure_wp_plugins() 655 test_message_inbox() 656 657 #============================================================ 658