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

Source Code for Module Gnumed.wxpython.gmGuiMain

   1  # -*- coding: utf8 -*- 
   2  """GNUmed GUI client. 
   3   
   4  This contains the GUI application framework and main window 
   5  of the all signing all dancing GNUmed Python Reference 
   6  client. It relies on the <gnumed.py> launcher having set up 
   7  the non-GUI-related runtime environment. 
   8   
   9  This source code is protected by the GPL licensing scheme. 
  10  Details regarding the GPL are available at http://www.gnu.org 
  11  You may use and share it as long as you don't deny this right 
  12  to anybody else. 
  13   
  14  copyright: authors 
  15  """ 
  16  #============================================================================== 
  17  __version__ = "$Revision: 1.491 $" 
  18  __author__  = "H. Herb <hherb@gnumed.net>,\ 
  19                             K. Hilbert <Karsten.Hilbert@gmx.net>,\ 
  20                             I. Haywood <i.haywood@ugrad.unimelb.edu.au>" 
  21  __license__ = 'GPL (details at http://www.gnu.org)' 
  22   
  23  # stdlib 
  24  import sys, time, os, locale, os.path, datetime as pyDT 
  25  import webbrowser, shutil, logging, urllib2, subprocess, glob 
  26   
  27   
  28  # 3rd party libs 
  29  # wxpython version cannot be enforced inside py2exe and friends 
  30  if not hasattr(sys, 'frozen'): 
  31          import wxversion 
  32          wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) 
  33   
  34  try: 
  35          import wx 
  36          import wx.lib.pubsub 
  37  except ImportError: 
  38          print "GNUmed startup: Cannot import wxPython library." 
  39          print "GNUmed startup: Make sure wxPython is installed." 
  40          print 'CRITICAL ERROR: Error importing wxPython. Halted.' 
  41          raise 
  42   
  43  # do this check just in case, so we can make sure 
  44  # py2exe and friends include the proper version, too 
  45  version = int(u'%s%s' % (wx.MAJOR_VERSION, wx.MINOR_VERSION)) 
  46  if (version < 28) or ('unicode' not in wx.PlatformInfo): 
  47          print "GNUmed startup: Unsupported wxPython version (%s: %s)." % (wx.VERSION_STRING, wx.PlatformInfo) 
  48          print "GNUmed startup: wxPython 2.8+ with unicode support is required." 
  49          print 'CRITICAL ERROR: Proper wxPython version not found. Halted.' 
  50          raise ValueError('wxPython 2.8+ with unicode support not found') 
  51   
  52   
  53  # GNUmed libs 
  54  from Gnumed.pycommon import gmCfg, gmPG2, gmDispatcher, gmGuiBroker, gmI18N 
  55  from Gnumed.pycommon import gmExceptions, gmShellAPI, gmTools, gmDateTime 
  56  from Gnumed.pycommon import gmHooks, gmBackendListener, gmCfg2, gmLog2 
  57   
  58  from Gnumed.business import gmPerson, gmClinicalRecord, gmSurgery, gmEMRStructItems 
  59   
  60  from Gnumed.exporters import gmPatientExporter 
  61   
  62  from Gnumed.wxpython import gmGuiHelpers, gmHorstSpace, gmEMRBrowser 
  63  from Gnumed.wxpython import gmDemographicsWidgets, gmEMRStructWidgets 
  64  from Gnumed.wxpython import gmPatSearchWidgets, gmAllergyWidgets, gmListWidgets 
  65  from Gnumed.wxpython import gmProviderInboxWidgets, gmCfgWidgets, gmExceptionHandlingWidgets 
  66  from Gnumed.wxpython import gmNarrativeWidgets, gmPhraseWheel, gmMedicationWidgets 
  67  from Gnumed.wxpython import gmStaffWidgets, gmDocumentWidgets, gmTimer, gmMeasurementWidgets 
  68  from Gnumed.wxpython import gmFormWidgets, gmSnellen, gmVaccWidgets 
  69   
  70  try: 
  71          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
  72  except NameError: 
  73          _ = lambda x:x 
  74   
  75  _cfg = gmCfg2.gmCfgData() 
  76  _provider = None 
  77  _scripting_listener = None 
  78   
  79  _log = logging.getLogger('gm.main') 
  80  _log.info(__version__) 
  81  _log.info('wxPython GUI framework: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo)) 
  82   
  83  #============================================================================== 
84 -class gmTopLevelFrame(wx.Frame):
85 """GNUmed client's main windows frame. 86 87 This is where it all happens. Avoid popping up any other windows. 88 Most user interaction should happen to and from widgets within this frame 89 """ 90 #----------------------------------------------
91 - def __init__(self, parent, id, title, size=wx.DefaultSize):
92 """You'll have to browse the source to understand what the constructor does 93 """ 94 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE) 95 96 self.__gb = gmGuiBroker.GuiBroker() 97 self.__pre_exit_callbacks = [] 98 self.bar_width = -1 99 self.menu_id2plugin = {} 100 101 _log.info('workplace is >>>%s<<<', gmSurgery.gmCurrentPractice().active_workplace) 102 103 self.__setup_main_menu() 104 self.setup_statusbar() 105 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % ( 106 gmTools.coalesce(_provider['title'], ''), 107 _provider['firstnames'][:1], 108 _provider['lastnames'], 109 _provider['short_alias'], 110 _provider['db_user'] 111 )) 112 113 self.__set_window_title_template() 114 self.__update_window_title() 115 116 #icon_bundle = wx.IconBundle() 117 #icon_bundle.AddIcon(wx.Icon("my_icon_16_16.ico", wx.BITMAP_TYPE_ICO)) 118 #icon_bundle.AddIcon(wx.Icon("my_icon_32_32.ico", wx.BITMAP_TYPE_ICO)) 119 #self.SetIcons(icon_bundle) 120 self.SetIcon(gmTools.get_icon(wx = wx)) 121 122 self.__register_events() 123 124 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1) 125 self.vbox = wx.BoxSizer(wx.VERTICAL) 126 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1) 127 128 self.SetAutoLayout(True) 129 self.SetSizerAndFit(self.vbox) 130 131 # don't allow the window to get too small 132 # setsizehints only allows minimum size, therefore window can't become small enough 133 # effectively we need the font size to be configurable according to screen size 134 #self.vbox.SetSizeHints(self) 135 self.__set_GUI_size()
136 #----------------------------------------------
137 - def __set_GUI_size(self):
138 """Try to get previous window size from backend.""" 139 140 cfg = gmCfg.cCfgSQL() 141 142 # width 143 width = int(cfg.get2 ( 144 option = 'main.window.width', 145 workplace = gmSurgery.gmCurrentPractice().active_workplace, 146 bias = 'workplace', 147 default = 800 148 )) 149 150 # height 151 height = int(cfg.get2 ( 152 option = 'main.window.height', 153 workplace = gmSurgery.gmCurrentPractice().active_workplace, 154 bias = 'workplace', 155 default = 600 156 )) 157 158 dw = wx.DisplaySize()[0] 159 dh = wx.DisplaySize()[1] 160 161 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 162 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM())) 163 _log.debug('previous GUI size [%s:%s]', width, height) 164 165 # max size 166 if width > dw: 167 _log.debug('adjusting GUI width from %s to %s', width, dw) 168 width = dw 169 170 if height > dh: 171 _log.debug('adjusting GUI height from %s to %s', height, dh) 172 height = dh 173 174 # min size 175 if width < 100: 176 _log.debug('adjusting GUI width to minimum of 100 pixel') 177 width = 100 178 if height < 100: 179 _log.debug('adjusting GUI height to minimum of 100 pixel') 180 height = 100 181 182 _log.info('setting GUI to size [%s:%s]', width, height) 183 184 self.SetClientSize(wx.Size(width, height))
185 #----------------------------------------------
186 - def __setup_main_menu(self):
187 """Create the main menu entries. 188 189 Individual entries are farmed out to the modules. 190 """ 191 global wx 192 self.mainmenu = wx.MenuBar() 193 self.__gb['main.mainmenu'] = self.mainmenu 194 195 # -- menu "GNUmed" ----------------- 196 menu_gnumed = wx.Menu() 197 198 self.menu_plugins = wx.Menu() 199 menu_gnumed.AppendMenu(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins) 200 201 ID = wx.NewId() 202 menu_gnumed.Append(ID, _('Check for updates'), _('Check for new releases of the GNUmed client.')) 203 wx.EVT_MENU(self, ID, self.__on_check_for_updates) 204 205 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.')) 206 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item) 207 208 # -- 209 menu_gnumed.AppendSeparator() 210 211 # GNUmed / Preferences 212 menu_config = wx.Menu() 213 menu_gnumed.AppendMenu(wx.NewId(), _('Preferences ...'), menu_config) 214 215 item = menu_config.Append(-1, _('List configuration'), _('List all configuration items stored in the database.')) 216 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item) 217 218 # GNUmed / Preferences / Database 219 menu_cfg_db = wx.Menu() 220 menu_config.AppendMenu(wx.NewId(), _('Database ...'), menu_cfg_db) 221 222 ID = wx.NewId() 223 menu_cfg_db.Append(ID, _('Language'), _('Configure the database language')) 224 wx.EVT_MENU(self, ID, self.__on_configure_db_lang) 225 226 ID = wx.NewId() 227 menu_cfg_db.Append(ID, _('Welcome message'), _('Configure the database welcome message (all users).')) 228 wx.EVT_MENU(self, ID, self.__on_configure_db_welcome) 229 230 # GNUmed / Preferences / Client 231 menu_cfg_client = wx.Menu() 232 menu_config.AppendMenu(wx.NewId(), _('Client parameters ...'), menu_cfg_client) 233 234 ID = wx.NewId() 235 menu_cfg_client.Append(ID, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.')) 236 wx.EVT_MENU(self, ID, self.__on_configure_export_chunk_size) 237 238 ID = wx.NewId() 239 menu_cfg_client.Append(ID, _('Temporary directory'), _('Configure the directory to use as scratch space for temporary files.')) 240 wx.EVT_MENU(self, ID, self.__on_configure_temp_dir) 241 242 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.')) 243 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item) 244 245 # GNUmed / Preferences / User Interface 246 menu_cfg_ui = wx.Menu() 247 menu_config.AppendMenu(wx.NewId(), _('User interface ...'), menu_cfg_ui) 248 249 # -- submenu gnumed / config / ui / docs 250 menu_cfg_doc = wx.Menu() 251 menu_cfg_ui.AppendMenu(wx.NewId(), _('Document handling ...'), menu_cfg_doc) 252 253 ID = wx.NewId() 254 menu_cfg_doc.Append(ID, _('Review dialog'), _('Configure review dialog after document display.')) 255 wx.EVT_MENU(self, ID, self.__on_configure_doc_review_dialog) 256 257 ID = wx.NewId() 258 menu_cfg_doc.Append(ID, _('UUID display'), _('Configure unique ID dialog on document import.')) 259 wx.EVT_MENU(self, ID, self.__on_configure_doc_uuid_dialog) 260 261 ID = wx.NewId() 262 menu_cfg_doc.Append(ID, _('Empty documents'), _('Whether to allow saving documents without parts.')) 263 wx.EVT_MENU(self, ID, self.__on_configure_partless_docs) 264 265 # -- submenu gnumed / config / ui / updates 266 menu_cfg_update = wx.Menu() 267 menu_cfg_ui.AppendMenu(wx.NewId(), _('Update handling ...'), menu_cfg_update) 268 269 ID = wx.NewId() 270 menu_cfg_update.Append(ID, _('Auto-check'), _('Whether to auto-check for updates at startup.')) 271 wx.EVT_MENU(self, ID, self.__on_configure_update_check) 272 273 ID = wx.NewId() 274 menu_cfg_update.Append(ID, _('Check scope'), _('When checking for updates, consider latest branch, too ?')) 275 wx.EVT_MENU(self, ID, self.__on_configure_update_check_scope) 276 277 ID = wx.NewId() 278 menu_cfg_update.Append(ID, _('URL'), _('The URL to retrieve version information from.')) 279 wx.EVT_MENU(self, ID, self.__on_configure_update_url) 280 281 # -- submenu gnumed / config / ui / patient 282 menu_cfg_pat_search = wx.Menu() 283 menu_cfg_ui.AppendMenu(wx.NewId(), _('Person ...'), menu_cfg_pat_search) 284 285 ID = wx.NewId() 286 menu_cfg_pat_search.Append(ID, _('Birthday reminder'), _('Configure birthday reminder proximity interval.')) 287 wx.EVT_MENU(self, ID, self.__on_configure_dob_reminder_proximity) 288 289 ID = wx.NewId() 290 menu_cfg_pat_search.Append(ID, _('Immediate source activation'), _('Configure immediate activation of single external person.')) 291 wx.EVT_MENU(self, ID, self.__on_configure_quick_pat_search) 292 293 ID = wx.NewId() 294 menu_cfg_pat_search.Append(ID, _('Initial plugin'), _('Configure which plugin to show right after person activation.')) 295 wx.EVT_MENU(self, ID, self.__on_configure_initial_pat_plugin) 296 297 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default province/region/state for person creation.')) 298 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item) 299 300 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.')) 301 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item) 302 303 # -- submenu gnumed / config / ui / soap handling 304 menu_cfg_soap_editing = wx.Menu() 305 menu_cfg_ui.AppendMenu(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing) 306 307 ID = wx.NewId() 308 menu_cfg_soap_editing.Append(ID, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.')) 309 wx.EVT_MENU(self, ID, self.__on_allow_multiple_new_episodes) 310 311 # GNUmed / Preferences / External tools 312 menu_cfg_ext_tools = wx.Menu() 313 menu_config.AppendMenu(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools) 314 315 # ID = wx.NewId() 316 # menu_cfg_ext_tools.Append(ID, _('IFAP command'), _('Set the command to start IFAP.')) 317 # wx.EVT_MENU(self, ID, self.__on_configure_ifap_cmd) 318 319 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.')) 320 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item) 321 322 ID = wx.NewId() 323 menu_cfg_ext_tools.Append(ID, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.')) 324 wx.EVT_MENU(self, ID, self.__on_configure_ooo_settle_time) 325 326 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.')) 327 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item) 328 329 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.')) 330 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item) 331 332 item = menu_cfg_ext_tools.Append(-1, _('FreeDiams path'), _('Set the path for the FreeDiams binary.')) 333 self.Bind(wx.EVT_MENU, self.__on_configure_freediams_cmd, item) 334 335 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.')) 336 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item) 337 338 # -- submenu gnumed / config / emr 339 menu_cfg_emr = wx.Menu() 340 menu_config.AppendMenu(wx.NewId(), _('EMR ...'), menu_cfg_emr) 341 342 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.')) 343 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item) 344 345 # -- submenu gnumed / config / emr / encounter 346 menu_cfg_encounter = wx.Menu() 347 menu_cfg_emr.AppendMenu(wx.NewId(), _('Encounter ...'), menu_cfg_encounter) 348 349 ID = wx.NewId() 350 menu_cfg_encounter.Append(ID, _('Edit on patient change'), _('Edit encounter details on changing of patients.')) 351 wx.EVT_MENU(self, ID, self.__on_cfg_enc_pat_change) 352 353 ID = wx.NewId() 354 menu_cfg_encounter.Append(ID, _('Minimum duration'), _('Minimum duration of an encounter.')) 355 wx.EVT_MENU(self, ID, self.__on_cfg_enc_min_ttl) 356 357 ID = wx.NewId() 358 menu_cfg_encounter.Append(ID, _('Maximum duration'), _('Maximum duration of an encounter.')) 359 wx.EVT_MENU(self, ID, self.__on_cfg_enc_max_ttl) 360 361 ID = wx.NewId() 362 menu_cfg_encounter.Append(ID, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.')) 363 wx.EVT_MENU(self, ID, self.__on_cfg_enc_empty_ttl) 364 365 ID = wx.NewId() 366 menu_cfg_encounter.Append(ID, _('Default type'), _('Default type for new encounters.')) 367 wx.EVT_MENU(self, ID, self.__on_cfg_enc_default_type) 368 369 # -- submenu gnumed / config / emr / episode 370 menu_cfg_episode = wx.Menu() 371 menu_cfg_emr.AppendMenu(wx.NewId(), _('Episode ...'), menu_cfg_episode) 372 373 ID = wx.NewId() 374 menu_cfg_episode.Append(ID, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.')) 375 wx.EVT_MENU(self, ID, self.__on_cfg_epi_ttl) 376 377 # -- submenu gnumed / master data 378 menu_master_data = wx.Menu() 379 menu_gnumed.AppendMenu(wx.NewId(), _('&Master data ...'), menu_master_data) 380 381 item = menu_master_data.Append(-1, _('Workplace profiles'), _('Manage the plugins to load per workplace.')) 382 self.Bind(wx.EVT_MENU, self.__on_configure_workplace, item) 383 384 menu_master_data.AppendSeparator() 385 386 item = menu_master_data.Append(-1, _('&Document types'), _('Manage the document types available in the system.')) 387 self.Bind(wx.EVT_MENU, self.__on_edit_doc_types, item) 388 389 item = menu_master_data.Append(-1, _('&Form templates'), _('Manage templates for forms and letters.')) 390 self.Bind(wx.EVT_MENU, self.__on_manage_form_templates, item) 391 392 item = menu_master_data.Append(-1, _('&Text expansions'), _('Manage keyword based text expansion macros.')) 393 self.Bind(wx.EVT_MENU, self.__on_manage_text_expansion, item) 394 395 menu_master_data.AppendSeparator() 396 397 item = menu_master_data.Append(-1, _('&Encounter types'), _('Manage encounter types.')) 398 self.Bind(wx.EVT_MENU, self.__on_manage_encounter_types, item) 399 400 item = menu_master_data.Append(-1, _('&Provinces'), _('Manage provinces (counties, territories, ...).')) 401 self.Bind(wx.EVT_MENU, self.__on_manage_provinces, item) 402 403 menu_master_data.AppendSeparator() 404 405 item = menu_master_data.Append(-1, _('Substances'), _('Manage substances in use.')) 406 self.Bind(wx.EVT_MENU, self.__on_manage_substances, item) 407 408 item = menu_master_data.Append(-1, _('Drugs'), _('Manage branded drugs.')) 409 self.Bind(wx.EVT_MENU, self.__on_manage_branded_drugs, item) 410 411 item = menu_master_data.Append(-1, _('Drug components'), _('Manage components of branded drugs.')) 412 self.Bind(wx.EVT_MENU, self.__on_manage_substances_in_brands, item) 413 414 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.')) 415 self.Bind(wx.EVT_MENU, self.__on_update_atc, item) 416 417 menu_master_data.AppendSeparator() 418 419 item = menu_master_data.Append(-1, _('Diagnostic orgs'), _('Manage diagnostic organisations (path labs etc).')) 420 self.Bind(wx.EVT_MENU, self.__on_manage_test_orgs, item) 421 422 item = menu_master_data.Append(-1, _('&Test types'), _('Manage test/measurement types.')) 423 self.Bind(wx.EVT_MENU, self.__on_manage_test_types, item) 424 425 item = menu_master_data.Append(-1, _('&Meta test types'), _('Show meta test/measurement types.')) 426 self.Bind(wx.EVT_MENU, self.__on_manage_meta_test_types, item) 427 428 item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.')) 429 self.Bind(wx.EVT_MENU, self.__on_update_loinc, item) 430 431 menu_master_data.AppendSeparator() 432 433 item = menu_master_data.Append(-1, _('Vaccines'), _('Show known vaccines.')) 434 self.Bind(wx.EVT_MENU, self.__on_manage_vaccines, item) 435 436 # -- submenu gnumed / users 437 menu_users = wx.Menu() 438 menu_gnumed.AppendMenu(wx.NewId(), _('&Users ...'), menu_users) 439 440 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user')) 441 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item) 442 443 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users')) 444 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item) 445 446 # -- 447 menu_gnumed.AppendSeparator() 448 449 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.')) 450 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item) 451 452 self.mainmenu.Append(menu_gnumed, '&GNUmed') 453 454 # -- menu "Person" --------------------------- 455 menu_patient = wx.Menu() 456 457 ID_CREATE_PATIENT = wx.NewId() 458 menu_patient.Append(ID_CREATE_PATIENT, _('Register person'), _("Register a new person with GNUmed")) 459 wx.EVT_MENU(self, ID_CREATE_PATIENT, self.__on_create_new_patient) 460 461 # item = menu_patient.Append(-1, _('Register new (old style)'), _("Register a new person with this practice")) 462 # self.Bind(wx.EVT_MENU, self.__on_create_patient, item) 463 464 ID_LOAD_EXT_PAT = wx.NewId() 465 menu_patient.Append(ID_LOAD_EXT_PAT, _('Load external'), _('Load and possibly create person from an external source.')) 466 wx.EVT_MENU(self, ID_LOAD_EXT_PAT, self.__on_load_external_patient) 467 468 ID_DEL_PAT = wx.NewId() 469 menu_patient.Append(ID_DEL_PAT, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.')) 470 wx.EVT_MENU(self, ID_DEL_PAT, self.__on_delete_patient) 471 472 item = menu_patient.Append(-1, _('&Merge persons'), _('Merge two persons into one.')) 473 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item) 474 475 menu_patient.AppendSeparator() 476 477 ID_ENLIST_PATIENT_AS_STAFF = wx.NewId() 478 menu_patient.Append(ID_ENLIST_PATIENT_AS_STAFF, _('Enlist as user'), _('Enlist current person as GNUmed user')) 479 wx.EVT_MENU(self, ID_ENLIST_PATIENT_AS_STAFF, self.__on_enlist_patient_as_staff) 480 481 # FIXME: temporary until external program framework is active 482 ID = wx.NewId() 483 menu_patient.Append(ID, _('Export to GDT'), _('Export demographics of currently active person into GDT file.')) 484 wx.EVT_MENU(self, ID, self.__on_export_as_gdt) 485 486 menu_patient.AppendSeparator() 487 488 self.mainmenu.Append(menu_patient, '&Person') 489 self.__gb['main.patientmenu'] = menu_patient 490 491 # -- menu "EMR" --------------------------- 492 menu_emr = wx.Menu() 493 self.mainmenu.Append(menu_emr, _("&EMR")) 494 self.__gb['main.emrmenu'] = menu_emr 495 496 # - submenu "show as" 497 menu_emr_show = wx.Menu() 498 menu_emr.AppendMenu(wx.NewId(), _('Show as ...'), menu_emr_show) 499 self.__gb['main.emr_showmenu'] = menu_emr_show 500 501 # - summary 502 item = menu_emr_show.Append(-1, _('Summary'), _('Show a high-level summary of the EMR.')) 503 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item) 504 505 # - search 506 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient')) 507 self.Bind(wx.EVT_MENU, self.__on_search_emr, item) 508 509 item = menu_emr.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients')) 510 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item) 511 512 # -- submenu EMR / Add, Edit 513 menu_emr_edit = wx.Menu() 514 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit) 515 516 item = menu_emr_edit.Append(-1, _('&Past history (health issue / PMH)'), _('Add a past/previous medical history item (health issue) to the EMR of the active patient')) 517 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item) 518 519 item = menu_emr_edit.Append(-1, _('&Medication'), _('Add medication / substance use entry.')) 520 self.Bind(wx.EVT_MENU, self.__on_add_medication, item) 521 522 item = menu_emr_edit.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.')) 523 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item) 524 525 item = menu_emr_edit.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.')) 526 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item) 527 528 item = menu_emr_edit.Append(-1, _('&Hospital stays'), _('Manage hospital stays.')) 529 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item) 530 531 item = menu_emr_edit.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.')) 532 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item) 533 534 item = menu_emr_edit.Append(-1, _('&Measurement(s)'), _('Add (a) measurement result(s) for the current patient.')) 535 self.Bind(wx.EVT_MENU, self.__on_add_measurement, item) 536 537 # item = menu_emr_edit.Append(-1, ) 538 # self.Bind(wx.EVT_MENU, , item) 539 540 # -- EMR, again 541 542 # # - start new encounter 543 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.')) 544 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item) 545 546 # - list encounters 547 item = menu_emr.Append(-1, _('&Encounters list'), _('List all encounters including empty ones.')) 548 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item) 549 550 # - submenu GNUmed / "export as" 551 menu_emr.AppendSeparator() 552 553 menu_emr_export = wx.Menu() 554 menu_emr.AppendMenu(wx.NewId(), _('Export as ...'), menu_emr_export) 555 # 1) ASCII 556 ID_EXPORT_EMR_ASCII = wx.NewId() 557 menu_emr_export.Append ( 558 ID_EXPORT_EMR_ASCII, 559 _('Text document'), 560 _("Export the EMR of the active patient into a text file") 561 ) 562 wx.EVT_MENU(self, ID_EXPORT_EMR_ASCII, self.OnExportEMR) 563 # 2) journal format 564 ID_EXPORT_EMR_JOURNAL = wx.NewId() 565 menu_emr_export.Append ( 566 ID_EXPORT_EMR_JOURNAL, 567 _('Journal'), 568 _("Export the EMR of the active patient as a chronological journal into a text file") 569 ) 570 wx.EVT_MENU(self, ID_EXPORT_EMR_JOURNAL, self.__on_export_emr_as_journal) 571 # 3) Medistar import format 572 ID_EXPORT_MEDISTAR = wx.NewId() 573 menu_emr_export.Append ( 574 ID_EXPORT_MEDISTAR, 575 _('MEDISTAR import format'), 576 _("GNUmed -> MEDISTAR. Export progress notes of active patient's active encounter into a text file.") 577 ) 578 wx.EVT_MENU(self, ID_EXPORT_MEDISTAR, self.__on_export_for_medistar) 579 580 # - draw a line 581 menu_emr.AppendSeparator() 582 583 # -- menu "paperwork" --------------------- 584 menu_paperwork = wx.Menu() 585 586 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.')) 587 self.Bind(wx.EVT_MENU, self.__on_new_letter, item) 588 589 self.mainmenu.Append(menu_paperwork, _('&Correspondence')) 590 591 # menu "Tools" --------------------------- 592 self.menu_tools = wx.Menu() 593 self.__gb['main.toolsmenu'] = self.menu_tools 594 self.mainmenu.Append(self.menu_tools, _("&Tools")) 595 596 ID_DICOM_VIEWER = wx.NewId() 597 viewer = _('no viewer installed') 598 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 599 viewer = u'OsiriX' 600 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]: 601 viewer = u'Aeskulap' 602 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]: 603 viewer = u'AMIDE' 604 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]: 605 viewer = u'(x)medcon' 606 self.menu_tools.Append(ID_DICOM_VIEWER, _('DICOM viewer'), _('Start DICOM viewer (%s) for CD-ROM (X-Ray, CT, MR, etc). On Windows just insert CD.') % viewer) 607 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_dicom_viewer) 608 if viewer == _('no viewer installed'): 609 _log.info('neither of OsiriX / Aeskulap / AMIDE / xmedcon found, disabling "DICOM viewer" menu item') 610 self.menu_tools.Enable(id=ID_DICOM_VIEWER, enable=False) 611 612 # ID_DERMTOOL = wx.NewId() 613 # self.menu_tools.Append(ID_DERMTOOL, _("Dermatology"), _("A tool to aid dermatology diagnosis")) 614 # wx.EVT_MENU (self, ID_DERMTOOL, self.__dermtool) 615 616 ID = wx.NewId() 617 self.menu_tools.Append(ID, _('Snellen chart'), _('Display fullscreen snellen chart.')) 618 wx.EVT_MENU(self, ID, self.__on_snellen) 619 620 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.')) 621 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item) 622 623 self.menu_tools.AppendSeparator() 624 625 # menu "Knowledge" --------------------- 626 menu_knowledge = wx.Menu() 627 self.__gb['main.knowledgemenu'] = menu_knowledge 628 self.mainmenu.Append(menu_knowledge, _('&Knowledge')) 629 630 menu_drug_dbs = wx.Menu() 631 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs) 632 633 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.')) 634 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item) 635 636 # # - IFAP drug DB 637 # ID_IFAP = wx.NewId() 638 # menu_drug_dbs.Append(ID_IFAP, u'ifap', _('Start "ifap index PRAXIS" %s drug browser (Windows/Wine, Germany)') % gmTools.u_registered_trademark) 639 # wx.EVT_MENU(self, ID_IFAP, self.__on_ifap) 640 641 menu_id = wx.NewId() 642 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)')) 643 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch) 644 645 # menu_knowledge.AppendSeparator() 646 647 # - "recommended" medical links in the Wiki 648 ID_MEDICAL_LINKS = wx.NewId() 649 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.')) 650 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links) 651 652 # -- menu "Office" -------------------- 653 self.menu_office = wx.Menu() 654 655 self.__gb['main.officemenu'] = self.menu_office 656 self.mainmenu.Append(self.menu_office, _('&Office')) 657 658 # -- menu "Help" -------------- 659 help_menu = wx.Menu() 660 661 ID = wx.NewId() 662 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.')) 663 wx.EVT_MENU(self, ID, self.__on_display_wiki) 664 665 ID = wx.NewId() 666 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.')) 667 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online) 668 669 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.')) 670 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item) 671 672 menu_debugging = wx.Menu() 673 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging) 674 675 ID_SCREENSHOT = wx.NewId() 676 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.')) 677 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot) 678 679 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.')) 680 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item) 681 682 ID = wx.NewId() 683 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.')) 684 wx.EVT_MENU(self, ID, self.__on_backup_log_file) 685 686 ID = wx.NewId() 687 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.')) 688 wx.EVT_MENU(self, ID, self.__on_display_bugtracker) 689 690 ID_UNBLOCK = wx.NewId() 691 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.')) 692 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor) 693 694 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.')) 695 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item) 696 697 # item = menu_debugging.Append(-1, _('Reload hook script'), _('Reload hook script from hard drive.')) 698 # self.Bind(wx.EVT_MENU, self.__on_reload_hook_script, item) 699 700 if _cfg.get(option = 'debug'): 701 ID_TOGGLE_PAT_LOCK = wx.NewId() 702 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient'), _('Lock/unlock patient - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !')) 703 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock) 704 705 ID_TEST_EXCEPTION = wx.NewId() 706 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.')) 707 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception) 708 709 ID = wx.NewId() 710 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).')) 711 wx.EVT_MENU(self, ID, self.__on_invoke_inspector) 712 try: 713 import wx.lib.inspection 714 except ImportError: 715 menu_debugging.Enable(id = ID, enable = False) 716 717 help_menu.AppendSeparator() 718 719 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "") 720 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout) 721 722 ID_CONTRIBUTORS = wx.NewId() 723 help_menu.Append(ID_CONTRIBUTORS, _('GNUmed contributors'), _('show GNUmed contributors')) 724 wx.EVT_MENU(self, ID_CONTRIBUTORS, self.__on_show_contributors) 725 726 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.')) 727 self.Bind(wx.EVT_MENU, self.__on_about_database, item) 728 729 help_menu.AppendSeparator() 730 731 # among other things the Manual is added from a plugin 732 self.__gb['main.helpmenu'] = help_menu 733 self.mainmenu.Append(help_menu, _("&Help")) 734 735 736 # and activate menu structure 737 self.SetMenuBar(self.mainmenu)
738 #----------------------------------------------
739 - def __load_plugins(self):
740 pass
741 #---------------------------------------------- 742 # event handling 743 #----------------------------------------------
744 - def __register_events(self):
745 """register events we want to react to""" 746 747 wx.EVT_CLOSE(self, self.OnClose) 748 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session) 749 wx.EVT_END_SESSION(self, self._on_end_session) 750 751 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection) 752 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_pat_name_changed) 753 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_pat_name_changed) 754 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext) 755 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention) 756 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning) 757 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback) 758 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded) 759 760 wx.lib.pubsub.Publisher().subscribe(listener = self._on_set_statustext_pubsub, topic = 'statustext') 761 762 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
763 #----------------------------------------------
764 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
765 766 _log.debug('registering plugin with menu system') 767 _log.debug(' generic name: %s', plugin_name) 768 _log.debug(' class name: %s', class_name) 769 _log.debug(' specific menu: %s', menu_name) 770 _log.debug(' menu item: %s', menu_item_name) 771 772 # add to generic "go to plugin" menu 773 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name) 774 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 775 self.menu_id2plugin[item.Id] = class_name 776 777 # add to specific menu if so requested 778 if menu_name is not None: 779 menu = self.__gb['main.%smenu' % menu_name] 780 item = menu.Append(-1, menu_item_name, menu_help_string) 781 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 782 self.menu_id2plugin[item.Id] = class_name 783 784 return True
785 #----------------------------------------------
786 - def __on_raise_a_plugin(self, evt):
787 gmDispatcher.send ( 788 signal = u'display_widget', 789 name = self.menu_id2plugin[evt.Id] 790 )
791 #----------------------------------------------
792 - def _on_query_end_session(self, *args, **kwargs):
793 wx.Bell() 794 wx.Bell() 795 wx.Bell() 796 _log.warning('unhandled event detected: QUERY_END_SESSION') 797 _log.info('we should be saving ourselves from here') 798 gmLog2.flush() 799 print "unhandled event detected: QUERY_END_SESSION"
800 #----------------------------------------------
801 - def _on_end_session(self, *args, **kwargs):
802 wx.Bell() 803 wx.Bell() 804 wx.Bell() 805 _log.warning('unhandled event detected: END_SESSION') 806 gmLog2.flush() 807 print "unhandled event detected: END_SESSION"
808 #-----------------------------------------------
809 - def _register_pre_exit_callback(self, callback=None):
810 if not callable(callback): 811 raise TypeError(u'callback [%s] not callable' % callback) 812 813 self.__pre_exit_callbacks.append(callback)
814 #-----------------------------------------------
815 - def _on_set_statustext_pubsub(self, context=None):
816 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg']) 817 wx.CallAfter(self.SetStatusText, msg) 818 819 try: 820 if context.data['beep']: 821 wx.Bell() 822 except KeyError: 823 pass
824 #-----------------------------------------------
825 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
826 827 if msg is None: 828 msg = _('programmer forgot to specify status message') 829 830 if loglevel is not None: 831 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' ')) 832 833 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg) 834 wx.CallAfter(self.SetStatusText, msg) 835 836 if beep: 837 wx.Bell()
838 #-----------------------------------------------
839 - def _on_db_maintenance_warning(self):
840 wx.CallAfter(self.__on_db_maintenance_warning)
841 #-----------------------------------------------
843 844 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.')) 845 wx.Bell() 846 if not wx.GetApp().IsActive(): 847 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 848 849 gmHooks.run_hook_script(hook = u'db_maintenance_warning') 850 851 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 852 None, 853 -1, 854 caption = _('Database shutdown warning'), 855 question = _( 856 'The database will be shut down for maintenance\n' 857 'in a few minutes.\n' 858 '\n' 859 'In order to not suffer any loss of data you\n' 860 'will need to save your current work and log\n' 861 'out of this GNUmed client.\n' 862 ), 863 button_defs = [ 864 { 865 u'label': _('Close now'), 866 u'tooltip': _('Close this GNUmed client immediately.'), 867 u'default': False 868 }, 869 { 870 u'label': _('Finish work'), 871 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'), 872 u'default': True 873 } 874 ] 875 ) 876 decision = dlg.ShowModal() 877 if decision == wx.ID_YES: 878 top_win = wx.GetApp().GetTopWindow() 879 wx.CallAfter(top_win.Close)
880 #-----------------------------------------------
881 - def _on_request_user_attention(self, msg=None, urgent=False):
882 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
883 #-----------------------------------------------
884 - def __on_request_user_attention(self, msg=None, urgent=False):
885 # already in the foreground ? 886 if not wx.GetApp().IsActive(): 887 if urgent: 888 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 889 else: 890 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO) 891 892 if msg is not None: 893 self.SetStatusText(msg) 894 895 if urgent: 896 wx.Bell() 897 898 gmHooks.run_hook_script(hook = u'request_user_attention')
899 #-----------------------------------------------
900 - def _on_pat_name_changed(self):
901 wx.CallAfter(self.__on_pat_name_changed)
902 #-----------------------------------------------
903 - def __on_pat_name_changed(self):
904 self.__update_window_title()
905 #-----------------------------------------------
906 - def _on_post_patient_selection(self, **kwargs):
907 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
908 #----------------------------------------------
909 - def __on_post_patient_selection(self, **kwargs):
910 self.__update_window_title() 911 try: 912 gmHooks.run_hook_script(hook = u'post_patient_activation') 913 except: 914 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.')) 915 raise
916 #----------------------------------------------
917 - def _pre_selection_callback(self):
918 return self.__sanity_check_encounter()
919 #----------------------------------------------
920 - def __sanity_check_encounter(self):
921 922 dbcfg = gmCfg.cCfgSQL() 923 check_enc = bool(dbcfg.get2 ( 924 option = 'encounter.show_editor_before_patient_change', 925 workplace = gmSurgery.gmCurrentPractice().active_workplace, 926 bias = 'user', 927 default = True # True: if needed, not always unconditionally 928 )) 929 930 if not check_enc: 931 return True 932 933 pat = gmPerson.gmCurrentPatient() 934 emr = pat.get_emr() 935 enc = emr.active_encounter 936 937 # did we add anything to the EMR ? 938 has_narr = enc.has_narrative() 939 has_docs = enc.has_documents() 940 941 if (not has_narr) and (not has_docs): 942 return True 943 944 empty_aoe = (gmTools.coalesce(enc['assessment_of_encounter'], '').strip() == u'') 945 zero_duration = (enc['last_affirmed'] == enc['started']) 946 947 # all is well anyway 948 if (not empty_aoe) and (not zero_duration): 949 return True 950 951 if zero_duration: 952 enc['last_affirmed'] = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) 953 954 # no narrative, presumably only import of docs and done 955 if not has_narr: 956 if empty_aoe: 957 enc['assessment_of_encounter'] = _('only documents added') 958 enc['pk_type'] = gmEMRStructItems.get_encounter_type(description = 'chart review')[0]['pk'] 959 # "last_affirmed" should be latest modified_at of relevant docs but that's a lot more involved 960 enc.save_payload() 961 return True 962 963 # does have narrative 964 if empty_aoe: 965 # - work out suitable default 966 epis = emr.get_episodes_by_encounter() 967 if len(epis) > 0: 968 enc_summary = '' 969 for epi in epis: 970 enc_summary += '%s; ' % epi['description'] 971 enc['assessment_of_encounter'] = enc_summary 972 973 dlg = gmEMRStructWidgets.cEncounterEditAreaDlg(parent = self, encounter = enc) 974 dlg.ShowModal() 975 976 return True
977 #---------------------------------------------- 978 # menu "paperwork" 979 #----------------------------------------------
980 - def __on_show_docs(self, evt):
981 gmDispatcher.send(signal='show_document_viewer')
982 #----------------------------------------------
983 - def __on_new_letter(self, evt):
984 pat = gmPerson.gmCurrentPatient() 985 if not pat.connected: 986 gmDispatcher.send(signal = 'statustext', msg = _('Cannot write letter. No active patient.'), beep = True) 987 return True 988 #gmFormWidgets.create_new_letter(parent = self) 989 gmFormWidgets.print_doc_from_template(parent = self, keep_a_copy = True, cleanup = _cfg.get(option = 'debug'))
990 #----------------------------------------------
991 - def __on_manage_form_templates(self, evt):
993 #---------------------------------------------- 994 # help menu 995 #----------------------------------------------
996 - def OnAbout(self, event):
997 from Gnumed.wxpython import gmAbout 998 gmAbout = gmAbout.AboutFrame ( 999 self, 1000 -1, 1001 _("About GNUmed"), 1002 size=wx.Size(350, 300), 1003 style = wx.MAXIMIZE_BOX, 1004 version = _cfg.get(option = 'client_version') 1005 ) 1006 gmAbout.Centre(wx.BOTH) 1007 gmTopLevelFrame.otherWin = gmAbout 1008 gmAbout.Show(True) 1009 del gmAbout
1010 #----------------------------------------------
1011 - def __on_about_database(self, evt):
1012 praxis = gmSurgery.gmCurrentPractice() 1013 msg = praxis.db_logon_banner 1014 1015 login = gmPG2.get_default_login() 1016 1017 auth = _( 1018 '\n\n' 1019 ' workplace: %s\n' 1020 ' account: %s\n' 1021 ' database: %s\n' 1022 ' server: %s\n' 1023 ) % ( 1024 praxis.active_workplace, 1025 login.user, 1026 login.database, 1027 gmTools.coalesce(login.host, u'<localhost>') 1028 ) 1029 1030 msg += auth 1031 1032 gmGuiHelpers.gm_show_info(msg, _('About database and server'))
1033 #----------------------------------------------
1034 - def __on_show_contributors(self, event):
1035 from Gnumed.wxpython import gmAbout 1036 contribs = gmAbout.cContributorsDlg ( 1037 parent = self, 1038 id = -1, 1039 title = _('GNUmed contributors'), 1040 size = wx.Size(400,600), 1041 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER 1042 ) 1043 contribs.ShowModal() 1044 del contribs 1045 del gmAbout
1046 #---------------------------------------------- 1047 # GNUmed menu 1048 #----------------------------------------------
1049 - def __on_exit_gnumed(self, event):
1050 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler).""" 1051 _log.debug('gmTopLevelFrame._on_exit_gnumed() start') 1052 self.Close(True) # -> calls wx.EVT_CLOSE handler 1053 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1054 #----------------------------------------------
1055 - def __on_check_for_updates(self, evt):
1057 #----------------------------------------------
1058 - def __on_announce_maintenance(self, evt):
1059 send = gmGuiHelpers.gm_show_question ( 1060 _('This will send a notification about database downtime\n' 1061 'to all GNUmed clients connected to your database.\n' 1062 '\n' 1063 'Do you want to send the notification ?\n' 1064 ), 1065 _('Announcing database maintenance downtime') 1066 ) 1067 if not send: 1068 return 1069 gmPG2.send_maintenance_notification()
1070 #---------------------------------------------- 1071 #----------------------------------------------
1072 - def __on_list_configuration(self, evt):
1073 gmCfgWidgets.list_configuration(parent = self)
1074 #---------------------------------------------- 1075 # submenu GNUmed / options / client 1076 #----------------------------------------------
1077 - def __on_configure_temp_dir(self, evt):
1078 1079 cfg = gmCfg.cCfgSQL() 1080 1081 tmp_dir = gmTools.coalesce ( 1082 cfg.get2 ( 1083 option = "horstspace.tmp_dir", 1084 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1085 bias = 'workplace' 1086 ), 1087 os.path.expanduser(os.path.join('~', '.gnumed', 'tmp')) 1088 ) 1089 1090 dlg = wx.DirDialog ( 1091 parent = self, 1092 message = _('Choose temporary directory ...'), 1093 defaultPath = tmp_dir, 1094 style = wx.DD_DEFAULT_STYLE 1095 ) 1096 result = dlg.ShowModal() 1097 tmp_dir = dlg.GetPath() 1098 dlg.Destroy() 1099 1100 if result != wx.ID_OK: 1101 return 1102 1103 cfg.set ( 1104 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1105 option = "horstspace.tmp_dir", 1106 value = tmp_dir 1107 )
1108 #----------------------------------------------
1109 - def __on_configure_export_chunk_size(self, evt):
1110 1111 def is_valid(value): 1112 try: 1113 i = int(value) 1114 except: 1115 return False, value 1116 if i < 0: 1117 return False, value 1118 if i > (1024 * 1024 * 1024 * 10): # 10 GB 1119 return False, value 1120 return True, i
1121 1122 gmCfgWidgets.configure_string_option ( 1123 message = _( 1124 'Some network installations cannot cope with loading\n' 1125 'documents of arbitrary size in one piece from the\n' 1126 'database (mainly observed on older Windows versions)\n.' 1127 '\n' 1128 'Under such circumstances documents need to be retrieved\n' 1129 'in chunks and reassembled on the client.\n' 1130 '\n' 1131 'Here you can set the size (in Bytes) above which\n' 1132 'GNUmed will retrieve documents in chunks. Setting this\n' 1133 'value to 0 will disable the chunking protocol.' 1134 ), 1135 option = 'horstspace.blob_export_chunk_size', 1136 bias = 'workplace', 1137 default_value = 1024 * 1024, 1138 validator = is_valid 1139 )
1140 #---------------------------------------------- 1141 # submenu GNUmed / database 1142 #----------------------------------------------
1143 - def __on_configure_db_lang(self, event):
1144 1145 langs = gmPG2.get_translation_languages() 1146 1147 for lang in [ 1148 gmI18N.system_locale_level['language'], 1149 gmI18N.system_locale_level['country'], 1150 gmI18N.system_locale_level['full'] 1151 ]: 1152 if lang not in langs: 1153 langs.append(lang) 1154 1155 selected_lang = gmPG2.get_current_user_language() 1156 try: 1157 selections = [langs.index(selected_lang)] 1158 except ValueError: 1159 selections = None 1160 1161 language = gmListWidgets.get_choices_from_list ( 1162 parent = self, 1163 msg = _( 1164 'Please select your database language from the list below.\n' 1165 '\n' 1166 'Your current setting is [%s].\n' 1167 '\n' 1168 'This setting will not affect the language the user interface\n' 1169 'is displayed in but rather that of the metadata returned\n' 1170 'from the database such as encounter types, document types,\n' 1171 'and EMR formatting.\n' 1172 '\n' 1173 'To switch back to the default English language unselect all\n' 1174 'pre-selected languages from the list below.' 1175 ) % gmTools.coalesce(selected_lang, _('not configured')), 1176 caption = _('Configuring database language'), 1177 choices = langs, 1178 selections = selections, 1179 columns = [_('Language')], 1180 data = langs, 1181 single_selection = True, 1182 can_return_empty = True 1183 ) 1184 1185 if language is None: 1186 return 1187 1188 if language == []: 1189 language = None 1190 1191 try: 1192 _provider.get_staff().database_language = language 1193 return 1194 except ValueError: 1195 pass 1196 1197 force_language = gmGuiHelpers.gm_show_question ( 1198 _('The database currently holds no translations for\n' 1199 'language [%s]. However, you can add translations\n' 1200 'for things like document or encounter types yourself.\n' 1201 '\n' 1202 'Do you want to force the language setting to [%s] ?' 1203 ) % (language, language), 1204 _('Configuring database language') 1205 ) 1206 if not force_language: 1207 return 1208 1209 gmPG2.force_user_language(language = language)
1210 #----------------------------------------------
1211 - def __on_configure_db_welcome(self, event):
1212 dlg = gmGuiHelpers.cGreetingEditorDlg(self, -1) 1213 dlg.ShowModal()
1214 #---------------------------------------------- 1215 # submenu GNUmed - config - external tools 1216 #----------------------------------------------
1217 - def __on_configure_ooo_settle_time(self, event):
1218 1219 def is_valid(value): 1220 try: 1221 float(value) 1222 return True, value 1223 except: 1224 return False, value
1225 1226 gmCfgWidgets.configure_string_option ( 1227 message = _( 1228 'When GNUmed cannot find an OpenOffice server it\n' 1229 'will try to start one. OpenOffice, however, needs\n' 1230 'some time to fully start up.\n' 1231 '\n' 1232 'Here you can set the time for GNUmed to wait for OOo.\n' 1233 ), 1234 option = 'external.ooo.startup_settle_time', 1235 bias = 'workplace', 1236 default_value = 2.0, 1237 validator = is_valid 1238 ) 1239 #----------------------------------------------
1240 - def __on_configure_drug_data_source(self, evt):
1241 gmMedicationWidgets.configure_drug_data_source(parent = self)
1242 #----------------------------------------------
1243 - def __on_configure_measurements_url(self, evt):
1244 1245 def is_valid(value): 1246 value = value.strip() 1247 if value == u'': 1248 return True, value 1249 try: 1250 urllib2.urlopen(value) 1251 return True, value 1252 except: 1253 return False, value
1254 1255 gmCfgWidgets.configure_string_option ( 1256 message = _( 1257 'GNUmed will use this URL to access an encyclopedia of\n' 1258 'measurement/lab methods from within the measurments grid.\n' 1259 '\n' 1260 'You can leave this empty but to set it to a specific\n' 1261 'address the URL must be accessible now.' 1262 ), 1263 option = 'external.urls.measurements_encyclopedia', 1264 bias = 'user', 1265 default_value = u'http://www.laborlexikon.de', 1266 validator = is_valid 1267 ) 1268 #----------------------------------------------
1269 - def __on_configure_acs_risk_calculator_cmd(self, event):
1270 1271 def is_valid(value): 1272 found, binary = gmShellAPI.detect_external_binary(value) 1273 if not found: 1274 gmDispatcher.send ( 1275 signal = 'statustext', 1276 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1277 beep = True 1278 ) 1279 return False, value 1280 return True, binary
1281 1282 gmCfgWidgets.configure_string_option ( 1283 message = _( 1284 'Enter the shell command with which to start the\n' 1285 'the ACS risk assessment calculator.\n' 1286 '\n' 1287 'GNUmed will try to verify the path which may,\n' 1288 'however, fail if you are using an emulator such\n' 1289 'as Wine. Nevertheless, starting the calculator\n' 1290 'will work as long as the shell command is correct\n' 1291 'despite the failing test.' 1292 ), 1293 option = 'external.tools.acs_risk_calculator_cmd', 1294 bias = 'user', 1295 validator = is_valid 1296 ) 1297 #----------------------------------------------
1298 - def __on_configure_visual_soap_cmd(self, event):
1299 gmNarrativeWidgets.configure_visual_progress_note_editor()
1300 #----------------------------------------------
1301 - def __on_configure_freediams_cmd(self, event):
1302 1303 def is_valid(value): 1304 found, binary = gmShellAPI.detect_external_binary(value) 1305 if not found: 1306 gmDispatcher.send ( 1307 signal = 'statustext', 1308 msg = _('The command [%s] is not found.') % value, 1309 beep = True 1310 ) 1311 return False, value 1312 return True, binary
1313 #------------------------------------------ 1314 gmCfgWidgets.configure_string_option ( 1315 message = _( 1316 'Enter the shell command with which to start\n' 1317 'the FreeDiams drug database frontend.\n' 1318 '\n' 1319 'GNUmed will try to verify that path.' 1320 ), 1321 option = 'external.tools.freediams_cmd', 1322 bias = 'workplace', 1323 default_value = None, 1324 validator = is_valid 1325 ) 1326 #----------------------------------------------
1327 - def __on_configure_ifap_cmd(self, event):
1328 1329 def is_valid(value): 1330 found, binary = gmShellAPI.detect_external_binary(value) 1331 if not found: 1332 gmDispatcher.send ( 1333 signal = 'statustext', 1334 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1335 beep = True 1336 ) 1337 return False, value 1338 return True, binary
1339 1340 gmCfgWidgets.configure_string_option ( 1341 message = _( 1342 'Enter the shell command with which to start the\n' 1343 'the IFAP drug database.\n' 1344 '\n' 1345 'GNUmed will try to verify the path which may,\n' 1346 'however, fail if you are using an emulator such\n' 1347 'as Wine. Nevertheless, starting IFAP will work\n' 1348 'as long as the shell command is correct despite\n' 1349 'the failing test.' 1350 ), 1351 option = 'external.ifap-win.shell_command', 1352 bias = 'workplace', 1353 default_value = 'C:\Ifapwin\WIAMDB.EXE', 1354 validator = is_valid 1355 ) 1356 #---------------------------------------------- 1357 # submenu GNUmed / config / ui 1358 #----------------------------------------------
1359 - def __on_configure_startup_plugin(self, evt):
1360 1361 dbcfg = gmCfg.cCfgSQL() 1362 # get list of possible plugins 1363 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1364 option = u'horstspace.notebook.plugin_load_order', 1365 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1366 bias = 'user' 1367 ), []) 1368 1369 # get current setting 1370 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1371 option = u'horstspace.plugin_to_raise_after_startup', 1372 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1373 bias = 'user' 1374 ), u'gmEMRBrowserPlugin') 1375 try: 1376 selections = [plugin_list.index(initial_plugin)] 1377 except ValueError: 1378 selections = None 1379 1380 # now let user decide 1381 plugin = gmListWidgets.get_choices_from_list ( 1382 parent = self, 1383 msg = _( 1384 'Here you can choose which plugin you want\n' 1385 'GNUmed to display after initial startup.\n' 1386 '\n' 1387 'Note that the plugin must not require any\n' 1388 'patient to be activated.\n' 1389 '\n' 1390 'Select the desired plugin below:' 1391 ), 1392 caption = _('Configuration'), 1393 choices = plugin_list, 1394 selections = selections, 1395 columns = [_('GNUmed Plugin')], 1396 single_selection = True 1397 ) 1398 1399 if plugin is None: 1400 return 1401 1402 dbcfg.set ( 1403 option = u'patient_search.plugin_to_raise_after_startup', 1404 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1405 value = plugin 1406 )
1407 #---------------------------------------------- 1408 # submenu GNUmed / config / ui / patient search 1409 #----------------------------------------------
1410 - def __on_configure_quick_pat_search(self, evt):
1411 gmCfgWidgets.configure_boolean_option ( 1412 parent = self, 1413 question = _( 1414 'If there is only one external patient\n' 1415 'source available do you want GNUmed\n' 1416 'to immediately go ahead and search for\n' 1417 'matching patient records ?\n\n' 1418 'If not GNUmed will let you confirm the source.' 1419 ), 1420 option = 'patient_search.external_sources.immediately_search_if_single_source', 1421 button_tooltips = [ 1422 _('Yes, search for matches immediately.'), 1423 _('No, let me confirm the external patient first.') 1424 ] 1425 )
1426 #----------------------------------------------
1427 - def __on_cfg_default_region(self, evt):
1428 gmDemographicsWidgets.configure_default_region()
1429 #----------------------------------------------
1430 - def __on_cfg_default_country(self, evt):
1431 gmDemographicsWidgets.configure_default_country()
1432 #----------------------------------------------
1433 - def __on_configure_dob_reminder_proximity(self, evt):
1434 1435 def is_valid(value): 1436 return gmPG2.is_pg_interval(candidate=value), value
1437 1438 gmCfgWidgets.configure_string_option ( 1439 message = _( 1440 'When a patient is activated GNUmed checks the\n' 1441 "proximity of the patient's birthday.\n" 1442 '\n' 1443 'If the birthday falls within the range of\n' 1444 ' "today %s <the interval you set here>"\n' 1445 'GNUmed will remind you of the recent or\n' 1446 'imminent anniversary.' 1447 ) % u'\u2213', 1448 option = u'patient_search.dob_warn_interval', 1449 bias = 'user', 1450 default_value = '1 week', 1451 validator = is_valid 1452 ) 1453 #----------------------------------------------
1454 - def __on_allow_multiple_new_episodes(self, evt):
1455 1456 gmCfgWidgets.configure_boolean_option ( 1457 parent = self, 1458 question = _( 1459 'When adding progress notes do you want to\n' 1460 'allow opening several unassociated, new\n' 1461 'episodes for a patient at once ?\n' 1462 '\n' 1463 'This can be particularly helpful when entering\n' 1464 'progress notes on entirely new patients presenting\n' 1465 'with a multitude of problems on their first visit.' 1466 ), 1467 option = u'horstspace.soap_editor.allow_same_episode_multiple_times', 1468 button_tooltips = [ 1469 _('Yes, allow for multiple new episodes concurrently.'), 1470 _('No, only allow editing one new episode at a time.') 1471 ] 1472 )
1473 #----------------------------------------------
1474 - def __on_configure_initial_pat_plugin(self, evt):
1475 1476 dbcfg = gmCfg.cCfgSQL() 1477 # get list of possible plugins 1478 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1479 option = u'horstspace.notebook.plugin_load_order', 1480 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1481 bias = 'user' 1482 ), []) 1483 1484 # get current setting 1485 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1486 option = u'patient_search.plugin_to_raise_after_search', 1487 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1488 bias = 'user' 1489 ), u'gmEMRBrowserPlugin') 1490 try: 1491 selections = [plugin_list.index(initial_plugin)] 1492 except ValueError: 1493 selections = None 1494 1495 # now let user decide 1496 plugin = gmListWidgets.get_choices_from_list ( 1497 parent = self, 1498 msg = _( 1499 'When a patient is activated GNUmed can\n' 1500 'be told to switch to a specific plugin.\n' 1501 '\n' 1502 'Select the desired plugin below:' 1503 ), 1504 caption = _('Configuration'), 1505 choices = plugin_list, 1506 selections = selections, 1507 columns = [_('GNUmed Plugin')], 1508 single_selection = True 1509 ) 1510 1511 if plugin is None: 1512 return 1513 1514 dbcfg.set ( 1515 option = u'patient_search.plugin_to_raise_after_search', 1516 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1517 value = plugin 1518 )
1519 #---------------------------------------------- 1520 # submenu GNUmed / config / encounter 1521 #----------------------------------------------
1522 - def __on_cfg_medication_list_template(self, evt):
1523 gmMedicationWidgets.configure_medication_list_template(parent = self)
1524 #----------------------------------------------
1525 - def __on_cfg_enc_default_type(self, evt):
1526 enc_types = gmEMRStructItems.get_encounter_types() 1527 1528 gmCfgWidgets.configure_string_from_list_option ( 1529 parent = self, 1530 message = _('Select the default type for new encounters.\n'), 1531 option = 'encounter.default_type', 1532 bias = 'user', 1533 default_value = u'in surgery', 1534 choices = [ e[0] for e in enc_types ], 1535 columns = [_('Encounter type')], 1536 data = [ e[1] for e in enc_types ] 1537 )
1538 #----------------------------------------------
1539 - def __on_cfg_enc_pat_change(self, event):
1540 gmCfgWidgets.configure_boolean_option ( 1541 parent = self, 1542 question = _( 1543 'Do you want GNUmed to show the encounter\n' 1544 'details editor when changing the active patient ?' 1545 ), 1546 option = 'encounter.show_editor_before_patient_change', 1547 button_tooltips = [ 1548 _('Yes, show the encounter editor if it seems appropriate.'), 1549 _('No, never show the encounter editor even if it would seem useful.') 1550 ] 1551 )
1552 #----------------------------------------------
1553 - def __on_cfg_enc_empty_ttl(self, evt):
1554 1555 def is_valid(value): 1556 return gmPG2.is_pg_interval(candidate=value), value
1557 1558 gmCfgWidgets.configure_string_option ( 1559 message = _( 1560 'When a patient is activated GNUmed checks the\n' 1561 'chart for encounters lacking any entries.\n' 1562 '\n' 1563 'Any such encounters older than what you set\n' 1564 'here will be removed from the medical record.\n' 1565 '\n' 1566 'To effectively disable removal of such encounters\n' 1567 'set this option to an improbable value.\n' 1568 ), 1569 option = 'encounter.ttl_if_empty', 1570 bias = 'user', 1571 default_value = '1 week', 1572 validator = is_valid 1573 ) 1574 #----------------------------------------------
1575 - def __on_cfg_enc_min_ttl(self, evt):
1576 1577 def is_valid(value): 1578 return gmPG2.is_pg_interval(candidate=value), value
1579 1580 gmCfgWidgets.configure_string_option ( 1581 message = _( 1582 'When a patient is activated GNUmed checks the\n' 1583 'age of the most recent encounter.\n' 1584 '\n' 1585 'If that encounter is younger than this age\n' 1586 'the existing encounter will be continued.\n' 1587 '\n' 1588 '(If it is really old a new encounter is\n' 1589 ' started, or else GNUmed will ask you.)\n' 1590 ), 1591 option = 'encounter.minimum_ttl', 1592 bias = 'user', 1593 default_value = '1 hour 30 minutes', 1594 validator = is_valid 1595 ) 1596 #----------------------------------------------
1597 - def __on_cfg_enc_max_ttl(self, evt):
1598 1599 def is_valid(value): 1600 return gmPG2.is_pg_interval(candidate=value), value
1601 1602 gmCfgWidgets.configure_string_option ( 1603 message = _( 1604 'When a patient is activated GNUmed checks the\n' 1605 'age of the most recent encounter.\n' 1606 '\n' 1607 'If that encounter is older than this age\n' 1608 'GNUmed will always start a new encounter.\n' 1609 '\n' 1610 '(If it is very recent the existing encounter\n' 1611 ' is continued, or else GNUmed will ask you.)\n' 1612 ), 1613 option = 'encounter.maximum_ttl', 1614 bias = 'user', 1615 default_value = '6 hours', 1616 validator = is_valid 1617 ) 1618 #----------------------------------------------
1619 - def __on_cfg_epi_ttl(self, evt):
1620 1621 def is_valid(value): 1622 try: 1623 value = int(value) 1624 except: 1625 return False, value 1626 return gmPG2.is_pg_interval(candidate=value), value
1627 1628 gmCfgWidgets.configure_string_option ( 1629 message = _( 1630 'At any time there can only be one open (ongoing)\n' 1631 'episode for each health issue.\n' 1632 '\n' 1633 'When you try to open (add data to) an episode on a health\n' 1634 'issue GNUmed will check for an existing open episode on\n' 1635 'that issue. If there is any it will check the age of that\n' 1636 'episode. The episode is closed if it has been dormant (no\n' 1637 'data added, that is) for the period of time (in days) you\n' 1638 'set here.\n' 1639 '\n' 1640 "If the existing episode hasn't been dormant long enough\n" 1641 'GNUmed will consult you what to do.\n' 1642 '\n' 1643 'Enter maximum episode dormancy in DAYS:' 1644 ), 1645 option = 'episode.ttl', 1646 bias = 'user', 1647 default_value = 60, 1648 validator = is_valid 1649 ) 1650 #----------------------------------------------
1651 - def __on_configure_user_email(self, evt):
1652 email = gmSurgery.gmCurrentPractice().user_email 1653 1654 dlg = wx.TextEntryDialog ( 1655 parent = self, 1656 message = _( 1657 'This email address will be used when GNUmed\n' 1658 'is sending email on your behalf such as when\n' 1659 'reporting bugs or when you choose to contribute\n' 1660 'reference material to the GNUmed community.\n' 1661 '\n' 1662 'The developers will then be able to get back to you\n' 1663 'directly with advice. Otherwise you would have to\n' 1664 'follow the mailing list discussion for help.\n' 1665 '\n' 1666 'Leave this blank if you wish to stay anonymous.' 1667 ), 1668 caption = _('Please enter your email address.'), 1669 defaultValue = gmTools.coalesce(email, u''), 1670 style = wx.OK | wx.CANCEL | wx.CENTRE 1671 ) 1672 decision = dlg.ShowModal() 1673 if decision == wx.ID_CANCEL: 1674 dlg.Destroy() 1675 return 1676 1677 email = dlg.GetValue().strip() 1678 gmSurgery.gmCurrentPractice().user_email = email 1679 gmExceptionHandlingWidgets.set_sender_email(email) 1680 dlg.Destroy()
1681 #----------------------------------------------
1682 - def __on_configure_workplace(self, evt):
1683 gmProviderInboxWidgets.configure_workplace_plugins(parent = self)
1684 #----------------------------------------------
1685 - def __on_configure_update_check(self, evt):
1686 gmCfgWidgets.configure_boolean_option ( 1687 question = _( 1688 'Do you want GNUmed to check for updates at startup ?\n' 1689 '\n' 1690 'You will still need your system administrator to\n' 1691 'actually install any updates for you.\n' 1692 ), 1693 option = u'horstspace.update.autocheck_at_startup', 1694 button_tooltips = [ 1695 _('Yes, check for updates at startup.'), 1696 _('No, do not check for updates at startup.') 1697 ] 1698 )
1699 #----------------------------------------------
1700 - def __on_configure_update_check_scope(self, evt):
1701 gmCfgWidgets.configure_boolean_option ( 1702 question = _( 1703 'When checking for updates do you want GNUmed to\n' 1704 'look for bug fix updates only or do you want to\n' 1705 'know about features updates, too ?\n' 1706 '\n' 1707 'Minor updates (x.y.z.a -> x.y.z.b) contain bug fixes\n' 1708 'only. They can usually be installed without much\n' 1709 'preparation. They never require a database upgrade.\n' 1710 '\n' 1711 'Major updates (x.y.a -> x..y.b or y.a -> x.b) come\n' 1712 'with new features. They need more preparation and\n' 1713 'often require a database upgrade.\n' 1714 '\n' 1715 'You will still need your system administrator to\n' 1716 'actually install any updates for you.\n' 1717 ), 1718 option = u'horstspace.update.consider_latest_branch', 1719 button_tooltips = [ 1720 _('Yes, check for feature updates, too.'), 1721 _('No, check for bug-fix updates only.') 1722 ] 1723 )
1724 #----------------------------------------------
1725 - def __on_configure_update_url(self, evt):
1726 1727 import urllib2 as url 1728 1729 def is_valid(value): 1730 try: 1731 url.urlopen(value) 1732 except: 1733 return False, value 1734 1735 return True, value
1736 1737 gmCfgWidgets.configure_string_option ( 1738 message = _( 1739 'GNUmed can check for new releases being available. To do\n' 1740 'so it needs to load version information from an URL.\n' 1741 '\n' 1742 'The default URL is:\n' 1743 '\n' 1744 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n' 1745 '\n' 1746 'but you can configure any other URL locally. Note\n' 1747 'that you must enter the location as a valid URL.\n' 1748 'Depending on the URL the client will need online\n' 1749 'access when checking for updates.' 1750 ), 1751 option = u'horstspace.update.url', 1752 bias = u'workplace', 1753 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt', 1754 validator = is_valid 1755 ) 1756 #----------------------------------------------
1757 - def __on_configure_partless_docs(self, evt):
1758 gmCfgWidgets.configure_boolean_option ( 1759 question = _( 1760 'Do you want to allow saving of new documents without\n' 1761 'any parts or do you want GNUmed to enforce that they\n' 1762 'contain at least one part before they can be saved ?\n' 1763 '\n' 1764 'Part-less documents can be useful if you want to build\n' 1765 'up an index of, say, archived documents but do not\n' 1766 'want to scan in all the pages contained therein.' 1767 ), 1768 option = u'horstspace.scan_index.allow_partless_documents', 1769 button_tooltips = [ 1770 _('Yes, allow saving documents without any parts.'), 1771 _('No, require documents to have at least one part.') 1772 ] 1773 )
1774 #----------------------------------------------
1775 - def __on_configure_doc_uuid_dialog(self, evt):
1776 gmCfgWidgets.configure_boolean_option ( 1777 question = _( 1778 'After importing a new document do you\n' 1779 'want GNUmed to display the unique ID\n' 1780 'it auto-generated for that document ?\n' 1781 '\n' 1782 'This can be useful if you want to label the\n' 1783 'originals with that ID for later identification.' 1784 ), 1785 option = u'horstspace.scan_index.show_doc_id', 1786 button_tooltips = [ 1787 _('Yes, display the ID generated for the new document after importing.'), 1788 _('No, do not display the ID generated for the new document after importing.') 1789 ] 1790 )
1791 #----------------------------------------------
1792 - def __on_configure_doc_review_dialog(self, evt):
1793 1794 def is_valid(value): 1795 try: 1796 value = int(value) 1797 except: 1798 return False, value 1799 if value not in [0, 1, 2]: 1800 return False, value 1801 return True, value
1802 1803 gmCfgWidgets.configure_string_option ( 1804 message = _( 1805 'GNUmed can show the document review dialog after\n' 1806 'calling the appropriate viewer for that document.\n' 1807 '\n' 1808 'Select the conditions under which you want\n' 1809 'GNUmed to do so:\n' 1810 '\n' 1811 ' 0: never display the review dialog\n' 1812 ' 1: always display the dialog\n' 1813 ' 2: only if there is no previous review by me\n' 1814 '\n' 1815 'Note that if a viewer is configured to not block\n' 1816 'GNUmed during document display the review dialog\n' 1817 'will actually appear in parallel to the viewer.' 1818 ), 1819 option = u'horstspace.document_viewer.review_after_display', 1820 bias = u'user', 1821 default_value = 2, 1822 validator = is_valid 1823 ) 1824 #----------------------------------------------
1825 - def __on_dicom_viewer(self, evt):
1826 1827 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 1828 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking=False) 1829 return 1830 1831 for viewer in ['aeskulap', 'amide', 'xmedcon']: 1832 found, cmd = gmShellAPI.detect_external_binary(binary = viewer) 1833 if found: 1834 gmShellAPI.run_command_in_shell(cmd, blocking=False) 1835 return 1836 1837 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
1838 #----------------------------------------------
1839 - def __on_acs_risk_assessment(self, evt):
1840 1841 dbcfg = gmCfg.cCfgSQL() 1842 cmd = dbcfg.get2 ( 1843 option = u'external.tools.acs_risk_calculator_cmd', 1844 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1845 bias = 'user' 1846 ) 1847 1848 if cmd is None: 1849 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True) 1850 return 1851 1852 cwd = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp')) 1853 try: 1854 subprocess.check_call ( 1855 args = (cmd,), 1856 close_fds = True, 1857 cwd = cwd 1858 ) 1859 except (OSError, ValueError, subprocess.CalledProcessError): 1860 _log.exception('there was a problem executing [%s]', cmd) 1861 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True) 1862 return 1863 1864 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d'))) 1865 for pdf in pdfs: 1866 try: 1867 open(pdf).close() 1868 except: 1869 _log.exception('error accessing [%s]', pdf) 1870 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the ARRIBA result in [%s] !') % pdf, beep = True) 1871 continue 1872 1873 doc = gmDocumentWidgets.save_file_as_new_document ( 1874 parent = self, 1875 filename = pdf, 1876 document_type = u'risk assessment' 1877 ) 1878 1879 try: 1880 os.remove(pdf) 1881 except StandardError: 1882 _log.exception('cannot remove [%s]', pdf) 1883 1884 if doc is None: 1885 continue 1886 doc['comment'] = u'ARRIBA: %s' % _('cardiovascular risk assessment') 1887 doc.save() 1888 1889 return
1890 #----------------------------------------------
1891 - def __on_snellen(self, evt):
1892 dlg = gmSnellen.cSnellenCfgDlg() 1893 if dlg.ShowModal() != wx.ID_OK: 1894 return 1895 1896 frame = gmSnellen.cSnellenChart ( 1897 width = dlg.vals[0], 1898 height = dlg.vals[1], 1899 alpha = dlg.vals[2], 1900 mirr = dlg.vals[3], 1901 parent = None 1902 ) 1903 frame.CentreOnScreen(wx.BOTH) 1904 # self.SetTopWindow(frame) 1905 # frame.Destroy = frame.DestroyWhenApp 1906 frame.Show(True)
1907 #---------------------------------------------- 1908 #---------------------------------------------- 1915 #----------------------------------------------
1916 - def __on_jump_to_drug_db(self, evt):
1917 gmMedicationWidgets.jump_to_drug_database()
1918 #----------------------------------------------
1919 - def __on_kompendium_ch(self, evt):
1920 webbrowser.open ( 1921 url = 'http://www.kompendium.ch', 1922 new = False, 1923 autoraise = True 1924 )
1925 #---------------------------------------------- 1926 # Help / Debugging 1927 #----------------------------------------------
1928 - def __on_save_screenshot(self, evt):
1929 wx.CallAfter(self.__save_screenshot) 1930 evt.Skip()
1931 #----------------------------------------------
1932 - def __save_screenshot(self):
1933 1934 time.sleep(0.5) 1935 1936 rect = self.GetRect() 1937 1938 # adjust for window decoration on Linux 1939 if sys.platform == 'linux2': 1940 client_x, client_y = self.ClientToScreen((0, 0)) 1941 border_width = client_x - rect.x 1942 title_bar_height = client_y - rect.y 1943 # If the window has a menu bar, remove it from the title bar height. 1944 if self.GetMenuBar(): 1945 title_bar_height /= 2 1946 rect.width += (border_width * 2) 1947 rect.height += title_bar_height + border_width 1948 1949 wdc = wx.ScreenDC() 1950 mdc = wx.MemoryDC() 1951 img = wx.EmptyBitmap(rect.width, rect.height) 1952 mdc.SelectObject(img) 1953 mdc.Blit ( # copy ... 1954 0, 0, # ... to here in the target ... 1955 rect.width, rect.height, # ... that much from ... 1956 wdc, # ... the source ... 1957 rect.x, rect.y # ... starting here 1958 ) 1959 1960 # FIXME: improve filename with patient/workplace/provider, allow user to select/change 1961 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') 1962 img.SaveFile(fname, wx.BITMAP_TYPE_PNG) 1963 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
1964 #----------------------------------------------
1965 - def __on_test_exception(self, evt):
1966 #import nonexistant_module 1967 raise ValueError('raised ValueError to test exception handling')
1968 #----------------------------------------------
1969 - def __on_invoke_inspector(self, evt):
1970 import wx.lib.inspection 1971 wx.lib.inspection.InspectionTool().Show()
1972 #----------------------------------------------
1973 - def __on_display_bugtracker(self, evt):
1974 webbrowser.open ( 1975 url = 'https://bugs.launchpad.net/gnumed/', 1976 new = False, 1977 autoraise = True 1978 )
1979 #----------------------------------------------
1980 - def __on_display_wiki(self, evt):
1981 webbrowser.open ( 1982 url = 'http://wiki.gnumed.de', 1983 new = False, 1984 autoraise = True 1985 )
1986 #----------------------------------------------
1987 - def __on_display_user_manual_online(self, evt):
1988 webbrowser.open ( 1989 url = 'http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual#UserGuideInManual', 1990 new = False, 1991 autoraise = True 1992 )
1993 #----------------------------------------------
1994 - def __on_menu_reference(self, evt):
1995 webbrowser.open ( 1996 url = 'http://wiki.gnumed.de/bin/view/Gnumed/MenuReference', 1997 new = False, 1998 autoraise = True 1999 )
2000 #----------------------------------------------
2001 - def __on_pgadmin3(self, evt):
2002 found, cmd = gmShellAPI.detect_external_binary(binary = 'pgadmin3') 2003 if found: 2004 gmShellAPI.run_command_in_shell(cmd, blocking=False) 2005 return 2006 gmDispatcher.send(signal = 'statustext', msg = _('pgAdmin III not found.'), beep = True)
2007 #----------------------------------------------
2008 - def __on_reload_hook_script(self, evt):
2009 if not gmHooks.import_hook_module(reimport = True): 2010 gmDispatcher.send(signal = 'statustext', msg = _('Error reloading hook script.'))
2011 #----------------------------------------------
2012 - def __on_unblock_cursor(self, evt):
2013 wx.EndBusyCursor()
2014 #----------------------------------------------
2015 - def __on_toggle_patient_lock(self, evt):
2016 curr_pat = gmPerson.gmCurrentPatient() 2017 if curr_pat.locked: 2018 curr_pat.force_unlock() 2019 else: 2020 curr_pat.locked = True
2021 #----------------------------------------------
2022 - def __on_show_log_file(self, evt):
2023 from Gnumed.pycommon import gmMimeLib 2024 gmLog2.flush() 2025 gmMimeLib.call_viewer_on_file(gmLog2._logfile_name, block = False)
2026 #----------------------------------------------
2027 - def __on_backup_log_file(self, evt):
2028 name = os.path.basename(gmLog2._logfile_name) 2029 name, ext = os.path.splitext(name) 2030 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 2031 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs')) 2032 2033 dlg = wx.FileDialog ( 2034 parent = self, 2035 message = _("Save current log as..."), 2036 defaultDir = new_path, 2037 defaultFile = new_name, 2038 wildcard = "%s (*.log)|*.log" % _("log files"), 2039 style = wx.SAVE 2040 ) 2041 choice = dlg.ShowModal() 2042 new_name = dlg.GetPath() 2043 dlg.Destroy() 2044 if choice != wx.ID_OK: 2045 return True 2046 2047 _log.warning('syncing log file for backup to [%s]', new_name) 2048 gmLog2.flush() 2049 shutil.copy2(gmLog2._logfile_name, new_name) 2050 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2051 #---------------------------------------------- 2052 # GNUmed / 2053 #----------------------------------------------
2054 - def OnClose(self, event):
2055 """This is the wx.EVT_CLOSE handler. 2056 2057 - framework still functional 2058 """ 2059 _log.debug('gmTopLevelFrame.OnClose() start') 2060 self._clean_exit() 2061 self.Destroy() 2062 _log.debug('gmTopLevelFrame.OnClose() end') 2063 return True
2064 #----------------------------------------------
2065 - def OnExportEMR(self, event):
2066 """ 2067 Export selected patient EMR to a file 2068 """ 2069 gmEMRBrowser.export_emr_to_ascii(parent=self)
2070 #----------------------------------------------
2071 - def __dermtool (self, event):
2072 import Gnumed.wxpython.gmDermTool as DT 2073 frame = DT.DermToolDialog(None, -1) 2074 frame.Show(True)
2075 #----------------------------------------------
2076 - def __on_start_new_encounter(self, evt):
2077 pat = gmPerson.gmCurrentPatient() 2078 if not pat.connected: 2079 gmDispatcher.send(signal = 'statustext', msg = _('Cannot start new encounter. No active patient.')) 2080 return False 2081 emr = pat.get_emr() 2082 gmEMRStructWidgets.start_new_encounter(emr = emr)
2083 #----------------------------------------------
2084 - def __on_list_encounters(self, evt):
2085 pat = gmPerson.gmCurrentPatient() 2086 if not pat.connected: 2087 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 2088 return False 2089 gmEMRStructWidgets.select_encounters()
2090 #----------------------------------------------
2091 - def __on_add_health_issue(self, event):
2092 pat = gmPerson.gmCurrentPatient() 2093 if not pat.connected: 2094 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add health issue. No active patient.')) 2095 return False 2096 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
2097 #----------------------------------------------
2098 - def __on_add_medication(self, evt):
2099 pat = gmPerson.gmCurrentPatient() 2100 if not pat.connected: 2101 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add medication. No active patient.')) 2102 return False 2103 2104 gmMedicationWidgets.edit_intake_of_substance(parent = self, substance = None) 2105 2106 evt.Skip()
2107 #----------------------------------------------
2108 - def __on_manage_allergies(self, evt):
2109 pat = gmPerson.gmCurrentPatient() 2110 if not pat.connected: 2111 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add allergy. No active patient.')) 2112 return False 2113 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 2114 dlg.ShowModal()
2115 #----------------------------------------------
2116 - def __on_manage_performed_procedures(self, evt):
2117 pat = gmPerson.gmCurrentPatient() 2118 if not pat.connected: 2119 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage performed procedures. No active patient.')) 2120 return False 2121 gmEMRStructWidgets.manage_performed_procedures(parent = self) 2122 evt.Skip()
2123 #----------------------------------------------
2124 - def __on_manage_hospital_stays(self, evt):
2125 pat = gmPerson.gmCurrentPatient() 2126 if not pat.connected: 2127 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage hospital stays. No active patient.')) 2128 return False 2129 gmEMRStructWidgets.manage_hospital_stays(parent = self) 2130 evt.Skip()
2131 #----------------------------------------------
2132 - def __on_edit_occupation(self, evt):
2133 pat = gmPerson.gmCurrentPatient() 2134 if not pat.connected: 2135 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit occupation. No active patient.')) 2136 return False 2137 gmDemographicsWidgets.edit_occupation() 2138 evt.Skip()
2139 #----------------------------------------------
2140 - def __on_add_measurement(self, evt):
2141 pat = gmPerson.gmCurrentPatient() 2142 if not pat.connected: 2143 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add measurement. No active patient.')) 2144 return False 2145 gmMeasurementWidgets.edit_measurement(parent = self, measurement = None) 2146 evt.Skip()
2147 #----------------------------------------------
2148 - def __on_show_emr_summary(self, event):
2149 pat = gmPerson.gmCurrentPatient() 2150 if not pat.connected: 2151 gmDispatcher.send(signal = 'statustext', msg = _('Cannot show EMR summary. No active patient.')) 2152 return False 2153 2154 emr = pat.get_emr() 2155 dlg = wx.MessageDialog ( 2156 parent = self, 2157 message = emr.format_statistics(), 2158 caption = _('EMR Summary'), 2159 style = wx.OK | wx.STAY_ON_TOP 2160 ) 2161 dlg.ShowModal() 2162 dlg.Destroy() 2163 return True
2164 #----------------------------------------------
2165 - def __on_search_emr(self, event):
2166 return gmNarrativeWidgets.search_narrative_in_emr(parent=self)
2167 #----------------------------------------------
2168 - def __on_search_across_emrs(self, event):
2169 gmNarrativeWidgets.search_narrative_across_emrs(parent=self)
2170 #----------------------------------------------
2171 - def __on_export_emr_as_journal(self, event):
2172 # sanity checks 2173 pat = gmPerson.gmCurrentPatient() 2174 if not pat.connected: 2175 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.')) 2176 return False 2177 # get file name 2178 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 2179 # FIXME: make configurable 2180 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])) 2181 gmTools.mkdir(aDefDir) 2182 # FIXME: make configurable 2183 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames']) 2184 dlg = wx.FileDialog ( 2185 parent = self, 2186 message = _("Save patient's EMR journal as..."), 2187 defaultDir = aDefDir, 2188 defaultFile = fname, 2189 wildcard = aWildcard, 2190 style = wx.SAVE 2191 ) 2192 choice = dlg.ShowModal() 2193 fname = dlg.GetPath() 2194 dlg.Destroy() 2195 if choice != wx.ID_OK: 2196 return True 2197 2198 _log.debug('exporting EMR journal to [%s]' % fname) 2199 # instantiate exporter 2200 exporter = gmPatientExporter.cEMRJournalExporter() 2201 2202 wx.BeginBusyCursor() 2203 try: 2204 fname = exporter.export_to_file(filename = fname) 2205 except: 2206 wx.EndBusyCursor() 2207 gmGuiHelpers.gm_show_error ( 2208 _('Error exporting patient EMR as chronological journal.'), 2209 _('EMR journal export') 2210 ) 2211 raise 2212 wx.EndBusyCursor() 2213 2214 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False) 2215 2216 return True
2217 #----------------------------------------------
2218 - def __on_export_for_medistar(self, event):
2219 gmNarrativeWidgets.export_narrative_for_medistar_import ( 2220 parent = self, 2221 soap_cats = u'soap', 2222 encounter = None # IOW, the current one 2223 )
2224 #----------------------------------------------
2225 - def __on_load_external_patient(self, event):
2226 dbcfg = gmCfg.cCfgSQL() 2227 search_immediately = bool(dbcfg.get2 ( 2228 option = 'patient_search.external_sources.immediately_search_if_single_source', 2229 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2230 bias = 'user', 2231 default = 0 2232 )) 2233 gmPatSearchWidgets.get_person_from_external_sources(parent=self, search_immediately=search_immediately, activate_immediately=True)
2234 #----------------------------------------------
2235 - def __on_export_as_gdt(self, event):
2236 curr_pat = gmPerson.gmCurrentPatient() 2237 if not curr_pat.connected: 2238 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.')) 2239 return False 2240 # FIXME: configurable 2241 enc = 'cp850' 2242 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt')) 2243 curr_pat.export_as_gdt(filename = fname, encoding = enc) 2244 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2245 #----------------------------------------------
2246 - def __on_create_new_patient(self, evt):
2247 gmDemographicsWidgets.create_new_person(parent = self, activate = True)
2248 #---------------------------------------------- 2249 # def __on_create_patient(self, event): 2250 # """Launch create patient wizard. 2251 # """ 2252 # wiz = gmDemographicsWidgets.cNewPatientWizard(parent=self) 2253 # wiz.RunWizard(activate=True) 2254 #----------------------------------------------
2255 - def __on_enlist_patient_as_staff(self, event):
2256 pat = gmPerson.gmCurrentPatient() 2257 if not pat.connected: 2258 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add staff member. No active patient.')) 2259 return False 2260 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2261 dlg.ShowModal()
2262 #----------------------------------------------
2263 - def __on_delete_patient(self, event):
2264 pat = gmPerson.gmCurrentPatient() 2265 if not pat.connected: 2266 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete patient. No patient active.')) 2267 return False 2268 gmDemographicsWidgets.disable_identity(identity=pat) 2269 return True
2270 #----------------------------------------------
2271 - def __on_merge_patients(self, event):
2272 gmPatSearchWidgets.merge_patients(parent=self)
2273 #----------------------------------------------
2274 - def __on_add_new_staff(self, event):
2275 """Create new person and add it as staff.""" 2276 #wiz = gmDemographicsWidgets.cNewPatientWizard(parent=self) 2277 #if not wiz.RunWizard(activate=True): 2278 # return False 2279 gmDemographicsWidgets.create_new_person(parent = self, activate = True) 2280 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2281 dlg.ShowModal()
2282 #----------------------------------------------
2283 - def __on_edit_staff_list(self, event):
2284 dlg = gmStaffWidgets.cEditStaffListDlg(parent=self, id=-1) 2285 dlg.ShowModal()
2286 #----------------------------------------------
2287 - def __on_edit_doc_types(self, event):
2288 dlg = gmDocumentWidgets.cEditDocumentTypesDlg(parent=self, id=-1) 2289 dlg.ShowModal()
2290 #----------------------------------------------
2291 - def __on_manage_text_expansion(self, evt):
2292 gmProviderInboxWidgets.configure_keyword_text_expansion(parent=self)
2293 #----------------------------------------------
2294 - def __on_manage_encounter_types(self, evt):
2295 gmEMRStructWidgets.manage_encounter_types(parent=self)
2296 #----------------------------------------------
2297 - def __on_manage_provinces(self, evt):
2298 gmDemographicsWidgets.manage_provinces(parent=self)
2299 #----------------------------------------------
2300 - def __on_manage_substances(self, evt):
2301 gmMedicationWidgets.manage_substances_in_use(parent = self)
2302 #----------------------------------------------
2303 - def __on_manage_branded_drugs(self, evt):
2304 gmMedicationWidgets.manage_branded_drugs(parent = self)
2305 #----------------------------------------------
2306 - def __on_manage_substances_in_brands(self, evt):
2307 gmMedicationWidgets.manage_substances_in_brands(parent = self)
2308 #----------------------------------------------
2309 - def __on_manage_test_orgs(self, evt):
2310 gmMeasurementWidgets.manage_measurement_orgs(parent = self)
2311 #----------------------------------------------
2312 - def __on_manage_test_types(self, evt):
2313 gmMeasurementWidgets.manage_measurement_types(parent = self)
2314 #----------------------------------------------
2315 - def __on_manage_meta_test_types(self, evt):
2316 gmMeasurementWidgets.manage_meta_test_types(parent = self)
2317 #----------------------------------------------
2318 - def __on_update_loinc(self, evt):
2319 gmMeasurementWidgets.update_loinc_reference_data()
2320 #----------------------------------------------
2321 - def __on_update_atc(self, evt):
2322 gmMedicationWidgets.update_atc_reference_data()
2323 #----------------------------------------------
2324 - def __on_manage_vaccines(self, evt):
2325 gmVaccWidgets.manage_vaccines(parent = self)
2326 #----------------------------------------------
2327 - def _clean_exit(self):
2328 """Cleanup helper. 2329 2330 - should ALWAYS be called when this program is 2331 to be terminated 2332 - ANY code that should be executed before a 2333 regular shutdown should go in here 2334 - framework still functional 2335 """ 2336 _log.debug('gmTopLevelFrame._clean_exit() start') 2337 2338 # shut down backend notifications listener 2339 listener = gmBackendListener.gmBackendListener() 2340 try: 2341 listener.shutdown() 2342 except: 2343 _log.exception('cannot stop backend notifications listener thread') 2344 2345 # shutdown application scripting listener 2346 if _scripting_listener is not None: 2347 try: 2348 _scripting_listener.shutdown() 2349 except: 2350 _log.exception('cannot stop scripting listener thread') 2351 2352 # shutdown timers 2353 self.clock_update_timer.Stop() 2354 gmTimer.shutdown() 2355 gmPhraseWheel.shutdown() 2356 2357 # run synchronous pre-exit callback 2358 for call_back in self.__pre_exit_callbacks: 2359 try: 2360 call_back() 2361 except: 2362 print "*** pre-exit callback failed ***" 2363 print call_back 2364 _log.exception('callback [%s] failed', call_back) 2365 2366 # signal imminent demise to plugins 2367 gmDispatcher.send(u'application_closing') 2368 2369 # do not show status line messages anymore 2370 gmDispatcher.disconnect(self._on_set_statustext, 'statustext') 2371 2372 # remember GUI size 2373 curr_width, curr_height = self.GetClientSizeTuple() 2374 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height)) 2375 dbcfg = gmCfg.cCfgSQL() 2376 dbcfg.set ( 2377 option = 'main.window.width', 2378 value = curr_width, 2379 workplace = gmSurgery.gmCurrentPractice().active_workplace 2380 ) 2381 dbcfg.set ( 2382 option = 'main.window.height', 2383 value = curr_height, 2384 workplace = gmSurgery.gmCurrentPractice().active_workplace 2385 ) 2386 2387 if _cfg.get(option = 'debug'): 2388 print '---=== GNUmed shutdown ===---' 2389 print _('You have to manually close this window to finalize shutting down GNUmed.') 2390 print _('This is so that you can inspect the console output at your leisure.') 2391 print '---=== GNUmed shutdown ===---' 2392 2393 # shutdown GUI exception handling 2394 gmExceptionHandlingWidgets.uninstall_wx_exception_handler() 2395 2396 # are we clean ? 2397 import threading 2398 _log.debug("%s active threads", threading.activeCount()) 2399 for t in threading.enumerate(): 2400 _log.debug('thread %s', t) 2401 2402 _log.debug('gmTopLevelFrame._clean_exit() end')
2403 #---------------------------------------------- 2404 # internal API 2405 #----------------------------------------------
2406 - def __set_window_title_template(self):
2407 2408 if _cfg.get(option = 'slave'): 2409 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % ( 2410 _cfg.get(option = 'slave personality'), 2411 _cfg.get(option = 'xml-rpc port') 2412 ) 2413 else: 2414 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2415 #----------------------------------------------
2416 - def __update_window_title(self):
2417 """Update title of main window based on template. 2418 2419 This gives nice tooltips on iconified GNUmed instances. 2420 2421 User research indicates that in the title bar people want 2422 the date of birth, not the age, so please stick to this 2423 convention. 2424 """ 2425 args = {} 2426 2427 pat = gmPerson.gmCurrentPatient() 2428 if pat.connected: 2429 # title = pat['title'] 2430 # if title is None: 2431 # title = '' 2432 # else: 2433 # title = title[:4] 2434 2435 args['pat'] = u'%s %s %s (%s) #%d' % ( 2436 gmTools.coalesce(pat['title'], u'', u'%.4s'), 2437 #title, 2438 pat['firstnames'], 2439 pat['lastnames'], 2440 pat.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()), 2441 pat['pk_identity'] 2442 ) 2443 else: 2444 args['pat'] = _('no patient') 2445 2446 args['prov'] = u'%s%s.%s' % ( 2447 gmTools.coalesce(_provider['title'], u'', u'%s '), 2448 _provider['firstnames'][:1], 2449 _provider['lastnames'] 2450 ) 2451 2452 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace 2453 2454 self.SetTitle(self.__title_template % args)
2455 #---------------------------------------------- 2456 #----------------------------------------------
2457 - def setup_statusbar(self):
2458 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP) 2459 sb.SetStatusWidths([-1, 225]) 2460 # add time and date display to the right corner of the status bar 2461 self.clock_update_timer = wx.PyTimer(self._cb_update_clock) 2462 self._cb_update_clock() 2463 # update every second 2464 self.clock_update_timer.Start(milliseconds = 1000)
2465 #----------------------------------------------
2466 - def _cb_update_clock(self):
2467 """Displays date and local time in the second slot of the status bar""" 2468 t = time.localtime(time.time()) 2469 st = time.strftime('%c', t).decode(gmI18N.get_encoding()) 2470 self.SetStatusText(st,1)
2471 #------------------------------------------------
2472 - def Lock(self):
2473 """Lock GNUmed client against unauthorized access""" 2474 # FIXME 2475 # for i in range(1, self.nb.GetPageCount()): 2476 # self.nb.GetPage(i).Enable(False) 2477 return
2478 #----------------------------------------------
2479 - def Unlock(self):
2480 """Unlock the main notebook widgets 2481 As long as we are not logged into the database backend, 2482 all pages but the 'login' page of the main notebook widget 2483 are locked; i.e. not accessible by the user 2484 """ 2485 #unlock notebook pages 2486 # for i in range(1, self.nb.GetPageCount()): 2487 # self.nb.GetPage(i).Enable(True) 2488 # go straight to patient selection 2489 # self.nb.AdvanceSelection() 2490 return
2491 #-----------------------------------------------
2492 - def OnPanelSize (self, event):
2493 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
2494 #==============================================================================
2495 -class gmApp(wx.App):
2496
2497 - def OnInit(self):
2498 2499 self.__starting_up = True 2500 2501 gmExceptionHandlingWidgets.install_wx_exception_handler() 2502 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version')) 2503 2504 # _log.info('display: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 2505 2506 # set this so things like "wx.StandardPaths.GetDataDir()" work as expected 2507 self.SetAppName(u'gnumed') 2508 self.SetVendorName(u'The GNUmed Development Community.') 2509 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 2510 paths.init_paths(wx = wx, app_name = u'gnumed') 2511 2512 if not self.__setup_prefs_file(): 2513 return False 2514 2515 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email) 2516 2517 self.__guibroker = gmGuiBroker.GuiBroker() 2518 self.__setup_platform() 2519 2520 if not self.__establish_backend_connection(): 2521 return False 2522 2523 if not _cfg.get(option = 'skip-update-check'): 2524 self.__check_for_updates() 2525 2526 if _cfg.get(option = 'slave'): 2527 if not self.__setup_scripting_listener(): 2528 return False 2529 2530 # FIXME: load last position from backend 2531 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640,440)) 2532 frame.CentreOnScreen(wx.BOTH) 2533 self.SetTopWindow(frame) 2534 frame.Show(True) 2535 2536 if _cfg.get(option = 'debug'): 2537 self.RedirectStdio() 2538 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window')) 2539 # print this so people know what this window is for 2540 # and don't get suprised when it pops up later 2541 print '---=== GNUmed startup ===---' 2542 print _('redirecting STDOUT/STDERR to this log window') 2543 print '---=== GNUmed startup ===---' 2544 2545 self.__setup_user_activity_timer() 2546 self.__register_events() 2547 2548 wx.CallAfter(self._do_after_init) 2549 2550 return True
2551 #----------------------------------------------
2552 - def OnExit(self):
2553 """Called internally by wxPython after EVT_CLOSE has been handled on last frame. 2554 2555 - after destroying all application windows and controls 2556 - before wx.Windows internal cleanup 2557 """ 2558 _log.debug('gmApp.OnExit() start') 2559 2560 self.__shutdown_user_activity_timer() 2561 2562 if _cfg.get(option = 'debug'): 2563 self.RestoreStdio() 2564 sys.stdin = sys.__stdin__ 2565 sys.stdout = sys.__stdout__ 2566 sys.stderr = sys.__stderr__ 2567 2568 _log.debug('gmApp.OnExit() end')
2569 #----------------------------------------------
2570 - def _on_query_end_session(self, *args, **kwargs):
2571 wx.Bell() 2572 wx.Bell() 2573 wx.Bell() 2574 _log.warning('unhandled event detected: QUERY_END_SESSION') 2575 _log.info('we should be saving ourselves from here') 2576 gmLog2.flush() 2577 print "unhandled event detected: QUERY_END_SESSION"
2578 #----------------------------------------------
2579 - def _on_end_session(self, *args, **kwargs):
2580 wx.Bell() 2581 wx.Bell() 2582 wx.Bell() 2583 _log.warning('unhandled event detected: END_SESSION') 2584 gmLog2.flush() 2585 print "unhandled event detected: END_SESSION"
2586 #----------------------------------------------
2587 - def _on_app_activated(self, evt):
2588 if evt.GetActive(): 2589 if self.__starting_up: 2590 gmHooks.run_hook_script(hook = u'app_activated_startup') 2591 else: 2592 gmHooks.run_hook_script(hook = u'app_activated') 2593 else: 2594 gmHooks.run_hook_script(hook = u'app_deactivated') 2595 2596 evt.Skip()
2597 #----------------------------------------------
2598 - def _on_user_activity(self, evt):
2599 self.user_activity_detected = True 2600 evt.Skip()
2601 #----------------------------------------------
2602 - def _on_user_activity_timer_expired(self, cookie=None):
2603 2604 if self.user_activity_detected: 2605 self.elapsed_inactivity_slices = 0 2606 self.user_activity_detected = False 2607 self.elapsed_inactivity_slices += 1 2608 else: 2609 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices: 2610 # print "User was inactive for 30 seconds." 2611 pass 2612 2613 self.user_activity_timer.Start(oneShot = True)
2614 #---------------------------------------------- 2615 # internal helpers 2616 #----------------------------------------------
2617 - def _signal_debugging_monitor(*args, **kwargs):
2618 try: 2619 kwargs['originated_in_database'] 2620 print '==> got notification from database "%s":' % kwargs['signal'] 2621 except KeyError: 2622 print '==> received signal from client: "%s"' % kwargs['signal'] 2623 2624 del kwargs['signal'] 2625 for key in kwargs.keys(): 2626 print ' [%s]: %s' % (key, kwargs[key])
2627 #----------------------------------------------
2628 - def _signal_debugging_monitor_pubsub(self, msg):
2629 print "wx.lib.pubsub message:" 2630 print msg.topic 2631 print msg.data
2632 #----------------------------------------------
2633 - def _do_after_init(self):
2634 self.__starting_up = False 2635 gmClinicalRecord.set_func_ask_user(a_func = gmEMRStructWidgets.ask_for_encounter_continuation) 2636 self.__guibroker['horstspace.top_panel'].patient_selector.SetFocus() 2637 gmHooks.run_hook_script(hook = u'startup-after-GUI-init')
2638 #----------------------------------------------
2640 self.user_activity_detected = True 2641 self.elapsed_inactivity_slices = 0 2642 # FIXME: make configurable 2643 self.max_user_inactivity_slices = 15 # 15 * 2000ms == 30 seconds 2644 self.user_activity_timer = gmTimer.cTimer ( 2645 callback = self._on_user_activity_timer_expired, 2646 delay = 2000 # hence a minimum of 2 and max of 3.999... seconds after which inactivity is detected 2647 ) 2648 self.user_activity_timer.Start(oneShot=True)
2649 #----------------------------------------------
2651 try: 2652 self.user_activity_timer.Stop() 2653 del self.user_activity_timer 2654 except: 2655 pass
2656 #----------------------------------------------
2657 - def __register_events(self):
2658 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session) 2659 wx.EVT_END_SESSION(self, self._on_end_session) 2660 2661 # You can bind your app to wx.EVT_ACTIVATE_APP which will fire when your 2662 # app gets/looses focus, or you can wx.EVT_ACTIVATE with any of your 2663 # toplevel windows and call evt.GetActive() in the handler to see whether 2664 # it is gaining or loosing focus. 2665 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated) 2666 2667 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity) 2668 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
2669 2670 # if _cfg.get(option = 'debug'): 2671 # gmDispatcher.connect(receiver = self._signal_debugging_monitor) 2672 # _log.debug('connected old signal monitor') 2673 # wx.lib.pubsub.Publisher().subscribe ( 2674 # listener = self._signal_debugging_monitor_pubsub, 2675 # topic = wx.lib.pubsub.getStrAllTopics() 2676 # ) 2677 # _log.debug('connected wx.lib.pubsub based signal monitor for all topics: [%s]', wx.lib.pubsub.getStrAllTopics()) 2678 #----------------------------------------------
2679 - def __check_for_updates(self):
2680 2681 dbcfg = gmCfg.cCfgSQL() 2682 2683 do_check = bool(dbcfg.get2 ( 2684 option = u'horstspace.update.autocheck_at_startup', 2685 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2686 bias = 'workplace', 2687 default = True 2688 )) 2689 2690 if not do_check: 2691 return 2692 2693 gmCfgWidgets.check_for_updates()
2694 #----------------------------------------------
2696 """Handle all the database related tasks necessary for startup.""" 2697 2698 # log on 2699 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')]) 2700 2701 from Gnumed.wxpython import gmAuthWidgets 2702 connected = gmAuthWidgets.connect_to_database ( 2703 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')], 2704 require_version = not override 2705 ) 2706 if not connected: 2707 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection") 2708 return False 2709 2710 # check account <-> staff member association 2711 try: 2712 global _provider 2713 _provider = gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 2714 except ValueError: 2715 account = gmPG2.get_current_user() 2716 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account) 2717 msg = _( 2718 'The database account [%s] cannot be used as a\n' 2719 'staff member login for GNUmed. There was an\n' 2720 'error retrieving staff details for it.\n\n' 2721 'Please ask your administrator for help.\n' 2722 ) % account 2723 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions')) 2724 return False 2725 2726 # improve exception handler setup 2727 tmp = '%s%s %s (%s = %s)' % ( 2728 gmTools.coalesce(_provider['title'], ''), 2729 _provider['firstnames'], 2730 _provider['lastnames'], 2731 _provider['short_alias'], 2732 _provider['db_user'] 2733 ) 2734 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp) 2735 2736 # display database banner 2737 surgery = gmSurgery.gmCurrentPractice() 2738 msg = surgery.db_logon_banner 2739 if msg.strip() != u'': 2740 2741 login = gmPG2.get_default_login() 2742 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % ( 2743 login.database, 2744 gmTools.coalesce(login.host, u'localhost') 2745 )) 2746 msg = auth + msg + u'\n\n' 2747 2748 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 2749 None, 2750 -1, 2751 caption = _('Verifying database'), 2752 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '), 2753 button_defs = [ 2754 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True}, 2755 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False} 2756 ] 2757 ) 2758 go_on = dlg.ShowModal() 2759 dlg.Destroy() 2760 if go_on != wx.ID_YES: 2761 _log.info('user decided to not connect to this database') 2762 return False 2763 2764 # check database language settings 2765 self.__check_db_lang() 2766 2767 return True
2768 #----------------------------------------------
2769 - def __setup_prefs_file(self):
2770 """Setup access to a config file for storing preferences.""" 2771 2772 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 2773 2774 candidates = [] 2775 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')]) 2776 if explicit_file is not None: 2777 candidates.append(explicit_file) 2778 # provide a few fallbacks in the event the --conf-file isn't writable 2779 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf')) 2780 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf')) 2781 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf')) 2782 2783 prefs_file = None 2784 for candidate in candidates: 2785 try: 2786 open(candidate, 'a+').close() 2787 prefs_file = candidate 2788 break 2789 except IOError: 2790 continue 2791 2792 if prefs_file is None: 2793 msg = _( 2794 'Cannot find configuration file in any of:\n' 2795 '\n' 2796 ' %s\n' 2797 'You may need to use the comand line option\n' 2798 '\n' 2799 ' --conf-file=<FILE>' 2800 ) % '\n '.join(candidates) 2801 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files')) 2802 return False 2803 2804 _cfg.set_option(option = u'user_preferences_file', value = prefs_file) 2805 _log.info('user preferences file: %s', prefs_file) 2806 2807 return True
2808 #----------------------------------------------
2809 - def __setup_scripting_listener(self):
2810 2811 from socket import error as SocketError 2812 from Gnumed.pycommon import gmScriptingListener 2813 from Gnumed.wxpython import gmMacro 2814 2815 slave_personality = gmTools.coalesce ( 2816 _cfg.get ( 2817 group = u'workplace', 2818 option = u'slave personality', 2819 source_order = [ 2820 ('explicit', 'return'), 2821 ('workbase', 'return'), 2822 ('user', 'return'), 2823 ('system', 'return') 2824 ] 2825 ), 2826 u'gnumed-client' 2827 ) 2828 _cfg.set_option(option = 'slave personality', value = slave_personality) 2829 2830 # FIXME: handle port via /var/run/ 2831 port = int ( 2832 gmTools.coalesce ( 2833 _cfg.get ( 2834 group = u'workplace', 2835 option = u'xml-rpc port', 2836 source_order = [ 2837 ('explicit', 'return'), 2838 ('workbase', 'return'), 2839 ('user', 'return'), 2840 ('system', 'return') 2841 ] 2842 ), 2843 9999 2844 ) 2845 ) 2846 _cfg.set_option(option = 'xml-rpc port', value = port) 2847 2848 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality) 2849 global _scripting_listener 2850 try: 2851 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor) 2852 except SocketError, e: 2853 _log.exception('cannot start GNUmed XML-RPC server') 2854 gmGuiHelpers.gm_show_error ( 2855 aMessage = ( 2856 'Cannot start the GNUmed server:\n' 2857 '\n' 2858 ' [%s]' 2859 ) % e, 2860 aTitle = _('GNUmed startup') 2861 ) 2862 return False 2863 2864 return True
2865 #----------------------------------------------
2866 - def __setup_platform(self):
2867 2868 import wx.lib.colourdb 2869 wx.lib.colourdb.updateColourDB() 2870 2871 traits = self.GetTraits() 2872 try: 2873 _log.info('desktop environment: [%s]', traits.GetDesktopEnvironment()) 2874 except: 2875 pass 2876 2877 if wx.Platform == '__WXMSW__': 2878 _log.info('running on MS Windows') 2879 elif wx.Platform == '__WXGTK__': 2880 _log.info('running on GTK (probably Linux)') 2881 elif wx.Platform == '__WXMAC__': 2882 _log.info('running on Mac OS') 2883 else: 2884 _log.info('running on an unknown platform (%s)' % wx.Platform)
2885 #----------------------------------------------
2886 - def __check_db_lang(self):
2887 if gmI18N.system_locale is None or gmI18N.system_locale == '': 2888 _log.warning("system locale is undefined (probably meaning 'C')") 2889 return True 2890 2891 # get current database locale 2892 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}]) 2893 db_lang = rows[0]['lang'] 2894 2895 if db_lang is None: 2896 _log.debug("database locale currently not set") 2897 msg = _( 2898 "There is no language selected in the database for user [%s].\n" 2899 "Your system language is currently set to [%s].\n\n" 2900 "Do you want to set the database language to '%s' ?\n\n" 2901 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale) 2902 checkbox_msg = _('Remember to ignore missing language') 2903 else: 2904 _log.debug("current database locale: [%s]" % db_lang) 2905 msg = _( 2906 "The currently selected database language ('%s') does\n" 2907 "not match the current system language ('%s').\n" 2908 "\n" 2909 "Do you want to set the database language to '%s' ?\n" 2910 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale) 2911 checkbox_msg = _('Remember to ignore language mismatch') 2912 2913 # check if we can match up system and db language somehow 2914 if db_lang == gmI18N.system_locale_level['full']: 2915 _log.debug('Database locale (%s) up to date.' % db_lang) 2916 return True 2917 if db_lang == gmI18N.system_locale_level['country']: 2918 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale)) 2919 return True 2920 if db_lang == gmI18N.system_locale_level['language']: 2921 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale)) 2922 return True 2923 # no match 2924 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale)) 2925 2926 # returns either None or a locale string 2927 ignored_sys_lang = _cfg.get ( 2928 group = u'backend', 2929 option = u'ignored mismatching system locale', 2930 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')] 2931 ) 2932 2933 # are we to ignore *this* mismatch ? 2934 if gmI18N.system_locale == ignored_sys_lang: 2935 _log.info('configured to ignore system-to-database locale mismatch') 2936 return True 2937 2938 # no, so ask user 2939 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 2940 None, 2941 -1, 2942 caption = _('Checking database language settings'), 2943 question = msg, 2944 button_defs = [ 2945 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True}, 2946 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False} 2947 ], 2948 show_checkbox = True, 2949 checkbox_msg = checkbox_msg, 2950 checkbox_tooltip = _( 2951 'Checking this will make GNUmed remember your decision\n' 2952 'until the system language is changed.\n' 2953 '\n' 2954 'You can also reactivate this inquiry by removing the\n' 2955 'corresponding "ignore" option from the configuration file\n' 2956 '\n' 2957 ' [%s]' 2958 ) % _cfg.get(option = 'user_preferences_file') 2959 ) 2960 decision = dlg.ShowModal() 2961 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue() 2962 dlg.Destroy() 2963 2964 if decision == wx.ID_NO: 2965 if not remember_ignoring_problem: 2966 return True 2967 _log.info('User did not want to set database locale. Ignoring mismatch next time.') 2968 gmCfg2.set_option_in_INI_file ( 2969 filename = _cfg.get(option = 'user_preferences_file'), 2970 group = 'backend', 2971 option = 'ignored mismatching system locale', 2972 value = gmI18N.system_locale 2973 ) 2974 return True 2975 2976 # try setting database language (only possible if translation exists) 2977 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]: 2978 if len(lang) > 0: 2979 # users are getting confused, so don't show these "errors", 2980 # they really are just notices about us being nice 2981 rows, idx = gmPG2.run_rw_queries ( 2982 link_obj = None, 2983 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}], 2984 return_data = True 2985 ) 2986 if rows[0][0]: 2987 _log.debug("Successfully set database language to [%s]." % lang) 2988 else: 2989 _log.error('Cannot set database language to [%s].' % lang) 2990 continue 2991 return True 2992 2993 # no match found but user wanted to set language anyways, so force it 2994 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country']) 2995 gmPG2.run_rw_queries(queries = [{ 2996 'cmd': u'select i18n.force_curr_lang(%s)', 2997 'args': [gmI18N.system_locale_level['country']] 2998 }]) 2999 3000 return True
3001 #==============================================================================
3002 -def _signal_debugging_monitor(*args, **kwargs):
3003 try: 3004 kwargs['originated_in_database'] 3005 print '==> got notification from database "%s":' % kwargs['signal'] 3006 except KeyError: 3007 print '==> received signal from client: "%s"' % kwargs['signal'] 3008 3009 del kwargs['signal'] 3010 for key in kwargs.keys(): 3011 # careful because of possibly limited console output encoding 3012 try: print ' [%s]: %s' % (key, kwargs[key]) 3013 except: print 'cannot print signal information'
3014 #------------------------------------------------------------------------------
3015 -def _signal_debugging_monitor_pubsub(msg):
3016 # careful because of possibly limited console output encoding 3017 try: 3018 print '==> received wx.lib.pubsub message: "%s"' % msg.topic 3019 print ' data: %s' % msg.data 3020 print msg 3021 except: print 'problem printing pubsub message information'
3022 #==============================================================================
3023 -def main():
3024 3025 if _cfg.get(option = 'debug'): 3026 gmDispatcher.connect(receiver = _signal_debugging_monitor) 3027 _log.debug('gmDispatcher signal monitor activated') 3028 wx.lib.pubsub.Publisher().subscribe ( 3029 listener = _signal_debugging_monitor_pubsub, 3030 topic = wx.lib.pubsub.getStrAllTopics() 3031 ) 3032 _log.debug('wx.lib.pubsub signal monitor activated') 3033 3034 # create an instance of our GNUmed main application 3035 # - do not redirect stdio (yet) 3036 # - allow signals to be delivered 3037 app = gmApp(redirect = False, clearSigInt = False) 3038 app.MainLoop()
3039 #============================================================================== 3040 # Main 3041 #============================================================================== 3042 if __name__ == '__main__': 3043 3044 from GNUmed.pycommon import gmI18N 3045 gmI18N.activate_locale() 3046 gmI18N.install_domain() 3047 3048 _log.info('Starting up as main module.') 3049 main() 3050 3051 #============================================================================== 3052