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