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

Source Code for Module Gnumed.wxpython.gmGuiMain

   1  # -*- coding: utf8 -*- 
   2  """GNUmed GUI client. 
   3   
   4  This contains the GUI application framework and main window 
   5  of the all signing all dancing GNUmed Python Reference 
   6  client. It relies on the <gnumed.py> launcher having set up 
   7  the non-GUI-related runtime environment. 
   8   
   9  This source code is protected by the GPL licensing scheme. 
  10  Details regarding the GPL are available at http://www.gnu.org 
  11  You may use and share it as long as you don't deny this right 
  12  to anybody else. 
  13   
  14  copyright: authors 
  15  """ 
  16  #============================================================================== 
  17  __version__ = "$Revision: 1.491 $" 
  18  __author__  = "H. Herb <hherb@gnumed.net>,\ 
  19                             K. Hilbert <Karsten.Hilbert@gmx.net>,\ 
  20                             I. Haywood <i.haywood@ugrad.unimelb.edu.au>" 
  21  __license__ = 'GPL (details at http://www.gnu.org)' 
  22   
  23  # stdlib 
  24  import sys, time, os, cPickle, zlib, locale, os.path, datetime as pyDT 
  25  import webbrowser, shutil, logging, urllib2, subprocess, glob 
  26   
  27   
  28  # 3rd party libs 
  29  # wxpython version cannot be enforced inside py2exe and friends 
  30  if not hasattr(sys, 'frozen'): 
  31          import wxversion 
  32          wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) 
  33   
  34  try: 
  35          import wx 
  36          import wx.lib.pubsub 
  37  except ImportError: 
  38          print "GNUmed startup: Cannot import wxPython library." 
  39          print "GNUmed startup: Make sure wxPython is installed." 
  40          print 'CRITICAL ERROR: Error importing wxPython. Halted.' 
  41          raise 
  42   
  43  # do this check just in case, so we can make sure 
  44  # py2exe and friends include the proper version, too 
  45  version = int(u'%s%s' % (wx.MAJOR_VERSION, wx.MINOR_VERSION)) 
  46  if (version < 28) or ('unicode' not in wx.PlatformInfo): 
  47          print "GNUmed startup: Unsupported wxPython version (%s: %s)." % (wx.VERSION_STRING, wx.PlatformInfo) 
  48          print "GNUmed startup: wxPython 2.8+ with unicode support is required." 
  49          print 'CRITICAL ERROR: Proper wxPython version not found. Halted.' 
  50          raise ValueError('wxPython 2.8+ with unicode support not found') 
  51   
  52   
  53  # GNUmed libs 
  54  from Gnumed.pycommon import gmCfg, gmPG2, gmDispatcher, gmGuiBroker, gmI18N 
  55  from Gnumed.pycommon import gmExceptions, gmShellAPI, gmTools, gmDateTime 
  56  from Gnumed.pycommon import gmHooks, gmBackendListener, gmCfg2, gmLog2 
  57   
  58  from Gnumed.business import gmPerson, gmClinicalRecord, gmSurgery, gmEMRStructItems 
  59   
  60  from Gnumed.exporters import gmPatientExporter 
  61   
  62  from Gnumed.wxpython import gmGuiHelpers, gmHorstSpace, gmEMRBrowser 
  63  from Gnumed.wxpython import gmDemographicsWidgets, gmEMRStructWidgets 
  64  from Gnumed.wxpython import gmPatSearchWidgets, gmAllergyWidgets, gmListWidgets 
  65  from Gnumed.wxpython import gmProviderInboxWidgets, gmCfgWidgets, gmExceptionHandlingWidgets 
  66  from Gnumed.wxpython import gmNarrativeWidgets, gmPhraseWheel, gmMedicationWidgets 
  67  from Gnumed.wxpython import gmStaffWidgets, gmDocumentWidgets, gmTimer, gmMeasurementWidgets 
  68  from Gnumed.wxpython import gmFormWidgets, gmSnellen, gmVaccWidgets 
  69   
  70  try: 
  71          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
  72  except NameError: 
  73          _ = lambda x:x 
  74   
  75  _cfg = gmCfg2.gmCfgData() 
  76  _provider = None 
  77  _scripting_listener = None 
  78   
  79  _log = logging.getLogger('gm.main') 
  80  _log.info(__version__) 
  81  _log.info('wxPython GUI framework: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo)) 
  82   
  83  #============================================================================== 
  84  icon_serpent = \ 
  85  """x\xdae\x8f\xb1\x0e\x83 \x10\x86w\x9f\xe2\x92\x1blb\xf2\x07\x96\xeaH:0\xd6\ 
  86  \xc1\x85\xd5\x98N5\xa5\xef?\xf5N\xd0\x8a\xdcA\xc2\xf7qw\x84\xdb\xfa\xb5\xcd\ 
  87  \xd4\xda;\xc9\x1a\xc8\xb6\xcd<\xb5\xa0\x85\x1e\xeb\xbc\xbc7b!\xf6\xdeHl\x1c\ 
  88  \x94\x073\xec<*\xf7\xbe\xf7\x99\x9d\xb21~\xe7.\xf5\x1f\x1c\xd3\xbdVlL\xc2\ 
  89  \xcf\xf8ye\xd0\x00\x90\x0etH \x84\x80B\xaa\x8a\x88\x85\xc4(U\x9d$\xfeR;\xc5J\ 
  90  \xa6\x01\xbbt9\xceR\xc8\x81e_$\x98\xb9\x9c\xa9\x8d,y\xa9t\xc8\xcf\x152\xe0x\ 
  91  \xe9$\xf5\x07\x95\x0cD\x95t:\xb1\x92\xae\x9cI\xa8~\x84\x1f\xe0\xa3ec""" 
  92  #============================================================================== 
93 -class gmTopLevelFrame(wx.Frame):
94 """GNUmed client's main windows frame. 95 96 This is where it all happens. Avoid popping up any other windows. 97 Most user interaction should happen to and from widgets within this frame 98 """ 99 #----------------------------------------------
100 - def __init__(self, parent, id, title, size=wx.DefaultSize):
101 """You'll have to browse the source to understand what the constructor does 102 """ 103 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE) 104 105 self.__gb = gmGuiBroker.GuiBroker() 106 self.__pre_exit_callbacks = [] 107 self.bar_width = -1 108 self.menu_id2plugin = {} 109 110 _log.info('workplace is >>>%s<<<', gmSurgery.gmCurrentPractice().active_workplace) 111 112 self.__setup_main_menu() 113 self.setup_statusbar() 114 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % ( 115 gmTools.coalesce(_provider['title'], ''), 116 _provider['firstnames'][:1], 117 _provider['lastnames'], 118 _provider['short_alias'], 119 _provider['db_user'] 120 )) 121 122 self.__set_window_title_template() 123 self.__update_window_title() 124 self.__set_window_icon() 125 126 self.__register_events() 127 128 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1) 129 self.vbox = wx.BoxSizer(wx.VERTICAL) 130 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1) 131 132 self.SetAutoLayout(True) 133 self.SetSizerAndFit(self.vbox) 134 135 # don't allow the window to get too small 136 # setsizehints only allows minimum size, therefore window can't become small enough 137 # effectively we need the font size to be configurable according to screen size 138 #self.vbox.SetSizeHints(self) 139 self.__set_GUI_size()
140 #----------------------------------------------
141 - def __set_window_icon(self):
142 # set window icon 143 icon_bmp_data = wx.BitmapFromXPMData(cPickle.loads(zlib.decompress(icon_serpent))) 144 icon = wx.EmptyIcon() 145 icon.CopyFromBitmap(icon_bmp_data) 146 self.SetIcon(icon)
147 #----------------------------------------------
148 - def __set_GUI_size(self):
149 """Try to get previous window size from backend.""" 150 151 cfg = gmCfg.cCfgSQL() 152 153 # width 154 width = int(cfg.get2 ( 155 option = 'main.window.width', 156 workplace = gmSurgery.gmCurrentPractice().active_workplace, 157 bias = 'workplace', 158 default = 800 159 )) 160 161 # height 162 height = int(cfg.get2 ( 163 option = 'main.window.height', 164 workplace = gmSurgery.gmCurrentPractice().active_workplace, 165 bias = 'workplace', 166 default = 600 167 )) 168 169 dw = wx.DisplaySize()[0] 170 dh = wx.DisplaySize()[1] 171 172 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 173 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM())) 174 _log.debug('previous GUI size [%s:%s]', width, height) 175 176 # max size 177 if width > dw: 178 _log.debug('adjusting GUI width from %s to %s', width, dw) 179 width = dw 180 181 if height > dh: 182 _log.debug('adjusting GUI height from %s to %s', height, dh) 183 height = dh 184 185 # min size 186 if width < 100: 187 _log.debug('adjusting GUI width to minimum of 100 pixel') 188 width = 100 189 if height < 100: 190 _log.debug('adjusting GUI height to minimum of 100 pixel') 191 height = 100 192 193 _log.info('setting GUI to size [%s:%s]', width, height) 194 195 self.SetClientSize(wx.Size(width, height))
196 #----------------------------------------------
197 - def __setup_main_menu(self):
198 """Create the main menu entries. 199 200 Individual entries are farmed out to the modules. 201 """ 202 global wx 203 self.mainmenu = wx.MenuBar() 204 self.__gb['main.mainmenu'] = self.mainmenu 205 206 # -- menu "GNUmed" ----------------- 207 menu_gnumed = wx.Menu() 208 209 self.menu_plugins = wx.Menu() 210 menu_gnumed.AppendMenu(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins) 211 212 ID = wx.NewId() 213 menu_gnumed.Append(ID, _('Check for updates'), _('Check for new releases of the GNUmed client.')) 214 wx.EVT_MENU(self, ID, self.__on_check_for_updates) 215 216 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.')) 217 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item) 218 219 # -- 220 menu_gnumed.AppendSeparator() 221 222 # GNUmed / Preferences 223 menu_config = wx.Menu() 224 menu_gnumed.AppendMenu(wx.NewId(), _('Preferences ...'), menu_config) 225 226 item = menu_config.Append(-1, _('List configuration'), _('List all configuration items stored in the database.')) 227 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item) 228 229 # GNUmed / Preferences / Database 230 menu_cfg_db = wx.Menu() 231 menu_config.AppendMenu(wx.NewId(), _('Database ...'), menu_cfg_db) 232 233 ID = wx.NewId() 234 menu_cfg_db.Append(ID, _('Language'), _('Configure the database language')) 235 wx.EVT_MENU(self, ID, self.__on_configure_db_lang) 236 237 ID = wx.NewId() 238 menu_cfg_db.Append(ID, _('Welcome message'), _('Configure the database welcome message (all users).')) 239 wx.EVT_MENU(self, ID, self.__on_configure_db_welcome) 240 241 # GNUmed / Preferences / Client 242 menu_cfg_client = wx.Menu() 243 menu_config.AppendMenu(wx.NewId(), _('Client parameters ...'), menu_cfg_client) 244 245 ID = wx.NewId() 246 menu_cfg_client.Append(ID, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.')) 247 wx.EVT_MENU(self, ID, self.__on_configure_export_chunk_size) 248 249 ID = wx.NewId() 250 menu_cfg_client.Append(ID, _('Temporary directory'), _('Configure the directory to use as scratch space for temporary files.')) 251 wx.EVT_MENU(self, ID, self.__on_configure_temp_dir) 252 253 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.')) 254 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item) 255 256 # GNUmed / Preferences / User Interface 257 menu_cfg_ui = wx.Menu() 258 menu_config.AppendMenu(wx.NewId(), _('User interface ...'), menu_cfg_ui) 259 260 # -- submenu gnumed / config / ui / docs 261 menu_cfg_doc = wx.Menu() 262 menu_cfg_ui.AppendMenu(wx.NewId(), _('Document handling ...'), menu_cfg_doc) 263 264 ID = wx.NewId() 265 menu_cfg_doc.Append(ID, _('Review dialog'), _('Configure review dialog after document display.')) 266 wx.EVT_MENU(self, ID, self.__on_configure_doc_review_dialog) 267 268 ID = wx.NewId() 269 menu_cfg_doc.Append(ID, _('UUID display'), _('Configure unique ID dialog on document import.')) 270 wx.EVT_MENU(self, ID, self.__on_configure_doc_uuid_dialog) 271 272 ID = wx.NewId() 273 menu_cfg_doc.Append(ID, _('Empty documents'), _('Whether to allow saving documents without parts.')) 274 wx.EVT_MENU(self, ID, self.__on_configure_partless_docs) 275 276 # -- submenu gnumed / config / ui / updates 277 menu_cfg_update = wx.Menu() 278 menu_cfg_ui.AppendMenu(wx.NewId(), _('Update handling ...'), menu_cfg_update) 279 280 ID = wx.NewId() 281 menu_cfg_update.Append(ID, _('Auto-check'), _('Whether to auto-check for updates at startup.')) 282 wx.EVT_MENU(self, ID, self.__on_configure_update_check) 283 284 ID = wx.NewId() 285 menu_cfg_update.Append(ID, _('Check scope'), _('When checking for updates, consider latest branch, too ?')) 286 wx.EVT_MENU(self, ID, self.__on_configure_update_check_scope) 287 288 ID = wx.NewId() 289 menu_cfg_update.Append(ID, _('URL'), _('The URL to retrieve version information from.')) 290 wx.EVT_MENU(self, ID, self.__on_configure_update_url) 291 292 # -- submenu gnumed / config / ui / patient 293 menu_cfg_pat_search = wx.Menu() 294 menu_cfg_ui.AppendMenu(wx.NewId(), _('Person ...'), menu_cfg_pat_search) 295 296 ID = wx.NewId() 297 menu_cfg_pat_search.Append(ID, _('Birthday reminder'), _('Configure birthday reminder proximity interval.')) 298 wx.EVT_MENU(self, ID, self.__on_configure_dob_reminder_proximity) 299 300 ID = wx.NewId() 301 menu_cfg_pat_search.Append(ID, _('Immediate source activation'), _('Configure immediate activation of single external person.')) 302 wx.EVT_MENU(self, ID, self.__on_configure_quick_pat_search) 303 304 ID = wx.NewId() 305 menu_cfg_pat_search.Append(ID, _('Initial plugin'), _('Configure which plugin to show right after person activation.')) 306 wx.EVT_MENU(self, ID, self.__on_configure_initial_pat_plugin) 307 308 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default province/region/state for person creation.')) 309 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item) 310 311 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.')) 312 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item) 313 314 # -- submenu gnumed / config / ui / soap handling 315 menu_cfg_soap_editing = wx.Menu() 316 menu_cfg_ui.AppendMenu(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing) 317 318 ID = wx.NewId() 319 menu_cfg_soap_editing.Append(ID, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.')) 320 wx.EVT_MENU(self, ID, self.__on_allow_multiple_new_episodes) 321 322 # GNUmed / Preferences / External tools 323 menu_cfg_ext_tools = wx.Menu() 324 menu_config.AppendMenu(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools) 325 326 # ID = wx.NewId() 327 # menu_cfg_ext_tools.Append(ID, _('IFAP command'), _('Set the command to start IFAP.')) 328 # wx.EVT_MENU(self, ID, self.__on_configure_ifap_cmd) 329 330 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.')) 331 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item) 332 333 ID = wx.NewId() 334 menu_cfg_ext_tools.Append(ID, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.')) 335 wx.EVT_MENU(self, ID, self.__on_configure_ooo_settle_time) 336 337 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.')) 338 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item) 339 340 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.')) 341 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item) 342 343 item = menu_cfg_ext_tools.Append(-1, _('FreeDiams path'), _('Set the path for the FreeDiams binary.')) 344 self.Bind(wx.EVT_MENU, self.__on_configure_freediams_cmd, item) 345 346 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.')) 347 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item) 348 349 # -- submenu gnumed / config / emr 350 menu_cfg_emr = wx.Menu() 351 menu_config.AppendMenu(wx.NewId(), _('EMR ...'), menu_cfg_emr) 352 353 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.')) 354 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item) 355 356 # -- submenu gnumed / config / emr / encounter 357 menu_cfg_encounter = wx.Menu() 358 menu_cfg_emr.AppendMenu(wx.NewId(), _('Encounter ...'), menu_cfg_encounter) 359 360 ID = wx.NewId() 361 menu_cfg_encounter.Append(ID, _('Edit on patient change'), _('Edit encounter details on changing of patients.')) 362 wx.EVT_MENU(self, ID, self.__on_cfg_enc_pat_change) 363 364 ID = wx.NewId() 365 menu_cfg_encounter.Append(ID, _('Minimum duration'), _('Minimum duration of an encounter.')) 366 wx.EVT_MENU(self, ID, self.__on_cfg_enc_min_ttl) 367 368 ID = wx.NewId() 369 menu_cfg_encounter.Append(ID, _('Maximum duration'), _('Maximum duration of an encounter.')) 370 wx.EVT_MENU(self, ID, self.__on_cfg_enc_max_ttl) 371 372 ID = wx.NewId() 373 menu_cfg_encounter.Append(ID, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.')) 374 wx.EVT_MENU(self, ID, self.__on_cfg_enc_empty_ttl) 375 376 ID = wx.NewId() 377 menu_cfg_encounter.Append(ID, _('Default type'), _('Default type for new encounters.')) 378 wx.EVT_MENU(self, ID, self.__on_cfg_enc_default_type) 379 380 # -- submenu gnumed / config / emr / episode 381 menu_cfg_episode = wx.Menu() 382 menu_cfg_emr.AppendMenu(wx.NewId(), _('Episode ...'), menu_cfg_episode) 383 384 ID = wx.NewId() 385 menu_cfg_episode.Append(ID, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.')) 386 wx.EVT_MENU(self, ID, self.__on_cfg_epi_ttl) 387 388 # -- submenu gnumed / master data 389 menu_master_data = wx.Menu() 390 menu_gnumed.AppendMenu(wx.NewId(), _('&Master data ...'), menu_master_data) 391 392 item = menu_master_data.Append(-1, _('Workplace profiles'), _('Manage the plugins to load per workplace.')) 393 self.Bind(wx.EVT_MENU, self.__on_configure_workplace, item) 394 395 menu_master_data.AppendSeparator() 396 397 item = menu_master_data.Append(-1, _('&Document types'), _('Manage the document types available in the system.')) 398 self.Bind(wx.EVT_MENU, self.__on_edit_doc_types, item) 399 400 item = menu_master_data.Append(-1, _('&Form templates'), _('Manage templates for forms and letters.')) 401 self.Bind(wx.EVT_MENU, self.__on_manage_form_templates, item) 402 403 item = menu_master_data.Append(-1, _('&Text expansions'), _('Manage keyword based text expansion macros.')) 404 self.Bind(wx.EVT_MENU, self.__on_manage_text_expansion, item) 405 406 menu_master_data.AppendSeparator() 407 408 item = menu_master_data.Append(-1, _('&Encounter types'), _('Manage encounter types.')) 409 self.Bind(wx.EVT_MENU, self.__on_manage_encounter_types, item) 410 411 item = menu_master_data.Append(-1, _('&Provinces'), _('Manage provinces (counties, territories, ...).')) 412 self.Bind(wx.EVT_MENU, self.__on_manage_provinces, item) 413 414 menu_master_data.AppendSeparator() 415 416 item = menu_master_data.Append(-1, _('Substances'), _('Manage substances in use.')) 417 self.Bind(wx.EVT_MENU, self.__on_manage_substances, item) 418 419 item = menu_master_data.Append(-1, _('Drugs'), _('Manage branded drugs.')) 420 self.Bind(wx.EVT_MENU, self.__on_manage_branded_drugs, item) 421 422 item = menu_master_data.Append(-1, _('Drug components'), _('Manage components of branded drugs.')) 423 self.Bind(wx.EVT_MENU, self.__on_manage_substances_in_brands, item) 424 425 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.')) 426 self.Bind(wx.EVT_MENU, self.__on_update_atc, item) 427 428 menu_master_data.AppendSeparator() 429 430 item = menu_master_data.Append(-1, _('Diagnostic orgs'), _('Manage diagnostic organisations (path labs etc).')) 431 self.Bind(wx.EVT_MENU, self.__on_manage_test_orgs, item) 432 433 item = menu_master_data.Append(-1, _('&Test types'), _('Manage test/measurement types.')) 434 self.Bind(wx.EVT_MENU, self.__on_manage_test_types, item) 435 436 item = menu_master_data.Append(-1, _('&Meta test types'), _('Show meta test/measurement types.')) 437 self.Bind(wx.EVT_MENU, self.__on_manage_meta_test_types, item) 438 439 item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.')) 440 self.Bind(wx.EVT_MENU, self.__on_update_loinc, item) 441 442 menu_master_data.AppendSeparator() 443 444 item = menu_master_data.Append(-1, _('Vaccines'), _('Show known vaccines.')) 445 self.Bind(wx.EVT_MENU, self.__on_manage_vaccines, item) 446 447 # -- submenu gnumed / users 448 menu_users = wx.Menu() 449 menu_gnumed.AppendMenu(wx.NewId(), _('&Users ...'), menu_users) 450 451 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user')) 452 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item) 453 454 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users')) 455 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item) 456 457 # -- 458 menu_gnumed.AppendSeparator() 459 460 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.')) 461 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item) 462 463 self.mainmenu.Append(menu_gnumed, '&GNUmed') 464 465 # -- menu "Person" --------------------------- 466 menu_patient = wx.Menu() 467 468 ID_CREATE_PATIENT = wx.NewId() 469 menu_patient.Append(ID_CREATE_PATIENT, _('Register person'), _("Register a new person with GNUmed")) 470 wx.EVT_MENU(self, ID_CREATE_PATIENT, self.__on_create_new_patient) 471 472 # item = menu_patient.Append(-1, _('Register new (old style)'), _("Register a new person with this practice")) 473 # self.Bind(wx.EVT_MENU, self.__on_create_patient, item) 474 475 ID_LOAD_EXT_PAT = wx.NewId() 476 menu_patient.Append(ID_LOAD_EXT_PAT, _('Load external'), _('Load and possibly create person from an external source.')) 477 wx.EVT_MENU(self, ID_LOAD_EXT_PAT, self.__on_load_external_patient) 478 479 ID_DEL_PAT = wx.NewId() 480 menu_patient.Append(ID_DEL_PAT, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.')) 481 wx.EVT_MENU(self, ID_DEL_PAT, self.__on_delete_patient) 482 483 item = menu_patient.Append(-1, _('&Merge persons'), _('Merge two persons into one.')) 484 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item) 485 486 menu_patient.AppendSeparator() 487 488 ID_ENLIST_PATIENT_AS_STAFF = wx.NewId() 489 menu_patient.Append(ID_ENLIST_PATIENT_AS_STAFF, _('Enlist as user'), _('Enlist current person as GNUmed user')) 490 wx.EVT_MENU(self, ID_ENLIST_PATIENT_AS_STAFF, self.__on_enlist_patient_as_staff) 491 492 # FIXME: temporary until external program framework is active 493 ID = wx.NewId() 494 menu_patient.Append(ID, _('Export to GDT'), _('Export demographics of currently active person into GDT file.')) 495 wx.EVT_MENU(self, ID, self.__on_export_as_gdt) 496 497 menu_patient.AppendSeparator() 498 499 self.mainmenu.Append(menu_patient, '&Person') 500 self.__gb['main.patientmenu'] = menu_patient 501 502 # -- menu "EMR" --------------------------- 503 menu_emr = wx.Menu() 504 self.mainmenu.Append(menu_emr, _("&EMR")) 505 self.__gb['main.emrmenu'] = menu_emr 506 507 # - submenu "show as" 508 menu_emr_show = wx.Menu() 509 menu_emr.AppendMenu(wx.NewId(), _('Show as ...'), menu_emr_show) 510 self.__gb['main.emr_showmenu'] = menu_emr_show 511 512 # - summary 513 item = menu_emr_show.Append(-1, _('Summary'), _('Show a high-level summary of the EMR.')) 514 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item) 515 516 # - search 517 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient')) 518 self.Bind(wx.EVT_MENU, self.__on_search_emr, item) 519 520 item = menu_emr.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients')) 521 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item) 522 523 # -- submenu EMR / Add, Edit 524 menu_emr_edit = wx.Menu() 525 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit) 526 527 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')) 528 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item) 529 530 item = menu_emr_edit.Append(-1, _('&Medication'), _('Add medication / substance use entry.')) 531 self.Bind(wx.EVT_MENU, self.__on_add_medication, item) 532 533 item = menu_emr_edit.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.')) 534 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item) 535 536 item = menu_emr_edit.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.')) 537 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item) 538 539 item = menu_emr_edit.Append(-1, _('&Hospital stays'), _('Manage hospital stays.')) 540 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item) 541 542 item = menu_emr_edit.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.')) 543 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item) 544 545 item = menu_emr_edit.Append(-1, _('&Measurement(s)'), _('Add (a) measurement result(s) for the current patient.')) 546 self.Bind(wx.EVT_MENU, self.__on_add_measurement, item) 547 548 # item = menu_emr_edit.Append(-1, ) 549 # self.Bind(wx.EVT_MENU, , item) 550 551 # -- EMR, again 552 553 # # - start new encounter 554 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.')) 555 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item) 556 557 # - list encounters 558 item = menu_emr.Append(-1, _('&Encounters list'), _('List all encounters including empty ones.')) 559 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item) 560 561 # - submenu GNUmed / "export as" 562 menu_emr.AppendSeparator() 563 564 menu_emr_export = wx.Menu() 565 menu_emr.AppendMenu(wx.NewId(), _('Export as ...'), menu_emr_export) 566 # 1) ASCII 567 ID_EXPORT_EMR_ASCII = wx.NewId() 568 menu_emr_export.Append ( 569 ID_EXPORT_EMR_ASCII, 570 _('Text document'), 571 _("Export the EMR of the active patient into a text file") 572 ) 573 wx.EVT_MENU(self, ID_EXPORT_EMR_ASCII, self.OnExportEMR) 574 # 2) journal format 575 ID_EXPORT_EMR_JOURNAL = wx.NewId() 576 menu_emr_export.Append ( 577 ID_EXPORT_EMR_JOURNAL, 578 _('Journal'), 579 _("Export the EMR of the active patient as a chronological journal into a text file") 580 ) 581 wx.EVT_MENU(self, ID_EXPORT_EMR_JOURNAL, self.__on_export_emr_as_journal) 582 # 3) Medistar import format 583 ID_EXPORT_MEDISTAR = wx.NewId() 584 menu_emr_export.Append ( 585 ID_EXPORT_MEDISTAR, 586 _('MEDISTAR import format'), 587 _("GNUmed -> MEDISTAR. Export progress notes of active patient's active encounter into a text file.") 588 ) 589 wx.EVT_MENU(self, ID_EXPORT_MEDISTAR, self.__on_export_for_medistar) 590 591 # - draw a line 592 menu_emr.AppendSeparator() 593 594 # -- menu "paperwork" --------------------- 595 menu_paperwork = wx.Menu() 596 597 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.')) 598 self.Bind(wx.EVT_MENU, self.__on_new_letter, item) 599 600 self.mainmenu.Append(menu_paperwork, _('&Correspondence')) 601 602 # menu "Tools" --------------------------- 603 self.menu_tools = wx.Menu() 604 self.__gb['main.toolsmenu'] = self.menu_tools 605 self.mainmenu.Append(self.menu_tools, _("&Tools")) 606 607 ID_DICOM_VIEWER = wx.NewId() 608 viewer = _('no viewer installed') 609 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 610 viewer = u'OsiriX' 611 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]: 612 viewer = u'Aeskulap' 613 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]: 614 viewer = u'AMIDE' 615 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]: 616 viewer = u'(x)medcon' 617 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) 618 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_dicom_viewer) 619 if viewer == _('no viewer installed'): 620 _log.info('neither of OsiriX / Aeskulap / AMIDE / xmedcon found, disabling "DICOM viewer" menu item') 621 self.menu_tools.Enable(id=ID_DICOM_VIEWER, enable=False) 622 623 # ID_DERMTOOL = wx.NewId() 624 # self.menu_tools.Append(ID_DERMTOOL, _("Dermatology"), _("A tool to aid dermatology diagnosis")) 625 # wx.EVT_MENU (self, ID_DERMTOOL, self.__dermtool) 626 627 ID = wx.NewId() 628 self.menu_tools.Append(ID, _('Snellen chart'), _('Display fullscreen snellen chart.')) 629 wx.EVT_MENU(self, ID, self.__on_snellen) 630 631 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.')) 632 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item) 633 634 self.menu_tools.AppendSeparator() 635 636 # menu "Knowledge" --------------------- 637 menu_knowledge = wx.Menu() 638 self.__gb['main.knowledgemenu'] = menu_knowledge 639 self.mainmenu.Append(menu_knowledge, _('&Knowledge')) 640 641 menu_drug_dbs = wx.Menu() 642 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs) 643 644 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.')) 645 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item) 646 647 # # - IFAP drug DB 648 # ID_IFAP = wx.NewId() 649 # menu_drug_dbs.Append(ID_IFAP, u'ifap', _('Start "ifap index PRAXIS" %s drug browser (Windows/Wine, Germany)') % gmTools.u_registered_trademark) 650 # wx.EVT_MENU(self, ID_IFAP, self.__on_ifap) 651 652 menu_id = wx.NewId() 653 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)')) 654 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch) 655 656 # menu_knowledge.AppendSeparator() 657 658 # - "recommended" medical links in the Wiki 659 ID_MEDICAL_LINKS = wx.NewId() 660 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.')) 661 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links) 662 663 # -- menu "Office" -------------------- 664 self.menu_office = wx.Menu() 665 666 self.__gb['main.officemenu'] = self.menu_office 667 self.mainmenu.Append(self.menu_office, _('&Office')) 668 669 # -- menu "Help" -------------- 670 help_menu = wx.Menu() 671 672 ID = wx.NewId() 673 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.')) 674 wx.EVT_MENU(self, ID, self.__on_display_wiki) 675 676 ID = wx.NewId() 677 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.')) 678 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online) 679 680 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.')) 681 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item) 682 683 menu_debugging = wx.Menu() 684 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging) 685 686 ID_SCREENSHOT = wx.NewId() 687 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.')) 688 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot) 689 690 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.')) 691 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item) 692 693 ID = wx.NewId() 694 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.')) 695 wx.EVT_MENU(self, ID, self.__on_backup_log_file) 696 697 ID = wx.NewId() 698 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.')) 699 wx.EVT_MENU(self, ID, self.__on_display_bugtracker) 700 701 ID_UNBLOCK = wx.NewId() 702 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.')) 703 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor) 704 705 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.')) 706 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item) 707 708 # item = menu_debugging.Append(-1, _('Reload hook script'), _('Reload hook script from hard drive.')) 709 # self.Bind(wx.EVT_MENU, self.__on_reload_hook_script, item) 710 711 if _cfg.get(option = 'debug'): 712 ID_TOGGLE_PAT_LOCK = wx.NewId() 713 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient'), _('Lock/unlock patient - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !')) 714 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock) 715 716 ID_TEST_EXCEPTION = wx.NewId() 717 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.')) 718 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception) 719 720 ID = wx.NewId() 721 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).')) 722 wx.EVT_MENU(self, ID, self.__on_invoke_inspector) 723 try: 724 import wx.lib.inspection 725 except ImportError: 726 menu_debugging.Enable(id = ID, enable = False) 727 728 help_menu.AppendSeparator() 729 730 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "") 731 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout) 732 733 ID_CONTRIBUTORS = wx.NewId() 734 help_menu.Append(ID_CONTRIBUTORS, _('GNUmed contributors'), _('show GNUmed contributors')) 735 wx.EVT_MENU(self, ID_CONTRIBUTORS, self.__on_show_contributors) 736 737 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.')) 738 self.Bind(wx.EVT_MENU, self.__on_about_database, item) 739 740 help_menu.AppendSeparator() 741 742 # among other things the Manual is added from a plugin 743 self.__gb['main.helpmenu'] = help_menu 744 self.mainmenu.Append(help_menu, _("&Help")) 745 746 747 # and activate menu structure 748 self.SetMenuBar(self.mainmenu)
749 #----------------------------------------------
750 - def __load_plugins(self):
751 pass
752 #---------------------------------------------- 753 # event handling 754 #----------------------------------------------
755 - def __register_events(self):
756 """register events we want to react to""" 757 758 wx.EVT_CLOSE(self, self.OnClose) 759 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session) 760 wx.EVT_END_SESSION(self, self._on_end_session) 761 762 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection) 763 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_pat_name_changed) 764 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_pat_name_changed) 765 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext) 766 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention) 767 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning) 768 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback) 769 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded) 770 771 wx.lib.pubsub.Publisher().subscribe(listener = self._on_set_statustext_pubsub, topic = 'statustext') 772 773 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
774 #----------------------------------------------
775 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
776 777 _log.debug('registering plugin with menu system') 778 _log.debug(' generic name: %s', plugin_name) 779 _log.debug(' class name: %s', class_name) 780 _log.debug(' specific menu: %s', menu_name) 781 _log.debug(' menu item: %s', menu_item_name) 782 783 # add to generic "go to plugin" menu 784 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name) 785 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 786 self.menu_id2plugin[item.Id] = class_name 787 788 # add to specific menu if so requested 789 if menu_name is not None: 790 menu = self.__gb['main.%smenu' % menu_name] 791 item = menu.Append(-1, menu_item_name, menu_help_string) 792 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 793 self.menu_id2plugin[item.Id] = class_name 794 795 return True
796 #----------------------------------------------
797 - def __on_raise_a_plugin(self, evt):
798 gmDispatcher.send ( 799 signal = u'display_widget', 800 name = self.menu_id2plugin[evt.Id] 801 )
802 #----------------------------------------------
803 - def _on_query_end_session(self, *args, **kwargs):
804 wx.Bell() 805 wx.Bell() 806 wx.Bell() 807 _log.warning('unhandled event detected: QUERY_END_SESSION') 808 _log.info('we should be saving ourselves from here') 809 gmLog2.flush() 810 print "unhandled event detected: QUERY_END_SESSION"
811 #----------------------------------------------
812 - def _on_end_session(self, *args, **kwargs):
813 wx.Bell() 814 wx.Bell() 815 wx.Bell() 816 _log.warning('unhandled event detected: END_SESSION') 817 gmLog2.flush() 818 print "unhandled event detected: END_SESSION"
819 #-----------------------------------------------
820 - def _register_pre_exit_callback(self, callback=None):
821 if not callable(callback): 822 raise TypeError(u'callback [%s] not callable' % callback) 823 824 self.__pre_exit_callbacks.append(callback)
825 #-----------------------------------------------
826 - def _on_set_statustext_pubsub(self, context=None):
827 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg']) 828 wx.CallAfter(self.SetStatusText, msg) 829 830 try: 831 if context.data['beep']: 832 wx.Bell() 833 except KeyError: 834 pass
835 #-----------------------------------------------
836 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
837 838 if msg is None: 839 msg = _('programmer forgot to specify status message') 840 841 if loglevel is not None: 842 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' ')) 843 844 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg) 845 wx.CallAfter(self.SetStatusText, msg) 846 847 if beep: 848 wx.Bell()
849 #-----------------------------------------------
850 - def _on_db_maintenance_warning(self):
851 wx.CallAfter(self.__on_db_maintenance_warning)
852 #-----------------------------------------------
854 855 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.')) 856 wx.Bell() 857 if not wx.GetApp().IsActive(): 858 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 859 860 gmHooks.run_hook_script(hook = u'db_maintenance_warning') 861 862 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 863 None, 864 -1, 865 caption = _('Database shutdown warning'), 866 question = _( 867 'The database will be shut down for maintenance\n' 868 'in a few minutes.\n' 869 '\n' 870 'In order to not suffer any loss of data you\n' 871 'will need to save your current work and log\n' 872 'out of this GNUmed client.\n' 873 ), 874 button_defs = [ 875 { 876 u'label': _('Close now'), 877 u'tooltip': _('Close this GNUmed client immediately.'), 878 u'default': False 879 }, 880 { 881 u'label': _('Finish work'), 882 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'), 883 u'default': True 884 } 885 ] 886 ) 887 decision = dlg.ShowModal() 888 if decision == wx.ID_YES: 889 top_win = wx.GetApp().GetTopWindow() 890 wx.CallAfter(top_win.Close)
891 #-----------------------------------------------
892 - def _on_request_user_attention(self, msg=None, urgent=False):
893 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
894 #-----------------------------------------------
895 - def __on_request_user_attention(self, msg=None, urgent=False):
896 # already in the foreground ? 897 if not wx.GetApp().IsActive(): 898 if urgent: 899 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 900 else: 901 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO) 902 903 if msg is not None: 904 self.SetStatusText(msg) 905 906 if urgent: 907 wx.Bell() 908 909 gmHooks.run_hook_script(hook = u'request_user_attention')
910 #-----------------------------------------------
911 - def _on_pat_name_changed(self):
912 wx.CallAfter(self.__on_pat_name_changed)
913 #-----------------------------------------------
914 - def __on_pat_name_changed(self):
915 self.__update_window_title()
916 #-----------------------------------------------
917 - def _on_post_patient_selection(self, **kwargs):
918 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
919 #----------------------------------------------
920 - def __on_post_patient_selection(self, **kwargs):
921 self.__update_window_title() 922 try: 923 gmHooks.run_hook_script(hook = u'post_patient_activation') 924 except: 925 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.')) 926 raise
927 #----------------------------------------------
928 - def _pre_selection_callback(self):
929 return self.__sanity_check_encounter()
930 #----------------------------------------------
931 - def __sanity_check_encounter(self):
932 933 dbcfg = gmCfg.cCfgSQL() 934 check_enc = bool(dbcfg.get2 ( 935 option = 'encounter.show_editor_before_patient_change', 936 workplace = gmSurgery.gmCurrentPractice().active_workplace, 937 bias = 'user', 938 default = True # True: if needed, not always unconditionally 939 )) 940 941 if not check_enc: 942 return True 943 944 pat = gmPerson.gmCurrentPatient() 945 emr = pat.get_emr() 946 enc = emr.active_encounter 947 948 # did we add anything to the EMR ? 949 has_narr = enc.has_narrative() 950 has_docs = enc.has_documents() 951 952 if (not has_narr) and (not has_docs): 953 return True 954 955 empty_aoe = (gmTools.coalesce(enc['assessment_of_encounter'], '').strip() == u'') 956 zero_duration = (enc['last_affirmed'] == enc['started']) 957 958 # all is well anyway 959 if (not empty_aoe) and (not zero_duration): 960 return True 961 962 if zero_duration: 963 enc['last_affirmed'] = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) 964 965 # no narrative, presumably only import of docs and done 966 if not has_narr: 967 if empty_aoe: 968 enc['assessment_of_encounter'] = _('only documents added') 969 enc['pk_type'] = gmEMRStructItems.get_encounter_type(description = 'chart review')[0]['pk'] 970 # "last_affirmed" should be latest modified_at of relevant docs but that's a lot more involved 971 enc.save_payload() 972 return True 973 974 # does have narrative 975 if empty_aoe: 976 # - work out suitable default 977 epis = emr.get_episodes_by_encounter() 978 if len(epis) > 0: 979 enc_summary = '' 980 for epi in epis: 981 enc_summary += '%s; ' % epi['description'] 982 enc['assessment_of_encounter'] = enc_summary 983 984 dlg = gmEMRStructWidgets.cEncounterEditAreaDlg(parent = self, encounter = enc) 985 dlg.ShowModal() 986 987 return True
988 #---------------------------------------------- 989 # menu "paperwork" 990 #----------------------------------------------
991 - def __on_show_docs(self, evt):
992 gmDispatcher.send(signal='show_document_viewer')
993 #----------------------------------------------
994 - def __on_new_letter(self, evt):
995 pat = gmPerson.gmCurrentPatient() 996 if not pat.connected: 997 gmDispatcher.send(signal = 'statustext', msg = _('Cannot write letter. No active patient.'), beep = True) 998 return True 999 #gmFormWidgets.create_new_letter(parent = self) 1000 gmFormWidgets.print_doc_from_template(parent = self, keep_a_copy = True, cleanup = _cfg.get(option = 'debug'))
1001 #----------------------------------------------
1002 - def __on_manage_form_templates(self, evt):
1004 #---------------------------------------------- 1005 # help menu 1006 #----------------------------------------------
1007 - def OnAbout(self, event):
1008 from Gnumed.wxpython import gmAbout 1009 gmAbout = gmAbout.AboutFrame ( 1010 self, 1011 -1, 1012 _("About GNUmed"), 1013 size=wx.Size(350, 300), 1014 style = wx.MAXIMIZE_BOX, 1015 version = _cfg.get(option = 'client_version') 1016 ) 1017 gmAbout.Centre(wx.BOTH) 1018 gmTopLevelFrame.otherWin = gmAbout 1019 gmAbout.Show(True) 1020 del gmAbout
1021 #----------------------------------------------
1022 - def __on_about_database(self, evt):
1023 praxis = gmSurgery.gmCurrentPractice() 1024 msg = praxis.db_logon_banner 1025 1026 login = gmPG2.get_default_login() 1027 1028 auth = _( 1029 '\n\n' 1030 ' workplace: %s\n' 1031 ' account: %s\n' 1032 ' database: %s\n' 1033 ' server: %s\n' 1034 ) % ( 1035 praxis.active_workplace, 1036 login.user, 1037 login.database, 1038 gmTools.coalesce(login.host, u'<localhost>') 1039 ) 1040 1041 msg += auth 1042 1043 gmGuiHelpers.gm_show_info(msg, _('About database and server'))
1044 #----------------------------------------------
1045 - def __on_show_contributors(self, event):
1046 from Gnumed.wxpython import gmAbout 1047 contribs = gmAbout.cContributorsDlg ( 1048 parent = self, 1049 id = -1, 1050 title = _('GNUmed contributors'), 1051 size = wx.Size(400,600), 1052 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER 1053 ) 1054 contribs.ShowModal() 1055 del contribs 1056 del gmAbout
1057 #---------------------------------------------- 1058 # GNUmed menu 1059 #----------------------------------------------
1060 - def __on_exit_gnumed(self, event):
1061 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler).""" 1062 _log.debug('gmTopLevelFrame._on_exit_gnumed() start') 1063 self.Close(True) # -> calls wx.EVT_CLOSE handler 1064 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1065 #----------------------------------------------
1066 - def __on_check_for_updates(self, evt):
1068 #----------------------------------------------
1069 - def __on_announce_maintenance(self, evt):
1070 send = gmGuiHelpers.gm_show_question ( 1071 _('This will send a notification about database downtime\n' 1072 'to all GNUmed clients connected to your database.\n' 1073 '\n' 1074 'Do you want to send the notification ?\n' 1075 ), 1076 _('Announcing database maintenance downtime') 1077 ) 1078 if not send: 1079 return 1080 gmPG2.send_maintenance_notification()
1081 #---------------------------------------------- 1082 #----------------------------------------------
1083 - def __on_list_configuration(self, evt):
1084 gmCfgWidgets.list_configuration(parent = self)
1085 #---------------------------------------------- 1086 # submenu GNUmed / options / client 1087 #----------------------------------------------
1088 - def __on_configure_temp_dir(self, evt):
1089 1090 cfg = gmCfg.cCfgSQL() 1091 1092 tmp_dir = gmTools.coalesce ( 1093 cfg.get2 ( 1094 option = "horstspace.tmp_dir", 1095 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1096 bias = 'workplace' 1097 ), 1098 os.path.expanduser(os.path.join('~', '.gnumed', 'tmp')) 1099 ) 1100 1101 dlg = wx.DirDialog ( 1102 parent = self, 1103 message = _('Choose temporary directory ...'), 1104 defaultPath = tmp_dir, 1105 style = wx.DD_DEFAULT_STYLE 1106 ) 1107 result = dlg.ShowModal() 1108 tmp_dir = dlg.GetPath() 1109 dlg.Destroy() 1110 1111 if result != wx.ID_OK: 1112 return 1113 1114 cfg.set ( 1115 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1116 option = "horstspace.tmp_dir", 1117 value = tmp_dir 1118 )
1119 #----------------------------------------------
1120 - def __on_configure_export_chunk_size(self, evt):
1121 1122 def is_valid(value): 1123 try: 1124 i = int(value) 1125 except: 1126 return False, value 1127 if i < 0: 1128 return False, value 1129 if i > (1024 * 1024 * 1024 * 10): # 10 GB 1130 return False, value 1131 return True, i
1132 1133 gmCfgWidgets.configure_string_option ( 1134 message = _( 1135 'Some network installations cannot cope with loading\n' 1136 'documents of arbitrary size in one piece from the\n' 1137 'database (mainly observed on older Windows versions)\n.' 1138 '\n' 1139 'Under such circumstances documents need to be retrieved\n' 1140 'in chunks and reassembled on the client.\n' 1141 '\n' 1142 'Here you can set the size (in Bytes) above which\n' 1143 'GNUmed will retrieve documents in chunks. Setting this\n' 1144 'value to 0 will disable the chunking protocol.' 1145 ), 1146 option = 'horstspace.blob_export_chunk_size', 1147 bias = 'workplace', 1148 default_value = 1024 * 1024, 1149 validator = is_valid 1150 )
1151 #---------------------------------------------- 1152 # submenu GNUmed / database 1153 #----------------------------------------------
1154 - def __on_configure_db_lang(self, event):
1155 1156 langs = gmPG2.get_translation_languages() 1157 1158 for lang in [ 1159 gmI18N.system_locale_level['language'], 1160 gmI18N.system_locale_level['country'], 1161 gmI18N.system_locale_level['full'] 1162 ]: 1163 if lang not in langs: 1164 langs.append(lang) 1165 1166 selected_lang = gmPG2.get_current_user_language() 1167 try: 1168 selections = [langs.index(selected_lang)] 1169 except ValueError: 1170 selections = None 1171 1172 language = gmListWidgets.get_choices_from_list ( 1173 parent = self, 1174 msg = _( 1175 'Please select your database language from the list below.\n' 1176 '\n' 1177 'Your current setting is [%s].\n' 1178 '\n' 1179 'This setting will not affect the language the user interface\n' 1180 'is displayed in but rather that of the metadata returned\n' 1181 'from the database such as encounter types, document types,\n' 1182 'and EMR formatting.\n' 1183 '\n' 1184 'To switch back to the default English language unselect all\n' 1185 'pre-selected languages from the list below.' 1186 ) % gmTools.coalesce(selected_lang, _('not configured')), 1187 caption = _('Configuring database language'), 1188 choices = langs, 1189 selections = selections, 1190 columns = [_('Language')], 1191 data = langs, 1192 single_selection = True, 1193 can_return_empty = True 1194 ) 1195 1196 if language is None: 1197 return 1198 1199 if language == []: 1200 language = None 1201 1202 try: 1203 _provider.get_staff().database_language = language 1204 return 1205 except ValueError: 1206 pass 1207 1208 force_language = gmGuiHelpers.gm_show_question ( 1209 _('The database currently holds no translations for\n' 1210 'language [%s]. However, you can add translations\n' 1211 'for things like document or encounter types yourself.\n' 1212 '\n' 1213 'Do you want to force the language setting to [%s] ?' 1214 ) % (language, language), 1215 _('Configuring database language') 1216 ) 1217 if not force_language: 1218 return 1219 1220 gmPG2.force_user_language(language = language)
1221 #----------------------------------------------
1222 - def __on_configure_db_welcome(self, event):
1223 dlg = gmGuiHelpers.cGreetingEditorDlg(self, -1) 1224 dlg.ShowModal()
1225 #---------------------------------------------- 1226 # submenu GNUmed - config - external tools 1227 #----------------------------------------------
1228 - def __on_configure_ooo_settle_time(self, event):
1229 1230 def is_valid(value): 1231 try: 1232 float(value) 1233 return True, value 1234 except: 1235 return False, value
1236 1237 gmCfgWidgets.configure_string_option ( 1238 message = _( 1239 'When GNUmed cannot find an OpenOffice server it\n' 1240 'will try to start one. OpenOffice, however, needs\n' 1241 'some time to fully start up.\n' 1242 '\n' 1243 'Here you can set the time for GNUmed to wait for OOo.\n' 1244 ), 1245 option = 'external.ooo.startup_settle_time', 1246 bias = 'workplace', 1247 default_value = 2.0, 1248 validator = is_valid 1249 ) 1250 #----------------------------------------------
1251 - def __on_configure_drug_data_source(self, evt):
1252 gmMedicationWidgets.configure_drug_data_source(parent = self)
1253 #----------------------------------------------
1254 - def __on_configure_measurements_url(self, evt):
1255 1256 def is_valid(value): 1257 value = value.strip() 1258 if value == u'': 1259 return True, value 1260 try: 1261 urllib2.urlopen(value) 1262 return True, value 1263 except: 1264 return False, value
1265 1266 gmCfgWidgets.configure_string_option ( 1267 message = _( 1268 'GNUmed will use this URL to access an encyclopedia of\n' 1269 'measurement/lab methods from within the measurments grid.\n' 1270 '\n' 1271 'You can leave this empty but to set it to a specific\n' 1272 'address the URL must be accessible now.' 1273 ), 1274 option = 'external.urls.measurements_encyclopedia', 1275 bias = 'user', 1276 default_value = u'http://www.laborlexikon.de', 1277 validator = is_valid 1278 ) 1279 #----------------------------------------------
1280 - def __on_configure_acs_risk_calculator_cmd(self, event):
1281 1282 def is_valid(value): 1283 found, binary = gmShellAPI.detect_external_binary(value) 1284 if not found: 1285 gmDispatcher.send ( 1286 signal = 'statustext', 1287 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1288 beep = True 1289 ) 1290 return False, value 1291 return True, binary
1292 1293 gmCfgWidgets.configure_string_option ( 1294 message = _( 1295 'Enter the shell command with which to start the\n' 1296 'the ACS risk assessment calculator.\n' 1297 '\n' 1298 'GNUmed will try to verify the path which may,\n' 1299 'however, fail if you are using an emulator such\n' 1300 'as Wine. Nevertheless, starting the calculator\n' 1301 'will work as long as the shell command is correct\n' 1302 'despite the failing test.' 1303 ), 1304 option = 'external.tools.acs_risk_calculator_cmd', 1305 bias = 'user', 1306 validator = is_valid 1307 ) 1308 #----------------------------------------------
1309 - def __on_configure_visual_soap_cmd(self, event):
1310 gmNarrativeWidgets.configure_visual_progress_note_editor()
1311 #----------------------------------------------
1312 - def __on_configure_freediams_cmd(self, event):
1313 1314 def is_valid(value): 1315 found, binary = gmShellAPI.detect_external_binary(value) 1316 if not found: 1317 gmDispatcher.send ( 1318 signal = 'statustext', 1319 msg = _('The command [%s] is not found.') % value, 1320 beep = True 1321 ) 1322 return False, value 1323 return True, binary
1324 #------------------------------------------ 1325 gmCfgWidgets.configure_string_option ( 1326 message = _( 1327 'Enter the shell command with which to start\n' 1328 'the FreeDiams drug database frontend.\n' 1329 '\n' 1330 'GNUmed will try to verify that path.' 1331 ), 1332 option = 'external.tools.freediams_cmd', 1333 bias = 'workplace', 1334 default_value = None, 1335 validator = is_valid 1336 ) 1337 #----------------------------------------------
1338 - def __on_configure_ifap_cmd(self, event):
1339 1340 def is_valid(value): 1341 found, binary = gmShellAPI.detect_external_binary(value) 1342 if not found: 1343 gmDispatcher.send ( 1344 signal = 'statustext', 1345 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1346 beep = True 1347 ) 1348 return False, value 1349 return True, binary
1350 1351 gmCfgWidgets.configure_string_option ( 1352 message = _( 1353 'Enter the shell command with which to start the\n' 1354 'the IFAP drug database.\n' 1355 '\n' 1356 'GNUmed will try to verify the path which may,\n' 1357 'however, fail if you are using an emulator such\n' 1358 'as Wine. Nevertheless, starting IFAP will work\n' 1359 'as long as the shell command is correct despite\n' 1360 'the failing test.' 1361 ), 1362 option = 'external.ifap-win.shell_command', 1363 bias = 'workplace', 1364 default_value = 'C:\Ifapwin\WIAMDB.EXE', 1365 validator = is_valid 1366 ) 1367 #---------------------------------------------- 1368 # submenu GNUmed / config / ui 1369 #----------------------------------------------
1370 - def __on_configure_startup_plugin(self, evt):
1371 1372 dbcfg = gmCfg.cCfgSQL() 1373 # get list of possible plugins 1374 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1375 option = u'horstspace.notebook.plugin_load_order', 1376 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1377 bias = 'user' 1378 ), []) 1379 1380 # get current setting 1381 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1382 option = u'horstspace.plugin_to_raise_after_startup', 1383 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1384 bias = 'user' 1385 ), u'gmEMRBrowserPlugin') 1386 try: 1387 selections = [plugin_list.index(initial_plugin)] 1388 except ValueError: 1389 selections = None 1390 1391 # now let user decide 1392 plugin = gmListWidgets.get_choices_from_list ( 1393 parent = self, 1394 msg = _( 1395 'Here you can choose which plugin you want\n' 1396 'GNUmed to display after initial startup.\n' 1397 '\n' 1398 'Note that the plugin must not require any\n' 1399 'patient to be activated.\n' 1400 '\n' 1401 'Select the desired plugin below:' 1402 ), 1403 caption = _('Configuration'), 1404 choices = plugin_list, 1405 selections = selections, 1406 columns = [_('GNUmed Plugin')], 1407 single_selection = True 1408 ) 1409 1410 if plugin is None: 1411 return 1412 1413 dbcfg.set ( 1414 option = u'patient_search.plugin_to_raise_after_startup', 1415 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1416 value = plugin 1417 )
1418 #---------------------------------------------- 1419 # submenu GNUmed / config / ui / patient search 1420 #----------------------------------------------
1421 - def __on_configure_quick_pat_search(self, evt):
1422 gmCfgWidgets.configure_boolean_option ( 1423 parent = self, 1424 question = _( 1425 'If there is only one external patient\n' 1426 'source available do you want GNUmed\n' 1427 'to immediately go ahead and search for\n' 1428 'matching patient records ?\n\n' 1429 'If not GNUmed will let you confirm the source.' 1430 ), 1431 option = 'patient_search.external_sources.immediately_search_if_single_source', 1432 button_tooltips = [ 1433 _('Yes, search for matches immediately.'), 1434 _('No, let me confirm the external patient first.') 1435 ] 1436 )
1437 #----------------------------------------------
1438 - def __on_cfg_default_region(self, evt):
1439 gmDemographicsWidgets.configure_default_region()
1440 #----------------------------------------------
1441 - def __on_cfg_default_country(self, evt):
1442 gmDemographicsWidgets.configure_default_country()
1443 #----------------------------------------------
1444 - def __on_configure_dob_reminder_proximity(self, evt):
1445 1446 def is_valid(value): 1447 return gmPG2.is_pg_interval(candidate=value), value
1448 1449 gmCfgWidgets.configure_string_option ( 1450 message = _( 1451 'When a patient is activated GNUmed checks the\n' 1452 "proximity of the patient's birthday.\n" 1453 '\n' 1454 'If the birthday falls within the range of\n' 1455 ' "today %s <the interval you set here>"\n' 1456 'GNUmed will remind you of the recent or\n' 1457 'imminent anniversary.' 1458 ) % u'\u2213', 1459 option = u'patient_search.dob_warn_interval', 1460 bias = 'user', 1461 default_value = '1 week', 1462 validator = is_valid 1463 ) 1464 #----------------------------------------------
1465 - def __on_allow_multiple_new_episodes(self, evt):
1466 1467 gmCfgWidgets.configure_boolean_option ( 1468 parent = self, 1469 question = _( 1470 'When adding progress notes do you want to\n' 1471 'allow opening several unassociated, new\n' 1472 'episodes for a patient at once ?\n' 1473 '\n' 1474 'This can be particularly helpful when entering\n' 1475 'progress notes on entirely new patients presenting\n' 1476 'with a multitude of problems on their first visit.' 1477 ), 1478 option = u'horstspace.soap_editor.allow_same_episode_multiple_times', 1479 button_tooltips = [ 1480 _('Yes, allow for multiple new episodes concurrently.'), 1481 _('No, only allow editing one new episode at a time.') 1482 ] 1483 )
1484 #----------------------------------------------
1485 - def __on_configure_initial_pat_plugin(self, evt):
1486 1487 dbcfg = gmCfg.cCfgSQL() 1488 # get list of possible plugins 1489 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1490 option = u'horstspace.notebook.plugin_load_order', 1491 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1492 bias = 'user' 1493 ), []) 1494 1495 # get current setting 1496 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1497 option = u'patient_search.plugin_to_raise_after_search', 1498 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1499 bias = 'user' 1500 ), u'gmEMRBrowserPlugin') 1501 try: 1502 selections = [plugin_list.index(initial_plugin)] 1503 except ValueError: 1504 selections = None 1505 1506 # now let user decide 1507 plugin = gmListWidgets.get_choices_from_list ( 1508 parent = self, 1509 msg = _( 1510 'When a patient is activated GNUmed can\n' 1511 'be told to switch to a specific plugin.\n' 1512 '\n' 1513 'Select the desired plugin below:' 1514 ), 1515 caption = _('Configuration'), 1516 choices = plugin_list, 1517 selections = selections, 1518 columns = [_('GNUmed Plugin')], 1519 single_selection = True 1520 ) 1521 1522 if plugin is None: 1523 return 1524 1525 dbcfg.set ( 1526 option = u'patient_search.plugin_to_raise_after_search', 1527 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1528 value = plugin 1529 )
1530 #---------------------------------------------- 1531 # submenu GNUmed / config / encounter 1532 #----------------------------------------------
1533 - def __on_cfg_medication_list_template(self, evt):
1534 gmMedicationWidgets.configure_medication_list_template(parent = self)
1535 #----------------------------------------------
1536 - def __on_cfg_enc_default_type(self, evt):
1537 enc_types = gmEMRStructItems.get_encounter_types() 1538 1539 gmCfgWidgets.configure_string_from_list_option ( 1540 parent = self, 1541 message = _('Select the default type for new encounters.\n'), 1542 option = 'encounter.default_type', 1543 bias = 'user', 1544 default_value = u'in surgery', 1545 choices = [ e[0] for e in enc_types ], 1546 columns = [_('Encounter type')], 1547 data = [ e[1] for e in enc_types ] 1548 )
1549 #----------------------------------------------
1550 - def __on_cfg_enc_pat_change(self, event):
1551 gmCfgWidgets.configure_boolean_option ( 1552 parent = self, 1553 question = _( 1554 'Do you want GNUmed to show the encounter\n' 1555 'details editor when changing the active patient ?' 1556 ), 1557 option = 'encounter.show_editor_before_patient_change', 1558 button_tooltips = [ 1559 _('Yes, show the encounter editor if it seems appropriate.'), 1560 _('No, never show the encounter editor even if it would seem useful.') 1561 ] 1562 )
1563 #----------------------------------------------
1564 - def __on_cfg_enc_empty_ttl(self, evt):
1565 1566 def is_valid(value): 1567 return gmPG2.is_pg_interval(candidate=value), value
1568 1569 gmCfgWidgets.configure_string_option ( 1570 message = _( 1571 'When a patient is activated GNUmed checks the\n' 1572 'chart for encounters lacking any entries.\n' 1573 '\n' 1574 'Any such encounters older than what you set\n' 1575 'here will be removed from the medical record.\n' 1576 '\n' 1577 'To effectively disable removal of such encounters\n' 1578 'set this option to an improbable value.\n' 1579 ), 1580 option = 'encounter.ttl_if_empty', 1581 bias = 'user', 1582 default_value = '1 week', 1583 validator = is_valid 1584 ) 1585 #----------------------------------------------
1586 - def __on_cfg_enc_min_ttl(self, evt):
1587 1588 def is_valid(value): 1589 return gmPG2.is_pg_interval(candidate=value), value
1590 1591 gmCfgWidgets.configure_string_option ( 1592 message = _( 1593 'When a patient is activated GNUmed checks the\n' 1594 'age of the most recent encounter.\n' 1595 '\n' 1596 'If that encounter is younger than this age\n' 1597 'the existing encounter will be continued.\n' 1598 '\n' 1599 '(If it is really old a new encounter is\n' 1600 ' started, or else GNUmed will ask you.)\n' 1601 ), 1602 option = 'encounter.minimum_ttl', 1603 bias = 'user', 1604 default_value = '1 hour 30 minutes', 1605 validator = is_valid 1606 ) 1607 #----------------------------------------------
1608 - def __on_cfg_enc_max_ttl(self, evt):
1609 1610 def is_valid(value): 1611 return gmPG2.is_pg_interval(candidate=value), value
1612 1613 gmCfgWidgets.configure_string_option ( 1614 message = _( 1615 'When a patient is activated GNUmed checks the\n' 1616 'age of the most recent encounter.\n' 1617 '\n' 1618 'If that encounter is older than this age\n' 1619 'GNUmed will always start a new encounter.\n' 1620 '\n' 1621 '(If it is very recent the existing encounter\n' 1622 ' is continued, or else GNUmed will ask you.)\n' 1623 ), 1624 option = 'encounter.maximum_ttl', 1625 bias = 'user', 1626 default_value = '6 hours', 1627 validator = is_valid 1628 ) 1629 #----------------------------------------------
1630 - def __on_cfg_epi_ttl(self, evt):
1631 1632 def is_valid(value): 1633 try: 1634 value = int(value) 1635 except: 1636 return False, value 1637 return gmPG2.is_pg_interval(candidate=value), value
1638 1639 gmCfgWidgets.configure_string_option ( 1640 message = _( 1641 'At any time there can only be one open (ongoing)\n' 1642 'episode for each health issue.\n' 1643 '\n' 1644 'When you try to open (add data to) an episode on a health\n' 1645 'issue GNUmed will check for an existing open episode on\n' 1646 'that issue. If there is any it will check the age of that\n' 1647 'episode. The episode is closed if it has been dormant (no\n' 1648 'data added, that is) for the period of time (in days) you\n' 1649 'set here.\n' 1650 '\n' 1651 "If the existing episode hasn't been dormant long enough\n" 1652 'GNUmed will consult you what to do.\n' 1653 '\n' 1654 'Enter maximum episode dormancy in DAYS:' 1655 ), 1656 option = 'episode.ttl', 1657 bias = 'user', 1658 default_value = 60, 1659 validator = is_valid 1660 ) 1661 #----------------------------------------------
1662 - def __on_configure_user_email(self, evt):
1663 email = gmSurgery.gmCurrentPractice().user_email 1664 1665 dlg = wx.TextEntryDialog ( 1666 parent = self, 1667 message = _( 1668 'This email address will be used when GNUmed\n' 1669 'is sending email on your behalf such as when\n' 1670 'reporting bugs or when you choose to contribute\n' 1671 'reference material to the GNUmed community.\n' 1672 '\n' 1673 'The developers will then be able to get back to you\n' 1674 'directly with advice. Otherwise you would have to\n' 1675 'follow the mailing list discussion for help.\n' 1676 '\n' 1677 'Leave this blank if you wish to stay anonymous.' 1678 ), 1679 caption = _('Please enter your email address.'), 1680 defaultValue = gmTools.coalesce(email, u''), 1681 style = wx.OK | wx.CANCEL | wx.CENTRE 1682 ) 1683 decision = dlg.ShowModal() 1684 if decision == wx.ID_CANCEL: 1685 dlg.Destroy() 1686 return 1687 1688 email = dlg.GetValue().strip() 1689 gmSurgery.gmCurrentPractice().user_email = email 1690 gmExceptionHandlingWidgets.set_sender_email(email) 1691 dlg.Destroy()
1692 #----------------------------------------------
1693 - def __on_configure_workplace(self, evt):
1694 gmProviderInboxWidgets.configure_workplace_plugins(parent = self)
1695 #----------------------------------------------
1696 - def __on_configure_update_check(self, evt):
1697 gmCfgWidgets.configure_boolean_option ( 1698 question = _( 1699 'Do you want GNUmed to check for updates at startup ?\n' 1700 '\n' 1701 'You will still need your system administrator to\n' 1702 'actually install any updates for you.\n' 1703 ), 1704 option = u'horstspace.update.autocheck_at_startup', 1705 button_tooltips = [ 1706 _('Yes, check for updates at startup.'), 1707 _('No, do not check for updates at startup.') 1708 ] 1709 )
1710 #----------------------------------------------
1711 - def __on_configure_update_check_scope(self, evt):
1712 gmCfgWidgets.configure_boolean_option ( 1713 question = _( 1714 'When checking for updates do you want GNUmed to\n' 1715 'look for bug fix updates only or do you want to\n' 1716 'know about features updates, too ?\n' 1717 '\n' 1718 'Minor updates (x.y.z.a -> x.y.z.b) contain bug fixes\n' 1719 'only. They can usually be installed without much\n' 1720 'preparation. They never require a database upgrade.\n' 1721 '\n' 1722 'Major updates (x.y.a -> x..y.b or y.a -> x.b) come\n' 1723 'with new features. They need more preparation and\n' 1724 'often require a database upgrade.\n' 1725 '\n' 1726 'You will still need your system administrator to\n' 1727 'actually install any updates for you.\n' 1728 ), 1729 option = u'horstspace.update.consider_latest_branch', 1730 button_tooltips = [ 1731 _('Yes, check for feature updates, too.'), 1732 _('No, check for bug-fix updates only.') 1733 ] 1734 )
1735 #----------------------------------------------
1736 - def __on_configure_update_url(self, evt):
1737 1738 import urllib2 as url 1739 1740 def is_valid(value): 1741 try: 1742 url.urlopen(value) 1743 except: 1744 return False, value 1745 1746 return True, value
1747 1748 gmCfgWidgets.configure_string_option ( 1749 message = _( 1750 'GNUmed can check for new releases being available. To do\n' 1751 'so it needs to load version information from an URL.\n' 1752 '\n' 1753 'The default URL is:\n' 1754 '\n' 1755 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n' 1756 '\n' 1757 'but you can configure any other URL locally. Note\n' 1758 'that you must enter the location as a valid URL.\n' 1759 'Depending on the URL the client will need online\n' 1760 'access when checking for updates.' 1761 ), 1762 option = u'horstspace.update.url', 1763 bias = u'workplace', 1764 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt', 1765 validator = is_valid 1766 ) 1767 #----------------------------------------------
1768 - def __on_configure_partless_docs(self, evt):
1769 gmCfgWidgets.configure_boolean_option ( 1770 question = _( 1771 'Do you want to allow saving of new documents without\n' 1772 'any parts or do you want GNUmed to enforce that they\n' 1773 'contain at least one part before they can be saved ?\n' 1774 '\n' 1775 'Part-less documents can be useful if you want to build\n' 1776 'up an index of, say, archived documents but do not\n' 1777 'want to scan in all the pages contained therein.' 1778 ), 1779 option = u'horstspace.scan_index.allow_partless_documents', 1780 button_tooltips = [ 1781 _('Yes, allow saving documents without any parts.'), 1782 _('No, require documents to have at least one part.') 1783 ] 1784 )
1785 #----------------------------------------------
1786 - def __on_configure_doc_uuid_dialog(self, evt):
1787 gmCfgWidgets.configure_boolean_option ( 1788 question = _( 1789 'After importing a new document do you\n' 1790 'want GNUmed to display the unique ID\n' 1791 'it auto-generated for that document ?\n' 1792 '\n' 1793 'This can be useful if you want to label the\n' 1794 'originals with that ID for later identification.' 1795 ), 1796 option = u'horstspace.scan_index.show_doc_id', 1797 button_tooltips = [ 1798 _('Yes, display the ID generated for the new document after importing.'), 1799 _('No, do not display the ID generated for the new document after importing.') 1800 ] 1801 )
1802 #----------------------------------------------
1803 - def __on_configure_doc_review_dialog(self, evt):
1804 1805 def is_valid(value): 1806 try: 1807 value = int(value) 1808 except: 1809 return False, value 1810 if value not in [0, 1, 2]: 1811 return False, value 1812 return True, value
1813 1814 gmCfgWidgets.configure_string_option ( 1815 message = _( 1816 'GNUmed can show the document review dialog after\n' 1817 'calling the appropriate viewer for that document.\n' 1818 '\n' 1819 'Select the conditions under which you want\n' 1820 'GNUmed to do so:\n' 1821 '\n' 1822 ' 0: never display the review dialog\n' 1823 ' 1: always display the dialog\n' 1824 ' 2: only if there is no previous review by me\n' 1825 '\n' 1826 'Note that if a viewer is configured to not block\n' 1827 'GNUmed during document display the review dialog\n' 1828 'will actually appear in parallel to the viewer.' 1829 ), 1830 option = u'horstspace.document_viewer.review_after_display', 1831 bias = u'user', 1832 default_value = 2, 1833 validator = is_valid 1834 ) 1835 #----------------------------------------------
1836 - def __on_dicom_viewer(self, evt):
1837 1838 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 1839 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking=False) 1840 return 1841 1842 for viewer in ['aeskulap', 'amide', 'xmedcon']: 1843 found, cmd = gmShellAPI.detect_external_binary(binary = viewer) 1844 if found: 1845 gmShellAPI.run_command_in_shell(cmd, blocking=False) 1846 return 1847 1848 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
1849 #----------------------------------------------
1850 - def __on_acs_risk_assessment(self, evt):
1851 1852 dbcfg = gmCfg.cCfgSQL() 1853 cmd = dbcfg.get2 ( 1854 option = u'external.tools.acs_risk_calculator_cmd', 1855 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1856 bias = 'user' 1857 ) 1858 1859 if cmd is None: 1860 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True) 1861 return 1862 1863 cwd = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp')) 1864 try: 1865 subprocess.check_call ( 1866 args = (cmd,), 1867 close_fds = True, 1868 cwd = cwd 1869 ) 1870 except (OSError, ValueError, subprocess.CalledProcessError): 1871 _log.exception('there was a problem executing [%s]', cmd) 1872 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True) 1873 return 1874 1875 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d'))) 1876 for pdf in pdfs: 1877 try: 1878 open(pdf).close() 1879 except: 1880 _log.exception('error accessing [%s]', pdf) 1881 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the ARRIBA result in [%s] !') % pdf, beep = True) 1882 continue 1883 1884 doc = gmDocumentWidgets.save_file_as_new_document ( 1885 parent = self, 1886 filename = pdf, 1887 document_type = u'risk assessment' 1888 ) 1889 1890 try: 1891 os.remove(pdf) 1892 except StandardError: 1893 _log.exception('cannot remove [%s]', pdf) 1894 1895 if doc is None: 1896 continue 1897 doc['comment'] = u'ARRIBA: %s' % _('cardiovascular risk assessment') 1898 doc.save() 1899 1900 return
1901 #----------------------------------------------
1902 - def __on_snellen(self, evt):
1903 dlg = gmSnellen.cSnellenCfgDlg() 1904 if dlg.ShowModal() != wx.ID_OK: 1905 return 1906 1907 frame = gmSnellen.cSnellenChart ( 1908 width = dlg.vals[0], 1909 height = dlg.vals[1], 1910 alpha = dlg.vals[2], 1911 mirr = dlg.vals[3], 1912 parent = None 1913 ) 1914 frame.CentreOnScreen(wx.BOTH) 1915 # self.SetTopWindow(frame) 1916 # frame.Destroy = frame.DestroyWhenApp 1917 frame.Show(True)
1918 #---------------------------------------------- 1919 #---------------------------------------------- 1926 #----------------------------------------------
1927 - def __on_jump_to_drug_db(self, evt):
1928 gmMedicationWidgets.jump_to_drug_database()
1929 #----------------------------------------------
1930 - def __on_kompendium_ch(self, evt):
1931 webbrowser.open ( 1932 url = 'http://www.kompendium.ch', 1933 new = False, 1934 autoraise = True 1935 )
1936 #---------------------------------------------- 1937 # Help / Debugging 1938 #----------------------------------------------
1939 - def __on_save_screenshot(self, evt):
1940 wx.CallAfter(self.__save_screenshot) 1941 evt.Skip()
1942 #----------------------------------------------
1943 - def __save_screenshot(self):
1944 1945 time.sleep(0.5) 1946 1947 rect = self.GetRect() 1948 1949 # adjust for window decoration on Linux 1950 if sys.platform == 'linux2': 1951 client_x, client_y = self.ClientToScreen((0, 0)) 1952 border_width = client_x - rect.x 1953 title_bar_height = client_y - rect.y 1954 # If the window has a menu bar, remove it from the title bar height. 1955 if self.GetMenuBar(): 1956 title_bar_height /= 2 1957 rect.width += (border_width * 2) 1958 rect.height += title_bar_height + border_width 1959 1960 wdc = wx.ScreenDC() 1961 mdc = wx.MemoryDC() 1962 img = wx.EmptyBitmap(rect.width, rect.height) 1963 mdc.SelectObject(img) 1964 mdc.Blit ( # copy ... 1965 0, 0, # ... to here in the target ... 1966 rect.width, rect.height, # ... that much from ... 1967 wdc, # ... the source ... 1968 rect.x, rect.y # ... starting here 1969 ) 1970 1971 # FIXME: improve filename with patient/workplace/provider, allow user to select/change 1972 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') 1973 img.SaveFile(fname, wx.BITMAP_TYPE_PNG) 1974 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
1975 #----------------------------------------------
1976 - def __on_test_exception(self, evt):
1977 #import nonexistant_module 1978 raise ValueError('raised ValueError to test exception handling')
1979 #----------------------------------------------
1980 - def __on_invoke_inspector(self, evt):
1981 import wx.lib.inspection 1982 wx.lib.inspection.InspectionTool().Show()
1983 #----------------------------------------------
1984 - def __on_display_bugtracker(self, evt):
1985 webbrowser.open ( 1986 url = 'https://bugs.launchpad.net/gnumed/', 1987 new = False, 1988 autoraise = True 1989 )
1990 #----------------------------------------------
1991 - def __on_display_wiki(self, evt):
1992 webbrowser.open ( 1993 url = 'http://wiki.gnumed.de', 1994 new = False, 1995 autoraise = True 1996 )
1997 #----------------------------------------------
1998 - def __on_display_user_manual_online(self, evt):
1999 webbrowser.open ( 2000 url = 'http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual#UserGuideInManual', 2001 new = False, 2002 autoraise = True 2003 )
2004 #----------------------------------------------
2005 - def __on_menu_reference(self, evt):
2006 webbrowser.open ( 2007 url = 'http://wiki.gnumed.de/bin/view/Gnumed/MenuReference', 2008 new = False, 2009 autoraise = True 2010 )
2011 #----------------------------------------------
2012 - def __on_pgadmin3(self, evt):
2013 found, cmd = gmShellAPI.detect_external_binary(binary = 'pgadmin3') 2014 if found: 2015 gmShellAPI.run_command_in_shell(cmd, blocking=False) 2016 return 2017 gmDispatcher.send(signal = 'statustext', msg = _('pgAdmin III not found.'), beep = True)
2018 #----------------------------------------------
2019 - def __on_reload_hook_script(self, evt):
2020 if not gmHooks.import_hook_module(reimport = True): 2021 gmDispatcher.send(signal = 'statustext', msg = _('Error reloading hook script.'))
2022 #----------------------------------------------
2023 - def __on_unblock_cursor(self, evt):
2024 wx.EndBusyCursor()
2025 #----------------------------------------------
2026 - def __on_toggle_patient_lock(self, evt):
2027 curr_pat = gmPerson.gmCurrentPatient() 2028 if curr_pat.locked: 2029 curr_pat.force_unlock() 2030 else: 2031 curr_pat.locked = True
2032 #----------------------------------------------
2033 - def __on_show_log_file(self, evt):
2034 from Gnumed.pycommon import gmMimeLib 2035 gmLog2.flush() 2036 gmMimeLib.call_viewer_on_file(gmLog2._logfile_name, block = False)
2037 #----------------------------------------------
2038 - def __on_backup_log_file(self, evt):
2039 name = os.path.basename(gmLog2._logfile_name) 2040 name, ext = os.path.splitext(name) 2041 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 2042 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs')) 2043 2044 dlg = wx.FileDialog ( 2045 parent = self, 2046 message = _("Save current log as..."), 2047 defaultDir = new_path, 2048 defaultFile = new_name, 2049 wildcard = "%s (*.log)|*.log" % _("log files"), 2050 style = wx.SAVE 2051 ) 2052 choice = dlg.ShowModal() 2053 new_name = dlg.GetPath() 2054 dlg.Destroy() 2055 if choice != wx.ID_OK: 2056 return True 2057 2058 _log.warning('syncing log file for backup to [%s]', new_name) 2059 gmLog2.flush() 2060 shutil.copy2(gmLog2._logfile_name, new_name) 2061 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2062 #---------------------------------------------- 2063 # GNUmed / 2064 #----------------------------------------------
2065 - def OnClose(self, event):
2066 """This is the wx.EVT_CLOSE handler. 2067 2068 - framework still functional 2069 """ 2070 _log.debug('gmTopLevelFrame.OnClose() start') 2071 self._clean_exit() 2072 self.Destroy() 2073 _log.debug('gmTopLevelFrame.OnClose() end') 2074 return True
2075 #----------------------------------------------
2076 - def OnExportEMR(self, event):
2077 """ 2078 Export selected patient EMR to a file 2079 """ 2080 gmEMRBrowser.export_emr_to_ascii(parent=self)
2081 #----------------------------------------------
2082 - def __dermtool (self, event):
2083 import Gnumed.wxpython.gmDermTool as DT 2084 frame = DT.DermToolDialog(None, -1) 2085 frame.Show(True)
2086 #----------------------------------------------
2087 - def __on_start_new_encounter(self, evt):
2088 pat = gmPerson.gmCurrentPatient() 2089 if not pat.connected: 2090 gmDispatcher.send(signal = 'statustext', msg = _('Cannot start new encounter. No active patient.')) 2091 return False 2092 emr = pat.get_emr() 2093 gmEMRStructWidgets.start_new_encounter(emr = emr)
2094 #----------------------------------------------
2095 - def __on_list_encounters(self, evt):
2096 pat = gmPerson.gmCurrentPatient() 2097 if not pat.connected: 2098 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 2099 return False 2100 gmEMRStructWidgets.select_encounters()
2101 #----------------------------------------------
2102 - def __on_add_health_issue(self, event):
2103 pat = gmPerson.gmCurrentPatient() 2104 if not pat.connected: 2105 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add health issue. No active patient.')) 2106 return False 2107 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
2108 #----------------------------------------------
2109 - def __on_add_medication(self, evt):
2110 pat = gmPerson.gmCurrentPatient() 2111 if not pat.connected: 2112 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add medication. No active patient.')) 2113 return False 2114 2115 gmMedicationWidgets.edit_intake_of_substance(parent = self, substance = None) 2116 2117 evt.Skip()
2118 #----------------------------------------------
2119 - def __on_manage_allergies(self, evt):
2120 pat = gmPerson.gmCurrentPatient() 2121 if not pat.connected: 2122 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add allergy. No active patient.')) 2123 return False 2124 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 2125 dlg.ShowModal()
2126 #----------------------------------------------
2127 - def __on_manage_performed_procedures(self, evt):
2128 pat = gmPerson.gmCurrentPatient() 2129 if not pat.connected: 2130 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage performed procedures. No active patient.')) 2131 return False 2132 gmEMRStructWidgets.manage_performed_procedures(parent = self) 2133 evt.Skip()
2134 #----------------------------------------------
2135 - def __on_manage_hospital_stays(self, evt):
2136 pat = gmPerson.gmCurrentPatient() 2137 if not pat.connected: 2138 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage hospital stays. No active patient.')) 2139 return False 2140 gmEMRStructWidgets.manage_hospital_stays(parent = self) 2141 evt.Skip()
2142 #----------------------------------------------
2143 - def __on_edit_occupation(self, evt):
2144 pat = gmPerson.gmCurrentPatient() 2145 if not pat.connected: 2146 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit occupation. No active patient.')) 2147 return False 2148 gmDemographicsWidgets.edit_occupation() 2149 evt.Skip()
2150 #----------------------------------------------
2151 - def __on_add_measurement(self, evt):
2152 pat = gmPerson.gmCurrentPatient() 2153 if not pat.connected: 2154 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add measurement. No active patient.')) 2155 return False 2156 gmMeasurementWidgets.edit_measurement(parent = self, measurement = None) 2157 evt.Skip()
2158 #----------------------------------------------
2159 - def __on_show_emr_summary(self, event):
2160 pat = gmPerson.gmCurrentPatient() 2161 if not pat.connected: 2162 gmDispatcher.send(signal = 'statustext', msg = _('Cannot show EMR summary. No active patient.')) 2163 return False 2164 2165 emr = pat.get_emr() 2166 dlg = wx.MessageDialog ( 2167 parent = self, 2168 message = emr.format_statistics(), 2169 caption = _('EMR Summary'), 2170 style = wx.OK | wx.STAY_ON_TOP 2171 ) 2172 dlg.ShowModal() 2173 dlg.Destroy() 2174 return True
2175 #----------------------------------------------
2176 - def __on_search_emr(self, event):
2177 return gmNarrativeWidgets.search_narrative_in_emr(parent=self)
2178 #----------------------------------------------
2179 - def __on_search_across_emrs(self, event):
2180 gmNarrativeWidgets.search_narrative_across_emrs(parent=self)
2181 #----------------------------------------------
2182 - def __on_export_emr_as_journal(self, event):
2183 # sanity checks 2184 pat = gmPerson.gmCurrentPatient() 2185 if not pat.connected: 2186 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.')) 2187 return False 2188 # get file name 2189 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 2190 # FIXME: make configurable 2191 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])) 2192 gmTools.mkdir(aDefDir) 2193 # FIXME: make configurable 2194 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames']) 2195 dlg = wx.FileDialog ( 2196 parent = self, 2197 message = _("Save patient's EMR journal as..."), 2198 defaultDir = aDefDir, 2199 defaultFile = fname, 2200 wildcard = aWildcard, 2201 style = wx.SAVE 2202 ) 2203 choice = dlg.ShowModal() 2204 fname = dlg.GetPath() 2205 dlg.Destroy() 2206 if choice != wx.ID_OK: 2207 return True 2208 2209 _log.debug('exporting EMR journal to [%s]' % fname) 2210 # instantiate exporter 2211 exporter = gmPatientExporter.cEMRJournalExporter() 2212 2213 wx.BeginBusyCursor() 2214 try: 2215 fname = exporter.export_to_file(filename = fname) 2216 except: 2217 wx.EndBusyCursor() 2218 gmGuiHelpers.gm_show_error ( 2219 _('Error exporting patient EMR as chronological journal.'), 2220 _('EMR journal export') 2221 ) 2222 raise 2223 wx.EndBusyCursor() 2224 2225 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False) 2226 2227 return True
2228 #----------------------------------------------
2229 - def __on_export_for_medistar(self, event):
2230 gmNarrativeWidgets.export_narrative_for_medistar_import ( 2231 parent = self, 2232 soap_cats = u'soap', 2233 encounter = None # IOW, the current one 2234 )
2235 #----------------------------------------------
2236 - def __on_load_external_patient(self, event):
2237 dbcfg = gmCfg.cCfgSQL() 2238 search_immediately = bool(dbcfg.get2 ( 2239 option = 'patient_search.external_sources.immediately_search_if_single_source', 2240 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2241 bias = 'user', 2242 default = 0 2243 )) 2244 gmPatSearchWidgets.get_person_from_external_sources(parent=self, search_immediately=search_immediately, activate_immediately=True)
2245 #----------------------------------------------
2246 - def __on_export_as_gdt(self, event):
2247 curr_pat = gmPerson.gmCurrentPatient() 2248 if not curr_pat.connected: 2249 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.')) 2250 return False 2251 # FIXME: configurable 2252 enc = 'cp850' 2253 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt')) 2254 curr_pat.export_as_gdt(filename = fname, encoding = enc) 2255 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2256 #----------------------------------------------
2257 - def __on_create_new_patient(self, evt):
2258 gmDemographicsWidgets.create_new_person(parent = self, activate = True)
2259 #---------------------------------------------- 2260 # def __on_create_patient(self, event): 2261 # """Launch create patient wizard. 2262 # """ 2263 # wiz = gmDemographicsWidgets.cNewPatientWizard(parent=self) 2264 # wiz.RunWizard(activate=True) 2265 #----------------------------------------------
2266 - def __on_enlist_patient_as_staff(self, event):
2267 pat = gmPerson.gmCurrentPatient() 2268 if not pat.connected: 2269 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add staff member. No active patient.')) 2270 return False 2271 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2272 dlg.ShowModal()
2273 #----------------------------------------------
2274 - def __on_delete_patient(self, event):
2275 pat = gmPerson.gmCurrentPatient() 2276 if not pat.connected: 2277 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete patient. No patient active.')) 2278 return False 2279 gmDemographicsWidgets.disable_identity(identity=pat) 2280 return True
2281 #----------------------------------------------
2282 - def __on_merge_patients(self, event):
2283 gmPatSearchWidgets.merge_patients(parent=self)
2284 #----------------------------------------------
2285 - def __on_add_new_staff(self, event):
2286 """Create new person and add it as staff.""" 2287 #wiz = gmDemographicsWidgets.cNewPatientWizard(parent=self) 2288 #if not wiz.RunWizard(activate=True): 2289 # return False 2290 gmDemographicsWidgets.create_new_person(parent = self, activate = True) 2291 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2292 dlg.ShowModal()
2293 #----------------------------------------------
2294 - def __on_edit_staff_list(self, event):
2295 dlg = gmStaffWidgets.cEditStaffListDlg(parent=self, id=-1) 2296 dlg.ShowModal()
2297 #----------------------------------------------
2298 - def __on_edit_doc_types(self, event):
2299 dlg = gmDocumentWidgets.cEditDocumentTypesDlg(parent=self, id=-1) 2300 dlg.ShowModal()
2301 #----------------------------------------------
2302 - def __on_manage_text_expansion(self, evt):
2303 gmProviderInboxWidgets.configure_keyword_text_expansion(parent=self)
2304 #----------------------------------------------
2305 - def __on_manage_encounter_types(self, evt):
2306 gmEMRStructWidgets.manage_encounter_types(parent=self)
2307 #----------------------------------------------
2308 - def __on_manage_provinces(self, evt):
2309 gmDemographicsWidgets.manage_provinces(parent=self)
2310 #----------------------------------------------
2311 - def __on_manage_substances(self, evt):
2312 gmMedicationWidgets.manage_substances_in_use(parent = self)
2313 #----------------------------------------------
2314 - def __on_manage_branded_drugs(self, evt):
2315 gmMedicationWidgets.manage_branded_drugs(parent = self)
2316 #----------------------------------------------
2317 - def __on_manage_substances_in_brands(self, evt):
2318 gmMedicationWidgets.manage_substances_in_brands(parent = self)
2319 #----------------------------------------------
2320 - def __on_manage_test_orgs(self, evt):
2321 gmMeasurementWidgets.manage_measurement_orgs(parent = self)
2322 #----------------------------------------------
2323 - def __on_manage_test_types(self, evt):
2324 gmMeasurementWidgets.manage_measurement_types(parent = self)
2325 #----------------------------------------------
2326 - def __on_manage_meta_test_types(self, evt):
2327 gmMeasurementWidgets.manage_meta_test_types(parent = self)
2328 #----------------------------------------------
2329 - def __on_update_loinc(self, evt):
2330 gmMeasurementWidgets.update_loinc_reference_data()
2331 #----------------------------------------------
2332 - def __on_update_atc(self, evt):
2333 gmMedicationWidgets.update_atc_reference_data()
2334 #----------------------------------------------
2335 - def __on_manage_vaccines(self, evt):
2336 gmVaccWidgets.manage_vaccines(parent = self)
2337 #----------------------------------------------
2338 - def _clean_exit(self):
2339 """Cleanup helper. 2340 2341 - should ALWAYS be called when this program is 2342 to be terminated 2343 - ANY code that should be executed before a 2344 regular shutdown should go in here 2345 - framework still functional 2346 """ 2347 _log.debug('gmTopLevelFrame._clean_exit() start') 2348 2349 # shut down backend notifications listener 2350 listener = gmBackendListener.gmBackendListener() 2351 try: 2352 listener.shutdown() 2353 except: 2354 _log.exception('cannot stop backend notifications listener thread') 2355 2356 # shutdown application scripting listener 2357 if _scripting_listener is not None: 2358 try: 2359 _scripting_listener.shutdown() 2360 except: 2361 _log.exception('cannot stop scripting listener thread') 2362 2363 # shutdown timers 2364 self.clock_update_timer.Stop() 2365 gmTimer.shutdown() 2366 gmPhraseWheel.shutdown() 2367 2368 # run synchronous pre-exit callback 2369 for call_back in self.__pre_exit_callbacks: 2370 try: 2371 call_back() 2372 except: 2373 print "*** pre-exit callback failed ***" 2374 print call_back 2375 _log.exception('callback [%s] failed', call_back) 2376 2377 # signal imminent demise to plugins 2378 gmDispatcher.send(u'application_closing') 2379 2380 # do not show status line messages anymore 2381 gmDispatcher.disconnect(self._on_set_statustext, 'statustext') 2382 2383 # remember GUI size 2384 curr_width, curr_height = self.GetClientSizeTuple() 2385 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height)) 2386 dbcfg = gmCfg.cCfgSQL() 2387 dbcfg.set ( 2388 option = 'main.window.width', 2389 value = curr_width, 2390 workplace = gmSurgery.gmCurrentPractice().active_workplace 2391 ) 2392 dbcfg.set ( 2393 option = 'main.window.height', 2394 value = curr_height, 2395 workplace = gmSurgery.gmCurrentPractice().active_workplace 2396 ) 2397 2398 if _cfg.get(option = 'debug'): 2399 print '---=== GNUmed shutdown ===---' 2400 print _('You have to manually close this window to finalize shutting down GNUmed.') 2401 print _('This is so that you can inspect the console output at your leisure.') 2402 print '---=== GNUmed shutdown ===---' 2403 2404 # shutdown GUI exception handling 2405 gmExceptionHandlingWidgets.uninstall_wx_exception_handler() 2406 2407 # are we clean ? 2408 import threading 2409 _log.debug("%s active threads", threading.activeCount()) 2410 for t in threading.enumerate(): 2411 _log.debug('thread %s', t) 2412 2413 _log.debug('gmTopLevelFrame._clean_exit() end')
2414 #---------------------------------------------- 2415 # internal API 2416 #----------------------------------------------
2417 - def __set_window_title_template(self):
2418 2419 if _cfg.get(option = 'slave'): 2420 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % ( 2421 _cfg.get(option = 'slave personality'), 2422 _cfg.get(option = 'xml-rpc port') 2423 ) 2424 else: 2425 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2426 #----------------------------------------------
2427 - def __update_window_title(self):
2428 """Update title of main window based on template. 2429 2430 This gives nice tooltips on iconified GNUmed instances. 2431 2432 User research indicates that in the title bar people want 2433 the date of birth, not the age, so please stick to this 2434 convention. 2435 """ 2436 args = {} 2437 2438 pat = gmPerson.gmCurrentPatient() 2439 if pat.connected: 2440 # title = pat['title'] 2441 # if title is None: 2442 # title = '' 2443 # else: 2444 # title = title[:4] 2445 2446 args['pat'] = u'%s %s %s (%s) #%d' % ( 2447 gmTools.coalesce(pat['title'], u'', u'%.4s'), 2448 #title, 2449 pat['firstnames'], 2450 pat['lastnames'], 2451 pat.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()), 2452 pat['pk_identity'] 2453 ) 2454 else: 2455 args['pat'] = _('no patient') 2456 2457 args['prov'] = u'%s%s.%s' % ( 2458 gmTools.coalesce(_provider['title'], u'', u'%s '), 2459 _provider['firstnames'][:1], 2460 _provider['lastnames'] 2461 ) 2462 2463 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace 2464 2465 self.SetTitle(self.__title_template % args)
2466 #---------------------------------------------- 2467 #----------------------------------------------
2468 - def setup_statusbar(self):
2469 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP) 2470 sb.SetStatusWidths([-1, 225]) 2471 # add time and date display to the right corner of the status bar 2472 self.clock_update_timer = wx.PyTimer(self._cb_update_clock) 2473 self._cb_update_clock() 2474 # update every second 2475 self.clock_update_timer.Start(milliseconds = 1000)
2476 #----------------------------------------------
2477 - def _cb_update_clock(self):
2478 """Displays date and local time in the second slot of the status bar""" 2479 t = time.localtime(time.time()) 2480 st = time.strftime('%c', t).decode(gmI18N.get_encoding()) 2481 self.SetStatusText(st,1)
2482 #------------------------------------------------
2483 - def Lock(self):
2484 """Lock GNUmed client against unauthorized access""" 2485 # FIXME 2486 # for i in range(1, self.nb.GetPageCount()): 2487 # self.nb.GetPage(i).Enable(False) 2488 return
2489 #----------------------------------------------
2490 - def Unlock(self):
2491 """Unlock the main notebook widgets 2492 As long as we are not logged into the database backend, 2493 all pages but the 'login' page of the main notebook widget 2494 are locked; i.e. not accessible by the user 2495 """ 2496 #unlock notebook pages 2497 # for i in range(1, self.nb.GetPageCount()): 2498 # self.nb.GetPage(i).Enable(True) 2499 # go straight to patient selection 2500 # self.nb.AdvanceSelection() 2501 return
2502 #-----------------------------------------------
2503 - def OnPanelSize (self, event):
2504 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
2505 #==============================================================================
2506 -class gmApp(wx.App):
2507
2508 - def OnInit(self):
2509 2510 self.__starting_up = True 2511 2512 gmExceptionHandlingWidgets.install_wx_exception_handler() 2513 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version')) 2514 2515 # _log.info('display: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 2516 2517 # set this so things like "wx.StandardPaths.GetDataDir()" work as expected 2518 self.SetAppName(u'gnumed') 2519 self.SetVendorName(u'The GNUmed Development Community.') 2520 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 2521 paths.init_paths(wx = wx, app_name = u'gnumed') 2522 2523 if not self.__setup_prefs_file(): 2524 return False 2525 2526 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email) 2527 2528 self.__guibroker = gmGuiBroker.GuiBroker() 2529 self.__setup_platform() 2530 2531 if not self.__establish_backend_connection(): 2532 return False 2533 2534 if not _cfg.get(option = 'skip-update-check'): 2535 self.__check_for_updates() 2536 2537 if _cfg.get(option = 'slave'): 2538 if not self.__setup_scripting_listener(): 2539 return False 2540 2541 # FIXME: load last position from backend 2542 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640,440)) 2543 frame.CentreOnScreen(wx.BOTH) 2544 self.SetTopWindow(frame) 2545 frame.Show(True) 2546 2547 if _cfg.get(option = 'debug'): 2548 self.RedirectStdio() 2549 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window')) 2550 # print this so people know what this window is for 2551 # and don't get suprised when it pops up later 2552 print '---=== GNUmed startup ===---' 2553 print _('redirecting STDOUT/STDERR to this log window') 2554 print '---=== GNUmed startup ===---' 2555 2556 self.__setup_user_activity_timer() 2557 self.__register_events() 2558 2559 wx.CallAfter(self._do_after_init) 2560 2561 return True
2562 #----------------------------------------------
2563 - def OnExit(self):
2564 """Called internally by wxPython after EVT_CLOSE has been handled on last frame. 2565 2566 - after destroying all application windows and controls 2567 - before wx.Windows internal cleanup 2568 """ 2569 _log.debug('gmApp.OnExit() start') 2570 2571 self.__shutdown_user_activity_timer() 2572 2573 if _cfg.get(option = 'debug'): 2574 self.RestoreStdio() 2575 sys.stdin = sys.__stdin__ 2576 sys.stdout = sys.__stdout__ 2577 sys.stderr = sys.__stderr__ 2578 2579 _log.debug('gmApp.OnExit() end')
2580 #----------------------------------------------
2581 - def _on_query_end_session(self, *args, **kwargs):
2582 wx.Bell() 2583 wx.Bell() 2584 wx.Bell() 2585 _log.warning('unhandled event detected: QUERY_END_SESSION') 2586 _log.info('we should be saving ourselves from here') 2587 gmLog2.flush() 2588 print "unhandled event detected: QUERY_END_SESSION"
2589 #----------------------------------------------
2590 - def _on_end_session(self, *args, **kwargs):
2591 wx.Bell() 2592 wx.Bell() 2593 wx.Bell() 2594 _log.warning('unhandled event detected: END_SESSION') 2595 gmLog2.flush() 2596 print "unhandled event detected: END_SESSION"
2597 #----------------------------------------------
2598 - def _on_app_activated(self, evt):
2599 if evt.GetActive(): 2600 if self.__starting_up: 2601 gmHooks.run_hook_script(hook = u'app_activated_startup') 2602 else: 2603 gmHooks.run_hook_script(hook = u'app_activated') 2604 else: 2605 gmHooks.run_hook_script(hook = u'app_deactivated') 2606 2607 evt.Skip()
2608 #----------------------------------------------
2609 - def _on_user_activity(self, evt):
2610 self.user_activity_detected = True 2611 evt.Skip()
2612 #----------------------------------------------
2613 - def _on_user_activity_timer_expired(self, cookie=None):
2614 2615 if self.user_activity_detected: 2616 self.elapsed_inactivity_slices = 0 2617 self.user_activity_detected = False 2618 self.elapsed_inactivity_slices += 1 2619 else: 2620 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices: 2621 # print "User was inactive for 30 seconds." 2622 pass 2623 2624 self.user_activity_timer.Start(oneShot = True)
2625 #---------------------------------------------- 2626 # internal helpers 2627 #----------------------------------------------
2628 - def _signal_debugging_monitor(*args, **kwargs):
2629 try: 2630 kwargs['originated_in_database'] 2631 print '==> got notification from database "%s":' % kwargs['signal'] 2632 except KeyError: 2633 print '==> received signal from client: "%s"' % kwargs['signal'] 2634 2635 del kwargs['signal'] 2636 for key in kwargs.keys(): 2637 print ' [%s]: %s' % (key, kwargs[key])
2638 #----------------------------------------------
2639 - def _signal_debugging_monitor_pubsub(self, msg):
2640 print "wx.lib.pubsub message:" 2641 print msg.topic 2642 print msg.data
2643 #----------------------------------------------
2644 - def _do_after_init(self):
2645 self.__starting_up = False 2646 gmClinicalRecord.set_func_ask_user(a_func = gmEMRStructWidgets.ask_for_encounter_continuation) 2647 self.__guibroker['horstspace.top_panel'].patient_selector.SetFocus() 2648 gmHooks.run_hook_script(hook = u'startup-after-GUI-init')
2649 #----------------------------------------------
2651 self.user_activity_detected = True 2652 self.elapsed_inactivity_slices = 0 2653 # FIXME: make configurable 2654 self.max_user_inactivity_slices = 15 # 15 * 2000ms == 30 seconds 2655 self.user_activity_timer = gmTimer.cTimer ( 2656 callback = self._on_user_activity_timer_expired, 2657 delay = 2000 # hence a minimum of 2 and max of 3.999... seconds after which inactivity is detected 2658 ) 2659 self.user_activity_timer.Start(oneShot=True)
2660 #----------------------------------------------
2662 try: 2663 self.user_activity_timer.Stop() 2664 del self.user_activity_timer 2665 except: 2666 pass
2667 #----------------------------------------------
2668 - def __register_events(self):
2669 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session) 2670 wx.EVT_END_SESSION(self, self._on_end_session) 2671 2672 # You can bind your app to wx.EVT_ACTIVATE_APP which will fire when your 2673 # app gets/looses focus, or you can wx.EVT_ACTIVATE with any of your 2674 # toplevel windows and call evt.GetActive() in the handler to see whether 2675 # it is gaining or loosing focus. 2676 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated) 2677 2678 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity) 2679 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
2680 2681 # if _cfg.get(option = 'debug'): 2682 # gmDispatcher.connect(receiver = self._signal_debugging_monitor) 2683 # _log.debug('connected old signal monitor') 2684 # wx.lib.pubsub.Publisher().subscribe ( 2685 # listener = self._signal_debugging_monitor_pubsub, 2686 # topic = wx.lib.pubsub.getStrAllTopics() 2687 # ) 2688 # _log.debug('connected wx.lib.pubsub based signal monitor for all topics: [%s]', wx.lib.pubsub.getStrAllTopics()) 2689 #----------------------------------------------
2690 - def __check_for_updates(self):
2691 2692 dbcfg = gmCfg.cCfgSQL() 2693 2694 do_check = bool(dbcfg.get2 ( 2695 option = u'horstspace.update.autocheck_at_startup', 2696 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2697 bias = 'workplace', 2698 default = True 2699 )) 2700 2701 if not do_check: 2702 return 2703 2704 gmCfgWidgets.check_for_updates()
2705 #----------------------------------------------
2707 """Handle all the database related tasks necessary for startup.""" 2708 2709 # log on 2710 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')]) 2711 2712 from Gnumed.wxpython import gmAuthWidgets 2713 connected = gmAuthWidgets.connect_to_database ( 2714 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')], 2715 require_version = not override 2716 ) 2717 if not connected: 2718 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection") 2719 return False 2720 2721 # check account <-> staff member association 2722 try: 2723 global _provider 2724 _provider = gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 2725 except ValueError: 2726 account = gmPG2.get_current_user() 2727 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account) 2728 msg = _( 2729 'The database account [%s] cannot be used as a\n' 2730 'staff member login for GNUmed. There was an\n' 2731 'error retrieving staff details for it.\n\n' 2732 'Please ask your administrator for help.\n' 2733 ) % account 2734 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions')) 2735 return False 2736 2737 # improve exception handler setup 2738 tmp = '%s%s %s (%s = %s)' % ( 2739 gmTools.coalesce(_provider['title'], ''), 2740 _provider['firstnames'], 2741 _provider['lastnames'], 2742 _provider['short_alias'], 2743 _provider['db_user'] 2744 ) 2745 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp) 2746 2747 # display database banner 2748 surgery = gmSurgery.gmCurrentPractice() 2749 msg = surgery.db_logon_banner 2750 if msg.strip() != u'': 2751 2752 login = gmPG2.get_default_login() 2753 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % ( 2754 login.database, 2755 gmTools.coalesce(login.host, u'localhost') 2756 )) 2757 msg = auth + msg + u'\n\n' 2758 2759 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 2760 None, 2761 -1, 2762 caption = _('Verifying database'), 2763 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '), 2764 button_defs = [ 2765 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True}, 2766 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False} 2767 ] 2768 ) 2769 go_on = dlg.ShowModal() 2770 dlg.Destroy() 2771 if go_on != wx.ID_YES: 2772 _log.info('user decided to not connect to this database') 2773 return False 2774 2775 # check database language settings 2776 self.__check_db_lang() 2777 2778 return True
2779 #----------------------------------------------
2780 - def __setup_prefs_file(self):
2781 """Setup access to a config file for storing preferences.""" 2782 2783 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 2784 2785 candidates = [] 2786 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')]) 2787 if explicit_file is not None: 2788 candidates.append(explicit_file) 2789 # provide a few fallbacks in the event the --conf-file isn't writable 2790 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf')) 2791 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf')) 2792 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf')) 2793 2794 prefs_file = None 2795 for candidate in candidates: 2796 try: 2797 open(candidate, 'a+').close() 2798 prefs_file = candidate 2799 break 2800 except IOError: 2801 continue 2802 2803 if prefs_file is None: 2804 msg = _( 2805 'Cannot find configuration file in any of:\n' 2806 '\n' 2807 ' %s\n' 2808 'You may need to use the comand line option\n' 2809 '\n' 2810 ' --conf-file=<FILE>' 2811 ) % '\n '.join(candidates) 2812 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files')) 2813 return False 2814 2815 _cfg.set_option(option = u'user_preferences_file', value = prefs_file) 2816 _log.info('user preferences file: %s', prefs_file) 2817 2818 return True
2819 #----------------------------------------------
2820 - def __setup_scripting_listener(self):
2821 2822 from socket import error as SocketError 2823 from Gnumed.pycommon import gmScriptingListener 2824 from Gnumed.wxpython import gmMacro 2825 2826 slave_personality = gmTools.coalesce ( 2827 _cfg.get ( 2828 group = u'workplace', 2829 option = u'slave personality', 2830 source_order = [ 2831 ('explicit', 'return'), 2832 ('workbase', 'return'), 2833 ('user', 'return'), 2834 ('system', 'return') 2835 ] 2836 ), 2837 u'gnumed-client' 2838 ) 2839 _cfg.set_option(option = 'slave personality', value = slave_personality) 2840 2841 # FIXME: handle port via /var/run/ 2842 port = int ( 2843 gmTools.coalesce ( 2844 _cfg.get ( 2845 group = u'workplace', 2846 option = u'xml-rpc port', 2847 source_order = [ 2848 ('explicit', 'return'), 2849 ('workbase', 'return'), 2850 ('user', 'return'), 2851 ('system', 'return') 2852 ] 2853 ), 2854 9999 2855 ) 2856 ) 2857 _cfg.set_option(option = 'xml-rpc port', value = port) 2858 2859 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality) 2860 global _scripting_listener 2861 try: 2862 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor) 2863 except SocketError, e: 2864 _log.exception('cannot start GNUmed XML-RPC server') 2865 gmGuiHelpers.gm_show_error ( 2866 aMessage = ( 2867 'Cannot start the GNUmed server:\n' 2868 '\n' 2869 ' [%s]' 2870 ) % e, 2871 aTitle = _('GNUmed startup') 2872 ) 2873 return False 2874 2875 return True
2876 #----------------------------------------------
2877 - def __setup_platform(self):
2878 2879 import wx.lib.colourdb 2880 wx.lib.colourdb.updateColourDB() 2881 2882 traits = self.GetTraits() 2883 try: 2884 _log.info('desktop environment: [%s]', traits.GetDesktopEnvironment()) 2885 except: 2886 pass 2887 2888 if wx.Platform == '__WXMSW__': 2889 _log.info('running on MS Windows') 2890 elif wx.Platform == '__WXGTK__': 2891 _log.info('running on GTK (probably Linux)') 2892 elif wx.Platform == '__WXMAC__': 2893 _log.info('running on Mac OS') 2894 else: 2895 _log.info('running on an unknown platform (%s)' % wx.Platform)
2896 #----------------------------------------------
2897 - def __check_db_lang(self):
2898 if gmI18N.system_locale is None or gmI18N.system_locale == '': 2899 _log.warning("system locale is undefined (probably meaning 'C')") 2900 return True 2901 2902 # get current database locale 2903 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}]) 2904 db_lang = rows[0]['lang'] 2905 2906 if db_lang is None: 2907 _log.debug("database locale currently not set") 2908 msg = _( 2909 "There is no language selected in the database for user [%s].\n" 2910 "Your system language is currently set to [%s].\n\n" 2911 "Do you want to set the database language to '%s' ?\n\n" 2912 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale) 2913 checkbox_msg = _('Remember to ignore missing language') 2914 else: 2915 _log.debug("current database locale: [%s]" % db_lang) 2916 msg = _( 2917 "The currently selected database language ('%s') does\n" 2918 "not match the current system language ('%s').\n" 2919 "\n" 2920 "Do you want to set the database language to '%s' ?\n" 2921 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale) 2922 checkbox_msg = _('Remember to ignore language mismatch') 2923 2924 # check if we can match up system and db language somehow 2925 if db_lang == gmI18N.system_locale_level['full']: 2926 _log.debug('Database locale (%s) up to date.' % db_lang) 2927 return True 2928 if db_lang == gmI18N.system_locale_level['country']: 2929 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale)) 2930 return True 2931 if db_lang == gmI18N.system_locale_level['language']: 2932 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale)) 2933 return True 2934 # no match 2935 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale)) 2936 2937 # returns either None or a locale string 2938 ignored_sys_lang = _cfg.get ( 2939 group = u'backend', 2940 option = u'ignored mismatching system locale', 2941 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')] 2942 ) 2943 2944 # are we to ignore *this* mismatch ? 2945 if gmI18N.system_locale == ignored_sys_lang: 2946 _log.info('configured to ignore system-to-database locale mismatch') 2947 return True 2948 2949 # no, so ask user 2950 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 2951 None, 2952 -1, 2953 caption = _('Checking database language settings'), 2954 question = msg, 2955 button_defs = [ 2956 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True}, 2957 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False} 2958 ], 2959 show_checkbox = True, 2960 checkbox_msg = checkbox_msg, 2961 checkbox_tooltip = _( 2962 'Checking this will make GNUmed remember your decision\n' 2963 'until the system language is changed.\n' 2964 '\n' 2965 'You can also reactivate this inquiry by removing the\n' 2966 'corresponding "ignore" option from the configuration file\n' 2967 '\n' 2968 ' [%s]' 2969 ) % _cfg.get(option = 'user_preferences_file') 2970 ) 2971 decision = dlg.ShowModal() 2972 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue() 2973 dlg.Destroy() 2974 2975 if decision == wx.ID_NO: 2976 if not remember_ignoring_problem: 2977 return True 2978 _log.info('User did not want to set database locale. Ignoring mismatch next time.') 2979 gmCfg2.set_option_in_INI_file ( 2980 filename = _cfg.get(option = 'user_preferences_file'), 2981 group = 'backend', 2982 option = 'ignored mismatching system locale', 2983 value = gmI18N.system_locale 2984 ) 2985 return True 2986 2987 # try setting database language (only possible if translation exists) 2988 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]: 2989 if len(lang) > 0: 2990 # users are getting confused, so don't show these "errors", 2991 # they really are just notices about us being nice 2992 rows, idx = gmPG2.run_rw_queries ( 2993 link_obj = None, 2994 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}], 2995 return_data = True 2996 ) 2997 if rows[0][0]: 2998 _log.debug("Successfully set database language to [%s]." % lang) 2999 else: 3000 _log.error('Cannot set database language to [%s].' % lang) 3001 continue 3002 return True 3003 3004 # no match found but user wanted to set language anyways, so force it 3005 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country']) 3006 gmPG2.run_rw_queries(queries = [{ 3007 'cmd': u'select i18n.force_curr_lang(%s)', 3008 'args': [gmI18N.system_locale_level['country']] 3009 }]) 3010 3011 return True
3012 #==============================================================================
3013 -def _signal_debugging_monitor(*args, **kwargs):
3014 try: 3015 kwargs['originated_in_database'] 3016 print '==> got notification from database "%s":' % kwargs['signal'] 3017 except KeyError: 3018 print '==> received signal from client: "%s"' % kwargs['signal'] 3019 3020 del kwargs['signal'] 3021 for key in kwargs.keys(): 3022 # careful because of possibly limited console output encoding 3023 try: print ' [%s]: %s' % (key, kwargs[key]) 3024 except: print 'cannot print signal information'
3025 #------------------------------------------------------------------------------
3026 -def _signal_debugging_monitor_pubsub(msg):
3027 # careful because of possibly limited console output encoding 3028 try: 3029 print '==> received wx.lib.pubsub message: "%s"' % msg.topic 3030 print ' data: %s' % msg.data 3031 print msg 3032 except: print 'problem printing pubsub message information'
3033 #==============================================================================
3034 -def main():
3035 3036 if _cfg.get(option = 'debug'): 3037 gmDispatcher.connect(receiver = _signal_debugging_monitor) 3038 _log.debug('gmDispatcher signal monitor activated') 3039 wx.lib.pubsub.Publisher().subscribe ( 3040 listener = _signal_debugging_monitor_pubsub, 3041 topic = wx.lib.pubsub.getStrAllTopics() 3042 ) 3043 _log.debug('wx.lib.pubsub signal monitor activated') 3044 3045 # create an instance of our GNUmed main application 3046 # - do not redirect stdio (yet) 3047 # - allow signals to be delivered 3048 app = gmApp(redirect = False, clearSigInt = False) 3049 app.MainLoop()
3050 #============================================================================== 3051 # Main 3052 #============================================================================== 3053 if __name__ == '__main__': 3054 3055 from GNUmed.pycommon import gmI18N 3056 gmI18N.activate_locale() 3057 gmI18N.install_domain() 3058 3059 _log.info('Starting up as main module.') 3060 main() 3061 3062 #============================================================================== 3063 # $Log: gmGuiMain.py,v $ 3064 # Revision 1.491 2010-02-07 15:14:07 ncq 3065 # - comment out some deprecated code 3066 # - root out a forgotten use of the old new-patient wizard (found by Jim) 3067 # 3068 # Revision 1.490 2010/02/06 21:06:59 ncq 3069 # - add time to status line messages 3070 # 3071 # Revision 1.489 2010/02/02 13:54:41 ncq 3072 # - tidy up master data management 3073 # - add managing diagnostic orgs 3074 # 3075 # Revision 1.488 2010/01/31 18:16:35 ncq 3076 # - access to default region/country setting 3077 # 3078 # Revision 1.487 2010/01/11 19:46:19 ncq 3079 # - cleanup 3080 # 3081 # Revision 1.486 2010/01/10 17:27:16 ncq 3082 # - check-for-updates now in cfg widgets 3083 # 3084 # Revision 1.485 2010/01/08 13:54:50 ncq 3085 # - retire old-style new-patient 3086 # 3087 # Revision 1.484 2010/01/06 14:40:51 ncq 3088 # - tie docs printing cleanup to --debug 3089 # 3090 # Revision 1.483 2010/01/01 21:21:24 ncq 3091 # - improved window title as per list 3092 # - make writing letters generic so LaTeX templates can be used, too 3093 # 3094 # Revision 1.482 2009/12/25 21:45:28 ncq 3095 # - configure meds list template 3096 # - manage form templates 3097 # 3098 # Revision 1.481 2009/12/21 15:06:45 ncq 3099 # - factor out check-for-updates 3100 # - support --skip-update-check 3101 # 3102 # Revision 1.480 2009/12/01 21:52:40 ncq 3103 # - improved menu items 3104 # - remove current medication menu item 3105 # 3106 # Revision 1.479 2009/11/28 18:29:33 ncq 3107 # - more master data management: drug brands and components thereof 3108 # 3109 # Revision 1.478 2009/11/15 01:06:49 ncq 3110 # - better start of new encounter 3111 # 3112 # Revision 1.477 2009/11/08 20:43:50 ncq 3113 # - search across all EMRs 3114 # 3115 # Revision 1.476 2009/11/06 15:18:27 ncq 3116 # - reanimate emr summary under show as ... 3117 # 3118 # Revision 1.475 2009/10/21 21:42:56 ncq 3119 # - fix faulty GUI string 3120 # 3121 # Revision 1.474 2009/10/21 08:56:40 ncq 3122 # - manage substances 3123 # - jump to drug db 3124 # 3125 # Revision 1.473 2009/10/20 10:26:50 ncq 3126 # - support drug data source configuration 3127 # 3128 # Revision 1.472 2009/09/29 13:16:03 ncq 3129 # - cleanup of code layout 3130 # - _set_ -> _configure_ 3131 # - start drug data source selection 3132 # 3133 # Revision 1.471 2009/09/17 21:53:41 ncq 3134 # - start support for managing performed procedures 3135 # 3136 # Revision 1.470 2009/09/13 18:45:25 ncq 3137 # - no more get-active-encounter() 3138 # 3139 # Revision 1.469 2009/09/01 22:32:42 ncq 3140 # - use edit-health-issue 3141 # 3142 # Revision 1.468 2009/08/03 20:48:29 ncq 3143 # - cleanup 3144 # 3145 # Revision 1.467 2009/07/23 16:40:55 ncq 3146 # - patient -> person 3147 # - staff -> user 3148 # - improved database language selection: pre-select current language 3149 # 3150 # Revision 1.466 2009/07/17 09:29:14 ncq 3151 # - some cleanup 3152 # - Destroy dangling dialog from startup sequence which prevented 3153 # proper closing 3154 # - improved plugin registration with menu system 3155 # 3156 # Revision 1.465 2009/07/15 12:22:13 ncq 3157 # - improved window title if running in slave mode 3158 # - some more room for the bottom-right time display 3159 # 3160 # Revision 1.464 2009/07/09 16:47:10 ncq 3161 # - go to plugins now with active letter 3162 # - if not lang is set it returns none, not zero rows 3163 # 3164 # Revision 1.463 2009/07/02 20:53:24 ncq 3165 # - flush log during close 3166 # - slightly safer shutdown 3167 # 3168 # Revision 1.462 2009/07/01 17:16:06 ncq 3169 # - somewhat improved menu layout as per list 3170 # - use improved plugin names on loading 3171 # 3172 # Revision 1.461 2009/06/29 15:32:02 ncq 3173 # - fix typo 3174 # 3175 # Revision 1.460 2009/06/29 15:16:27 ncq 3176 # - reorder menus as per list discussion 3177 # 3178 # Revision 1.459 2009/06/20 22:36:08 ncq 3179 # - move IFAP handling to proper file 3180 # - better wording of uptodate client message 3181 # - improved menu item wording as per list discussion 3182 # - show backend details in startup welcome message 3183 # 3184 # Revision 1.458 2009/06/11 12:47:44 ncq 3185 # - be more careful and more verbose about exiting 3186 # 3187 # Revision 1.457 2009/06/11 11:08:47 ncq 3188 # - better wrapping for database welcome message 3189 # 3190 # Revision 1.456 2009/06/10 21:03:17 ncq 3191 # - add menu item for updating ATC 3192 # 3193 # Revision 1.455 2009/06/04 16:13:11 ncq 3194 # - re-adjust to dob-less person 3195 # - update LOINC 3196 # - better about database 3197 # 3198 # Revision 1.455 2009/05/28 10:55:47 ncq 3199 # - adjust to DOB less persons 3200 # 3201 # Revision 1.454 2009/05/24 16:28:46 ncq 3202 # - list (meta) test types 3203 # 3204 # Revision 1.453 2009/05/18 15:32:05 ncq 3205 # - improved stdio message 3206 # 3207 # Revision 1.452 2009/05/13 12:19:58 ncq 3208 # - make moving narrative accessible from menu 3209 # - make new style new patient entry the default 3210 # 3211 # Revision 1.451 2009/05/08 08:00:32 ncq 3212 # - set true client version in exception handling earlier 3213 # 3214 # Revision 1.450 2009/04/21 17:00:41 ncq 3215 # - give access to new new-pat EA 3216 # 3217 # Revision 1.449 2009/04/20 11:40:45 ncq 3218 # - add MI/stroke risk calculator access 3219 # 3220 # Revision 1.448 2009/04/19 22:29:15 ncq 3221 # - implement editing url for hyperlink in upper left corner of measurements grid 3222 # 3223 # Revision 1.447 2009/04/16 12:49:05 ncq 3224 # - improved pubsub monitor output 3225 # 3226 # Revision 1.446 2009/04/14 18:37:30 ncq 3227 # - set vendor name 3228 # - add message monitor for pubsub 3229 # - move signal debugging monitors up to the module level 3230 # 3231 # Revision 1.445 2009/04/13 10:54:37 ncq 3232 # - support listing encounters 3233 # 3234 # Revision 1.444 2009/04/03 09:49:55 ncq 3235 # - user level access to hospital stay handling 3236 # - pubsub based listening for statustext 3237 # - explicit phrasewheel shutdown (timers) 3238 # 3239 # Revision 1.443 2009/02/17 11:49:45 ncq 3240 # - manage workplaces under master data now 3241 # 3242 # Revision 1.442 2009/02/17 08:34:58 ncq 3243 # - save screenshot now also supports window decorations 3244 # 3245 # Revision 1.441 2009/02/05 21:10:59 ncq 3246 # - rapid plugin access 3247 # 3248 # Revision 1.440 2009/02/05 13:03:38 ncq 3249 # - cleanup 3250 # 3251 # Revision 1.439 2009/02/04 12:34:55 ncq 3252 # - cleanup frame init 3253 # 3254 # Revision 1.438 2009/01/15 11:38:44 ncq 3255 # - better logging, cleanup 3256 # - fix logic error in xml-rpc port detection 3257 # - display personality/port in window title if enslaved 3258 # 3259 # Revision 1.437 2008/12/26 16:03:36 ncq 3260 # - properly dispose of user activity timer 3261 # 3262 # Revision 1.436 2008/12/25 23:32:50 ncq 3263 # - shutdown timers as early as possible during application shutdown 3264 # 3265 # Revision 1.435 2008/12/25 16:54:56 ncq 3266 # - support unsetting DB language 3267 # 3268 # Revision 1.434 2008/12/17 21:58:23 ncq 3269 # - add merging two patients 3270 # 3271 # Revision 1.433 2008/12/09 23:31:18 ncq 3272 # - help menu: show log file 3273 # 3274 # Revision 1.432 2008/10/26 01:22:30 ncq 3275 # - factor out searching EMR for narrative 3276 # 3277 # Revision 1.431 2008/10/22 12:20:32 ncq 3278 # - version handling for client, branch and db is now handled 3279 # in gnumed.py and gmPG2.py 3280 # 3281 # Revision 1.430 2008/10/12 16:48:13 ncq 3282 # - bump version 3283 # - derive db version via gmPG2 mapping 3284 # - no more "consultation" or "foundational" 3285 # - fix setting db lang 3286 # - apply wx.CallAfter to taking screenshot 3287 # - cleanup 3288 # - set client version for exception handling 3289 # 3290 # Revision 1.429 2008/09/02 20:21:48 ncq 3291 # - menu item to announce maintenance downtime 3292 # 3293 # Revision 1.428 2008/08/31 18:02:45 ncq 3294 # - add "Menu reference" menu item 3295 # 3296 # Revision 1.427 2008/08/31 16:16:27 ncq 3297 # - comment 3298 # 3299 # Revision 1.426 2008/08/23 14:47:54 ncq 3300 # - bump RC version 3301 # 3302 # Revision 1.425 2008/08/21 13:29:18 ncq 3303 # - add pgAdmin III to debug menu 3304 # 3305 # Revision 1.424 2008/08/17 10:31:38 ncq 3306 # - add "About database" 3307 # 3308 # Revision 1.423 2008/08/15 16:02:16 ncq 3309 # - do not GDT-export w/o active patient 3310 # - manage provinces 3311 # 3312 # Revision 1.422 2008/08/08 13:31:37 ncq 3313 # - a bit of cleanup 3314 # - support pre-exit sync callbacks 3315 # 3316 # Revision 1.421 2008/08/06 13:27:16 ncq 3317 # - include system locale in list when setting db lang 3318 # - allow forcing db lang 3319 # - improve startup db lang check 3320 # 3321 # Revision 1.420 2008/08/05 16:45:12 ncq 3322 # - add wxAppTraits querying 3323 # 3324 # Revision 1.419 2008/07/28 20:41:58 ncq 3325 # - support version in about box 3326 # 3327 # Revision 1.418 2008/07/28 15:52:29 ncq 3328 # - no more initial startup plugin, do with hook if wanted 3329 # - properly set sender email in exception handler after option was modified and client startup 3330 # - factor out Medistar export 3331 # 3332 # Revision 1.417 2008/07/24 14:02:03 ncq 3333 # - some menu reorg/renaming 3334 # - invoke encounter type managment 3335 # 3336 # Revision 1.416 2008/07/16 11:12:01 ncq 3337 # - cleanup 3338 # - enable user email configuration and use it 3339 # 3340 # Revision 1.415 2008/07/15 15:24:54 ncq 3341 # - check for wxp2.8 3342 # - set current branch to 0.3 3343 # 3344 # Revision 1.414 2008/07/14 13:47:15 ncq 3345 # - some menu reorg 3346 # - do synced encounter sanity check on patient change :-) 3347 # 3348 # Revision 1.413 2008/07/13 16:10:31 ncq 3349 # - master data menu 3350 # - manage text expansions 3351 # - add_new_measurement -> edit_measurement(measurement = None) 3352 # - cleanly shutdown timers 3353 # 3354 # Revision 1.412 2008/07/10 20:52:55 ncq 3355 # - better to call path detection with app name and wx 3356 # 3357 # Revision 1.411 2008/07/07 13:43:17 ncq 3358 # - current patient .connected 3359 # 3360 # Revision 1.410 2008/06/28 22:34:46 ncq 3361 # - add option on progress notes editor handling 3362 # 3363 # Revision 1.409 2008/06/28 18:26:50 ncq 3364 # - enable temp dir configuration 3365 # - link to kompendium.ch 3366 # - some menu reorg 3367 # 3368 # Revision 1.408 2008/06/26 17:01:57 ncq 3369 # - be extra careful about returning distinct results from cfg 3370 # 3371 # Revision 1.407 2008/06/16 21:35:12 ncq 3372 # - put "add measurements" under "observations" in emr menu 3373 # 3374 # Revision 1.406 2008/06/09 15:34:57 ncq 3375 # - "add measurement" from menu 3376 # 3377 # Revision 1.405 2008/05/29 13:28:37 ncq 3378 # - improved logging of EVT(_QUERY)_END_SESSION 3379 # 3380 # Revision 1.404 2008/05/26 13:31:34 ncq 3381 # - "properly" set current branch 3382 # 3383 # Revision 1.403 2008/05/26 12:09:37 ncq 3384 # - some cleanup 3385 # - check_for_updates and call that from menu item 3386 # and startup process 3387 # - menu items for configuring update check 3388 # 3389 # Revision 1.402 2008/05/21 15:53:06 ncq 3390 # - add initial support for update notifier 3391 # 3392 # Revision 1.401 2008/05/20 16:44:44 ncq 3393 # - clean up OnInit 3394 # - start listening to user inactivity 3395 # 3396 # Revision 1.400 2008/05/19 16:24:07 ncq 3397 # - let EMR format its summary itself 3398 # 3399 # Revision 1.399 2008/05/13 14:12:55 ncq 3400 # - exc handling adjustments 3401 # 3402 # Revision 1.398 2008/04/29 18:30:42 ncq 3403 # - promote workplace logging to info 3404 # 3405 # Revision 1.397 2008/04/28 13:32:39 ncq 3406 # - take approprate action on db maintenance warning 3407 # 3408 # Revision 1.396 2008/04/26 21:36:42 ncq 3409 # - fix faulty variable 3410 # - when debugging explicitely print into log window 3411 # immediately after creation so focus isn't taken 3412 # away at a later and inconvenient time 3413 # 3414 # Revision 1.395 2008/04/16 20:39:39 ncq 3415 # - working versions of the wxGlade code and use it, too 3416 # - show client version in login dialog 3417 # 3418 # Revision 1.394 2008/04/11 12:28:30 ncq 3419 # - abort if there's no user preferences config file whatsoever 3420 # 3421 # Revision 1.393 2008/03/29 16:09:53 ncq 3422 # - improved comments 3423 # - wx version checking for faulty 3424 # - enhance color db 3425 # - make sure at least one user preferences file candidate is writable 3426 # 3427 # Revision 1.392 2008/03/09 20:16:14 ncq 3428 # - load_patient_* -> get_person_* 3429 # 3430 # Revision 1.391 2008/03/06 18:34:08 ncq 3431 # - better error handling around IFAP access 3432 # 3433 # Revision 1.390 2008/03/05 22:38:26 ncq 3434 # - set encounter type to chart review on docs-only encounters 3435 # 3436 # Revision 1.389 2008/02/29 23:46:59 ncq 3437 # - new debugging option: widget inspector (needs 2.8) 3438 # 3439 # Revision 1.388 2008/02/25 17:37:16 ncq 3440 # - use new-style logging 3441 # 3442 # Revision 1.387 2008/01/30 14:07:49 ncq 3443 # - improved wording of partless document option 3444 # 3445 # Revision 1.386 2008/01/27 21:15:20 ncq 3446 # - configure partless docs 3447 # - label changes 3448 # - use gmCfg2 for setting options 3449 # 3450 # Revision 1.385 2008/01/22 12:23:39 ncq 3451 # - reorder menus as per list discussion 3452 # - wiki link/online user manual link in help menu 3453 # 3454 # Revision 1.384 2008/01/16 19:40:22 ncq 3455 # - menu item renaming "Upper lower" per Jim 3456 # - more config options 3457 # - add Aeskulap to DICOM viewers and better detection of those 3458 # 3459 # Revision 1.383 2008/01/13 01:19:11 ncq 3460 # - don't crash on inaccessible IFAP transfer file 3461 # - doc management configuration 3462 # - restore Stdio on exit 3463 # - set staff name for exception handling 3464 # 3465 # Revision 1.382 2008/01/07 19:53:00 ncq 3466 # - misspelled variable fix 3467 # 3468 # Revision 1.381 2008/01/05 22:30:30 ncq 3469 # - some wording cleanup for menu items 3470 # 3471 # Revision 1.380 2007/12/26 22:45:46 ncq 3472 # - tuples separate by , not : 3473 # 3474 # Revision 1.379 2007/12/23 22:03:59 ncq 3475 # - no more gmCLI 3476 # 3477 # Revision 1.378 2007/12/23 20:28:44 ncq 3478 # - use gmCfg2, less gmCLI use 3479 # - cleanup 3480 # - less guibroker use 3481 # 3482 # Revision 1.377 2007/12/12 16:24:32 ncq 3483 # - cleanup 3484 # 3485 # Revision 1.376 2007/12/11 12:49:26 ncq 3486 # - explicit signal handling 3487 # 3488 # Revision 1.375 2007/12/06 10:47:14 ncq 3489 # - submenu EMR -> History Taking 3490 # 3491 # Revision 1.374 2007/12/04 18:38:04 ncq 3492 # - edit occupation via menu 3493 # 3494 # Revision 1.373 2007/12/04 16:16:27 ncq 3495 # - use gmAuthWidgets 3496 # 3497 # Revision 1.372 2007/12/04 15:20:31 ncq 3498 # - assume default slave personality "gnumed-client" if not set 3499 # 3500 # Revision 1.371 2007/12/03 21:06:00 ncq 3501 # - streamline OnInit() 3502 # 3503 # Revision 1.370 2007/11/28 22:36:40 ncq 3504 # - listen on identity/name changes for current patient 3505 # 3506 # Revision 1.369 2007/11/23 23:33:50 ncq 3507 # - can now configure workplace plugins 3508 # 3509 # Revision 1.368 2007/11/03 17:57:19 ncq 3510 # - call hook on request_user_attention and app window actication/deactivation 3511 # - call hook on client init startup 3512 # - hence no more hardcoded checking external sources on startup 3513 # as users can do it from the hook if needed, hook example 3514 # updated thusly 3515 # - hence to check-sources-on-startup configuration needed 3516 # anymore 3517 # 3518 # Revision 1.367 2007/11/02 13:59:04 ncq 3519 # - teach client about its own version 3520 # - log client/db version 3521 # - a bunch of config options 3522 # - listen to request_user_attention 3523 # - listen to APP activation/deactivation 3524 # 3525 # Revision 1.366 2007/10/25 20:11:29 ncq 3526 # - configure initial plugin after patient search 3527 # 3528 # Revision 1.365 2007/10/25 16:41:04 ncq 3529 # - a whole bunch of config options 3530 # 3531 # Revision 1.364 2007/10/25 12:20:36 ncq 3532 # - improve db origination detection for signals in signal monitor 3533 # 3534 # Revision 1.363 2007/10/23 21:41:42 ncq 3535 # - on --debug monitor signals 3536 # 3537 # Revision 1.362 2007/10/23 21:25:32 ncq 3538 # - shutdown backend notification listener on exit 3539 # 3540 # Revision 1.361 2007/10/21 20:19:26 ncq 3541 # - add more config options 3542 # 3543 # Revision 1.360 2007/10/19 21:20:17 ncq 3544 # - init *all* image handler 3545 # 3546 # Revision 1.359 2007/10/19 12:51:39 ncq 3547 # - configure/do quick external patient search 3548 # - add Snellen chart 3549 # 3550 # Revision 1.358 2007/10/11 12:10:52 ncq 3551 # - add initial updateTitle() call 3552 # - reorganize menus a bit 3553 # - add gnumed / config / emr / encounter / edit-before-patient-change 3554 # - improve logic in encounter editor showing before patient change 3555 # 3556 # Revision 1.357 2007/10/08 12:49:48 ncq 3557 # - active_workplace now property of gmPractice 3558 # - rearrange options manage 3559 # - allow editing ifap startup command 3560 # 3561 # Revision 1.356 2007/09/20 21:30:39 ncq 3562 # - cleanup 3563 # - allow setting db logon banner 3564 # 3565 # Revision 1.355 2007/09/20 19:35:14 ncq 3566 # - somewhat cleanup exit code 3567 # 3568 # Revision 1.354 2007/09/17 21:46:51 ncq 3569 # - comment out unimplemented menu item 3570 # 3571 # Revision 1.353 2007/09/10 12:35:32 ncq 3572 # - cleanup 3573 # 3574 # Revision 1.352 2007/09/04 23:29:03 ncq 3575 # - slave mode now set via --slave inside login dialog 3576 # 3577 # Revision 1.351 2007/09/03 11:03:59 ncq 3578 # - enhanced error handling testing 3579 # 3580 # Revision 1.350 2007/08/31 23:04:40 ncq 3581 # - feedback on failing to write letter w/o active patient 3582 # 3583 # Revision 1.349 2007/08/29 14:40:41 ncq 3584 # - remove "activity" part from window title - we never started using it 3585 # - add menu item for managing paperwork templates 3586 # - no more singular get_choice_from_list() 3587 # - feedback on starting new encounter 3588 # 3589 # Revision 1.348 2007/08/12 00:09:07 ncq 3590 # - no more gmSignals.py 3591 # 3592 # Revision 1.347 2007/08/07 21:42:40 ncq 3593 # - cPaths -> gmPaths 3594 # 3595 # Revision 1.346 2007/07/22 10:47:48 ncq 3596 # - fix typo 3597 # 3598 # Revision 1.345 2007/07/22 10:04:49 ncq 3599 # - only allow new letter from menu if patient active 3600 # 3601 # Revision 1.344 2007/07/22 09:25:59 ncq 3602 # - support AMIDE DICOM viewer if installed 3603 # - menu "correspondence" with item "write letter" 3604 # - adjust to new get_choice_from_list() 3605 # 3606 # Revision 1.343 2007/07/17 21:43:50 ncq 3607 # - use refcounted patient lock 3608 # 3609 # Revision 1.342 2007/07/17 15:52:57 ncq 3610 # - display proper error message when starting the XML RPC server fails 3611 # 3612 # Revision 1.341 2007/07/17 13:52:12 ncq 3613 # - fix SQL query for db welcome message 3614 # 3615 # Revision 1.340 2007/07/17 13:42:13 ncq 3616 # - make displaying welcome message optional 3617 # 3618 # Revision 1.339 2007/07/11 21:09:05 ncq 3619 # - add lock/unlock patient 3620 # 3621 # Revision 1.338 2007/07/09 12:44:06 ncq 3622 # - make office menu accessible to plugins 3623 # 3624 # Revision 1.337 2007/06/28 12:37:22 ncq 3625 # - show proper title in caption line of main window 3626 # - improved menus 3627 # - allow signals to be delivered 3628 # 3629 # Revision 1.336 2007/06/11 20:30:46 ncq 3630 # - set expected database version to "devel" 3631 # 3632 # Revision 1.335 2007/06/10 10:18:37 ncq 3633 # - fix setting database language 3634 # 3635 # Revision 1.334 2007/05/21 14:48:58 ncq 3636 # - use export/EMR/pat['dirname'] 3637 # 3638 # Revision 1.333 2007/05/21 13:05:25 ncq 3639 # - catch-all wildcard on UNIX must be *, not *.* 3640 # 3641 # Revision 1.332 2007/05/18 10:14:50 ncq 3642 # - revert testing 3643 # 3644 # Revision 1.331 2007/05/18 10:14:22 ncq 3645 # - support OsiriX dicom viewer if available 3646 # - only enable dicom viewer menu item if a (known) viewer is available 3647 # (does not affect viewing from document system) 3648 # 3649 # Revision 1.330 2007/05/11 14:18:04 ncq 3650 # - put debugging stuff into submenue 3651 # 3652 # Revision 1.329 2007/05/08 16:06:03 ncq 3653 # - cleanup menu layout 3654 # - link to bug tracker on Savannah 3655 # - add exception handler test 3656 # - install/uninstall wxPython based exception display handler at appropriate times 3657 # 3658 # Revision 1.328 2007/05/08 11:15:41 ncq 3659 # - redirect stdio when debugging is enabled 3660 # 3661 # Revision 1.327 2007/05/07 12:35:20 ncq 3662 # - improve use of gmTools.cPaths() 3663 # 3664 # Revision 1.326 2007/05/07 08:04:13 ncq 3665 # - rename menu admin to office 3666 # 3667 # Revision 1.325 2007/04/27 13:29:08 ncq 3668 # - bump expected db version 3669 # 3670 # Revision 1.324 2007/04/25 22:01:25 ncq 3671 # - add database language configurator 3672 # 3673 # Revision 1.323 2007/04/19 13:12:51 ncq 3674 # - use gmTools.cPaths to use proper user prefs file 3675 # 3676 # Revision 1.322 2007/04/11 20:43:51 ncq 3677 # - cleanup 3678 # 3679 # Revision 1.321 2007/04/11 14:51:55 ncq 3680 # - use SetAppName() on App instance 3681 # 3682 # Revision 1.320 2007/04/02 18:40:58 ncq 3683 # - add menu item to start new encounter 3684 # 3685 # Revision 1.319 2007/04/01 15:28:14 ncq 3686 # - safely get_encoding() 3687 # 3688 # Revision 1.318 2007/03/26 16:09:50 ncq 3689 # - lots of statustext signal fixes 3690 # 3691 # Revision 1.317 2007/03/26 14:44:20 ncq 3692 # - eventually support flushing/backing up the log file 3693 # - add hook startup-after-GUI-init 3694 # 3695 # Revision 1.316 2007/03/23 16:42:46 ncq 3696 # - upon initial startup set focus to patient selector as requested by user ;-) 3697 # 3698 # Revision 1.315 2007/03/18 14:08:39 ncq 3699 # - add allergy handling 3700 # - disconnect statustext handler on shutdown 3701 # - run_hook_script() now in gmHooks.py 3702 # 3703 # Revision 1.314 2007/03/10 15:15:18 ncq 3704 # - anchor medical content links based on locale 3705 # 3706 # Revision 1.313 2007/03/09 16:58:13 ncq 3707 # - do not include encoding in GDT file name anymore, we now put it into the file itself 3708 # 3709 # Revision 1.312 2007/03/08 16:20:28 ncq 3710 # - typo fix 3711 # 3712 # Revision 1.311 2007/03/08 11:40:38 ncq 3713 # - setting client encoding now done directly from login function 3714 # - user preferences file now gnumed.conf again 3715 # 3716 # Revision 1.310 2007/03/02 15:40:42 ncq 3717 # - make ourselves a listener for gmSignals.statustext() 3718 # - decode() strftime() output to u'' 3719 # 3720 # Revision 1.309 2007/02/22 17:35:22 ncq 3721 # - add export as GDT 3722 # 3723 # Revision 1.308 2007/02/19 16:14:06 ncq 3724 # - use gmGuiHelpers.run_hook_script() 3725 # 3726 # Revision 1.307 2007/02/17 14:13:11 ncq 3727 # - gmPerson.gmCurrentProvider().workplace now property 3728 # 3729 # Revision 1.306 2007/02/09 15:01:14 ncq 3730 # - show consultation editor just before patient change if 3731 # either assessment of encounter is empty or the duration is zero 3732 # - if the duration is zero, then set last_affirmed to now() 3733 # 3734 # Revision 1.305 2007/02/04 17:30:08 ncq 3735 # - need to expand ~/ appropriately 3736 # 3737 # Revision 1.304 2007/01/30 17:53:29 ncq 3738 # - improved doc string 3739 # - cleanup 3740 # - use user preferences file for saving locale mismatch ignoring 3741 # 3742 # Revision 1.303 2007/01/24 11:04:53 ncq 3743 # - use global expected_db_ver and set it to "v5" 3744 # 3745 # Revision 1.302 2007/01/20 22:04:50 ncq 3746 # - run user script after patient activation 3747 # 3748 # Revision 1.301 2007/01/17 13:39:10 ncq 3749 # - show encounter summary editor before patient change 3750 # only if actually entered any data 3751 # 3752 # Revision 1.300 2007/01/15 13:06:49 ncq 3753 # - if we can "import webbrowser" we really shouldn't "gmShellAPI.run_command_in_shell('firefox')" 3754 # 3755 # Revision 1.299 2007/01/13 22:21:58 ncq 3756 # - try capturing the title bar, too, in snapshot() 3757 # 3758 # Revision 1.298 2007/01/09 18:02:46 ncq 3759 # - add jump_to_ifap() ready for being factored out 3760 # 3761 # Revision 1.297 2007/01/09 13:00:09 ncq 3762 # - wx.CallAfter(self._do_after_init) in OnInit() so we can properly order things 3763 # to do after init: we already check external patient sources 3764 # 3765 # Revision 1.296 2007/01/04 22:52:01 ncq 3766 # - accelerator key for "health issue" in EMR menu 3767 # 3768 # Revision 1.295 2006/12/27 16:44:02 ncq 3769 # - delay looking up of external patients on startup so we don't 3770 # fail the entire application if there's an error in that code 3771 # 3772 # Revision 1.294 2006/12/25 22:54:28 ncq 3773 # - add comment on prospective DICOM viewer behaviour 3774 # - link to firefox with URL of medical content links wiki page from knowledge menu 3775 # 3776 # Revision 1.293 2006/12/23 15:25:40 ncq 3777 # - use gmShellAPI 3778 # 3779 # Revision 1.292 2006/12/21 17:54:23 ncq 3780 # - cleanup 3781 # 3782 # Revision 1.291 2006/12/21 17:19:49 ncq 3783 # - need to do *something* in setup_platform, and be it "pass" 3784 # 3785 # Revision 1.290 2006/12/21 16:53:59 ncq 3786 # - init image handlers once for good 3787 # 3788 # Revision 1.289 2006/12/21 11:04:29 ncq 3789 # - ensureMinimal() is the proper way to go about ensuring a minimum wxPython version 3790 # - only set gmPG2 module global encoding if explicitely set by config file 3791 # - use more predefined wx.ID_*s and do away with module global wx.NewId() constants 3792 # - fix standalone startup to init gmI18N 3793 # 3794 # Revision 1.288 2006/12/18 12:59:24 ncq 3795 # - properly ensure minimum wxPython version, including unicode, 3796 # should now allow for 2.7, 2.8, gtk2, mac, msw 3797 # 3798 # Revision 1.287 2006/12/17 22:20:33 ncq 3799 # - accept wxPython > 2.6 3800 # 3801 # Revision 1.286 2006/12/15 15:26:21 ncq 3802 # - cleanup 3803 # 3804 # Revision 1.285 2006/12/15 15:25:01 ncq 3805 # - delete checking of database version to gmLogin.py where it belongs 3806 # 3807 # Revision 1.284 2006/12/13 15:01:35 ncq 3808 # - on_add_medication does not work yet 3809 # 3810 # Revision 1.283 2006/12/13 15:00:38 ncq 3811 # - import datetime 3812 # - we already have _provider so no need for on-the-spot gmPerson.gmCurrentProvider() 3813 # - improve menu item labels 3814 # - make transfer file and shell command configurable for ifap call 3815 # - snapshot name includes timestamp 3816 # 3817 # Revision 1.282 2006/12/06 16:08:44 ncq 3818 # - improved __on_ifap() to display return values in message box 3819 # 3820 # Revision 1.281 2006/12/05 14:00:16 ncq 3821 # - define expected db schema version 3822 # - improve schema hash checking 3823 # - add IFAP drug db link under "Knowledge" menu 3824 # 3825 # Revision 1.280 2006/12/01 13:58:12 ncq 3826 # - add screenshot function 3827 # 3828 # Revision 1.279 2006/11/24 14:22:57 ncq 3829 # - use shiny new health issue edit area 3830 # 3831 # Revision 1.278 2006/11/24 10:01:31 ncq 3832 # - gm_beep_statustext() -> gm_statustext() 3833 # 3834 # Revision 1.277 2006/11/20 17:26:46 ncq 3835 # - missing self. 3836 # 3837 # Revision 1.276 2006/11/20 16:04:08 ncq 3838 # - translate Help menu title 3839 # - move unlock mouse to tools menu 3840 # - comment out dermatology module from tools menu as there is no maintainer 3841 # 3842 # Revision 1.275 2006/11/19 11:15:13 ncq 3843 # - cannot wx.CallAfter() __on_pre_patient_selection() since 3844 # patient would have changed underhand 3845 # 3846 # Revision 1.274 2006/11/07 00:31:23 ncq 3847 # - remove some cruft 3848 # 3849 # Revision 1.273 2006/11/06 12:53:09 ncq 3850 # - lower severity of verbose part of "incompatible database warning" message 3851 # 3852 # Revision 1.272 2006/11/05 16:04:29 ncq 3853 # - add menu item GNUmed/Unlock mouse 3854 # 3855 # Revision 1.271 2006/10/31 12:39:54 ncq 3856 # - remove traces of gmPG 3857 # 3858 # Revision 1.270 2006/10/28 13:03:58 ncq 3859 # - check patient before calling wxCallAfter() in _pre_patient_selection 3860 # - strftime() doesn't take u'' 3861 # 3862 # Revision 1.269 2006/10/25 07:46:44 ncq 3863 # - Format() -> strftime() since datetime.datetime does not have .Format() 3864 # 3865 # Revision 1.268 2006/10/25 07:26:42 ncq 3866 # - make do without gmPG 3867 # 3868 # Revision 1.267 2006/10/24 13:24:12 ncq 3869 # - now use gmLogin.connect_to_database() 3870 # 3871 # Revision 1.266 2006/10/09 12:25:21 ncq 3872 # - almost entirely convert over to gmPG2 3873 # - rip out layout manager selection code 3874 # - better use of db level cfg 3875 # - default size now 800x600 3876 # 3877 # Revision 1.265 2006/08/11 13:10:08 ncq 3878 # - even if we cannot find wxversion still test for 2.6.x/unicode after 3879 # the fact and make very unhappy noises before drifting off into coma 3880 # 3881 # Revision 1.264 2006/08/06 20:04:02 ncq 3882 # - improve wxPython version checking and related messages 3883 # 3884 # Revision 1.263 2006/08/01 22:04:32 ncq 3885 # - call disable_identity() 3886 # 3887 # Revision 1.262 2006/07/30 18:47:19 ncq 3888 # - add load ext pat to patient menu 3889 # - prepare patient "deletion" from menu 3890 # 3891 # Revision 1.261 2006/07/24 11:30:02 ncq 3892 # - must set parent when loading external patients 3893 # 3894 # Revision 1.260 2006/07/21 21:34:58 ncq 3895 # - check for minimum required version/type of wxPython 3896 # 3897 # Revision 1.259 2006/07/18 21:17:21 ncq 3898 # - use gmPatSearchWidgets.load_patient_from_external_sources() 3899 # 3900 # Revision 1.258 2006/07/17 21:07:59 ncq 3901 # - create new patient from BDT file if not found 3902 # 3903 # Revision 1.257 2006/07/17 18:50:11 ncq 3904 # - upon startup activate patient read from xDT file if patient exists 3905 # 3906 # Revision 1.256 2006/07/17 10:53:50 ncq 3907 # - don't die on missing bdt file on startup 3908 # 3909 # Revision 1.255 2006/07/13 21:01:26 ncq 3910 # - display external patient on startup if XDT file available 3911 # 3912 # Revision 1.254 2006/07/07 12:09:00 ncq 3913 # - cleanup 3914 # - add document type editing to administrative menu 3915 # 3916 # Revision 1.253 2006/07/01 15:12:02 ncq 3917 # - set_curr_lang() failure has been downgraded to warning 3918 # 3919 # Revision 1.252 2006/07/01 11:32:13 ncq 3920 # - setting up database connection encoding now requires two encoding names 3921 # 3922 # Revision 1.251 2006/06/28 10:18:02 ncq 3923 # - only set gmPG default client encoding if actually set in the config file 3924 # 3925 # Revision 1.250 2006/06/13 20:35:46 ncq 3926 # - use localized date/time format taken from datetime library 3927 # 3928 # Revision 1.249 2006/06/10 05:12:42 ncq 3929 # - edit staff list 3930 # 3931 # Revision 1.248 2006/06/07 21:04:19 ncq 3932 # - fix re-setting DB lang to en_GB on failure to set preferred lang 3933 # 3934 # Revision 1.247 2006/06/06 20:48:31 ncq 3935 # - actually implement delisting staff member 3936 # 3937 # Revision 1.246 2006/06/06 10:22:23 ncq 3938 # - menu_office -> menu_administration 3939 # - menu_reference -> menu_knowledge 3940 # - cleanup 3941 # 3942 # Revision 1.245 2006/05/20 18:36:45 ncq 3943 # - reset DB language to EN on failing to set it to the user's locale 3944 # 3945 # Revision 1.244 2006/05/15 13:36:00 ncq 3946 # - signal cleanup: 3947 # - activating_patient -> pre_patient_selection 3948 # - patient_selected -> post_patient_selection 3949 # 3950 # Revision 1.243 2006/05/14 21:44:22 ncq 3951 # - add get_workplace() to gmPerson.gmCurrentProvider and make use thereof 3952 # - remove use of gmWhoAmI.py 3953 # 3954 # Revision 1.242 2006/05/14 18:09:05 ncq 3955 # - db_account -> db_user 3956 # 3957 # Revision 1.241 2006/05/12 12:20:38 ncq 3958 # - use gmCurrentProvider 3959 # - whoami -> whereami 3960 # 3961 # Revision 1.240 2006/05/10 13:08:37 ncq 3962 # - properly log physical screen size 3963 # 3964 # Revision 1.239 2006/05/06 18:50:43 ncq 3965 # - improve summary display after user complaint 3966 # 3967 # Revision 1.238 2006/05/04 17:52:04 ncq 3968 # - mark EMR summary for translation 3969 # 3970 # Revision 1.237 2006/05/04 09:49:20 ncq 3971 # - get_clinical_record() -> get_emr() 3972 # - adjust to changes in set_active_patient() 3973 # - need explicit set_active_patient() after ask_for_patient() if wanted 3974 # 3975 # Revision 1.236 2006/04/23 16:49:41 ncq 3976 # - add "Show EMR summary" as per list discussion 3977 # 3978 # Revision 1.235 2006/03/14 21:37:18 ncq 3979 # - add menu "Office" 3980 # - add menu item "add staff member" under "Office" serially calling new patient wizard and add staff dialog 3981 # - fix encounter summary 3982 # 3983 # Revision 1.234 2006/03/09 21:12:44 ncq 3984 # - allow current patient to be enlisted as staff from the main menu 3985 # 3986 # Revision 1.233 2006/02/27 22:38:36 ncq 3987 # - spell out rfe/aoe as per Richard's request 3988 # 3989 # Revision 1.232 2006/01/24 21:09:45 ncq 3990 # - use whoami.get_short_alias 3991 # 3992 # Revision 1.231 2006/01/15 14:29:44 ncq 3993 # - cleanup 3994 # 3995 # Revision 1.230 2006/01/09 20:27:04 ncq 3996 # - set_curr_lang() is in schema i18n, too 3997 # 3998 # Revision 1.229 2006/01/09 20:19:06 ncq 3999 # - adjust to i18n schema 4000 # 4001 # Revision 1.228 2006/01/03 12:12:03 ncq 4002 # - make epydoc happy re _() 4003 # 4004 # Revision 1.227 2005/12/27 18:54:50 ncq 4005 # - -> GNUmed 4006 # - enlarge About 4007 # - verify database on startup 4008 # - display database banner if it exists 4009 # 4010 # Revision 1.226 2005/12/14 17:01:51 ncq 4011 # - use improved db cfg option getting 4012 # 4013 # Revision 1.225 2005/11/29 18:59:41 ncq 4014 # - cleanup 4015 # 4016 # Revision 1.224 2005/11/27 20:20:46 ncq 4017 # - slave mode cfg return is string, not integer 4018 # 4019 # Revision 1.223 2005/11/18 15:23:23 ncq 4020 # - enable simple EMR search 4021 # 4022 # Revision 1.222 2005/11/06 11:10:42 ihaywood 4023 # dermtool proof-of-concept 4024 # Access from Tools|Dermatology menu item 4025 # A small range of derm pictures using free-as-in-speech sources are included. 4026 # 4027 # CVm: ---------------------------------------------------------------------- 4028 # 4029 # Revision 1.221 2005/10/12 22:32:22 ncq 4030 # - encounter['description'] -> encounter['aoe'] 4031 # 4032 # Revision 1.220 2005/10/08 12:37:25 sjtan 4033 # enc['description'] can return None 4034 # 4035 # Revision 1.219 2005/09/28 21:27:30 ncq 4036 # - a lot of wx2.6-ification 4037 # 4038 # Revision 1.218 2005/09/28 15:57:48 ncq 4039 # - a whole bunch of wx.Foo -> wx.Foo 4040 # 4041 # Revision 1.217 2005/09/27 20:44:58 ncq 4042 # - wx.wx* -> wx.* 4043 # 4044 # Revision 1.216 2005/09/26 18:01:50 ncq 4045 # - use proper way to import wx26 vs wx2.4 4046 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES 4047 # - time for fixup 4048 # 4049 # Revision 1.215 2005/09/24 09:17:28 ncq 4050 # - some wx2.6 compatibility fixes 4051 # 4052 # Revision 1.214 2005/09/11 17:34:10 ncq 4053 # - support consultation summary generation just before 4054 # switching to the next patient 4055 # 4056 # Revision 1.213 2005/09/04 07:30:24 ncq 4057 # - comment out search-patient menu item for now 4058 # 4059 # Revision 1.212 2005/07/24 18:57:48 ncq 4060 # - add "search" to "patient" menu - all it does is focus the search box ... 4061 # 4062 # Revision 1.211 2005/07/24 11:35:59 ncq 4063 # - use robustified gmTimer.Start() interface 4064 # 4065 # Revision 1.210 2005/07/11 09:05:31 ncq 4066 # - be more careful about failing to import wxPython 4067 # - make contributors list accessible from main menu 4068 # 4069 # Revision 1.209 2005/07/02 18:21:36 ncq 4070 # - GnuMed -> GNUmed 4071 # 4072 # Revision 1.208 2005/06/30 10:21:01 cfmoro 4073 # String corrections 4074 # 4075 # Revision 1.207 2005/06/30 10:10:08 cfmoro 4076 # String corrections 4077 # 4078 # Revision 1.206 2005/06/29 20:03:45 ncq 4079 # - cleanup 4080 # 4081 # Revision 1.205 2005/06/29 18:28:33 cfmoro 4082 # Minor fix 4083 # 4084 # Revision 1.204 2005/06/29 15:08:47 ncq 4085 # - some cleanup 4086 # - allow adding past history item from EMR menu 4087 # 4088 # Revision 1.203 2005/06/28 16:48:45 cfmoro 4089 # File dialog for journal and medistar EMR export 4090 # 4091 # Revision 1.202 2005/06/23 15:00:11 ncq 4092 # - cleanup 4093 # 4094 # Revision 1.201 2005/06/21 04:59:40 rterry 4095 # Fix to allow running gmAbout.py under wxpython26 wx.Size > wx.Size 4096 # 4097 # Revision 1.200 2005/06/19 16:38:03 ncq 4098 # - set encoding of gmGuiMain.py to latin1 4099 # 4100 # Revision 1.199 2005/06/13 21:41:29 ncq 4101 # - add missing function 4102 # 4103 # Revision 1.198 2005/06/12 22:16:22 ncq 4104 # - allow for explicitely setting timezone via config file 4105 # - cleanup, prepare for EMR search 4106 # 4107 # Revision 1.197 2005/06/07 20:52:49 ncq 4108 # - improved EMR menu structure 4109 # 4110 # Revision 1.196 2005/05/24 19:50:26 ncq 4111 # - make "patient" menu available globally 4112 # 4113 # Revision 1.195 2005/05/14 14:57:37 ncq 4114 # - activate new patient after creation 4115 # 4116 # Revision 1.194 2005/05/12 15:11:08 ncq 4117 # - add Medistar export menu item 4118 # 4119 # Revision 1.193 2005/04/28 21:29:58 ncq 4120 # - improve status bar 4121 # 4122 # Revision 1.192 2005/04/26 20:02:20 ncq 4123 # - proper call cNewPatientWizard 4124 # 4125 # Revision 1.191 2005/04/17 16:30:34 ncq 4126 # - improve menu structure 4127 # 4128 # Revision 1.190 2005/04/14 08:54:48 ncq 4129 # - comment out a display logging change that just might crash Richard 4130 # - add missing wx. prefix 4131 # 4132 # Revision 1.189 2005/04/12 18:33:29 cfmoro 4133 # typo fix 4134 # 4135 # Revision 1.188 2005/04/12 10:03:20 ncq 4136 # - slightly rearrange main menu 4137 # - add journal export function 4138 # - move to wx.* use 4139 # 4140 # Revision 1.187 2005/04/10 17:12:09 cfmoro 4141 # Added create patient menu option 4142 # 4143 # Revision 1.186 2005/04/03 20:12:12 ncq 4144 # - better wording in status line 4145 # - add menu "EMR" with "export" item and use gmEMRBrowser.export_emr_to_ascii() 4146 # 4147 # Revision 1.185 2005/04/02 20:45:12 cfmoro 4148 # Implementated exporting emr from gui client 4149 # 4150 # Revision 1.184 2005/03/29 07:27:54 ncq 4151 # - just silly cleanup 4152 # 4153 # Revision 1.183 2005/03/14 14:37:19 ncq 4154 # - attempt to log display settings 4155 # 4156 # Revision 1.182 2005/03/08 16:45:55 ncq 4157 # - properly handle title 4158 # 4159 # Revision 1.181 2005/03/06 14:50:45 ncq 4160 # - 'demographic record' -> get_identity() 4161 # 4162 # Revision 1.180 2005/02/13 15:28:07 ncq 4163 # - v_basic_person.i_pk -> pk_identity 4164 # 4165 # Revision 1.179 2005/02/12 13:58:20 ncq 4166 # - v_basic_person.i_id -> i_pk 4167 # 4168 # Revision 1.178 2005/02/03 20:19:16 ncq 4169 # - get_demographic_record() -> get_identity() 4170 # 4171 # Revision 1.177 2005/02/01 10:16:07 ihaywood 4172 # refactoring of gmDemographicRecord and follow-on changes as discussed. 4173 # 4174 # gmTopPanel moves to gmHorstSpace 4175 # gmRichardSpace added -- example code at present, haven't even run it myself 4176 # (waiting on some icon .pngs from Richard) 4177 # 4178 # Revision 1.176 2005/01/31 10:37:26 ncq 4179 # - gmPatient.py -> gmPerson.py 4180 # 4181 # Revision 1.175 2004/10/01 13:17:35 ncq 4182 # - eventually do what was intended on slave_mode != 1 4183 # 4184 # Revision 1.174 2004/10/01 11:49:59 ncq 4185 # - improve message on unset database language 4186 # 4187 # Revision 1.173 2004/09/13 09:36:43 ncq 4188 # - cleanup 4189 # - --slave -> 'main.slave_mode' 4190 # 4191 # Revision 1.172 2004/09/06 22:21:08 ncq 4192 # - properly use setDBParam() 4193 # - store sidebar.width if not found 4194 # 4195 # Revision 1.171 2004/09/05 14:47:24 ncq 4196 # - fix setDBParam() calls 4197 # 4198 # Revision 1.170 2004/08/20 13:34:48 ncq 4199 # - getFirstMatchingDBSet() -> getDBParam() 4200 # 4201 # Revision 1.169 2004/08/11 08:15:06 ncq 4202 # - log debugging info on why workplace appears to be list on Richard's machine 4203 # 4204 # Revision 1.168 2004/08/09 00:03:19 ncq 4205 # - Horst space layout manager factored out into its own file 4206 # 4207 # Revision 1.167 2004/08/04 17:16:02 ncq 4208 # - wxNotebookPlugin -> cNotebookPlugin 4209 # - derive cNotebookPluginOld from cNotebookPlugin 4210 # - make cNotebookPluginOld warn on use and implement old 4211 # explicit "main.notebook.raised_plugin"/ReceiveFocus behaviour 4212 # - ReceiveFocus() -> receive_focus() 4213 # 4214 # Revision 1.166 2004/07/28 15:40:05 ncq 4215 # - log wxWidgets version 4216 # 4217 # Revision 1.165 2004/07/24 17:21:49 ncq 4218 # - some cleanup, also re from wxPython import wx 4219 # - factored out Horst space layout manager into it's own 4220 # wx.Panel child class 4221 # - subsequently renamed 4222 # 'main.notebook.plugins' -> 'horstspace.notebook.pages' 4223 # 'modules.gui' -> 'horstspace.notebook.gui' (to be renamed horstspace.notebook.plugins later) 4224 # - adapt to said changes 4225 # 4226 # Revision 1.164 2004/07/24 10:26:35 ncq 4227 # - two missing event.Skip()s added 4228 # 4229 # Revision 1.163 2004/07/19 11:50:42 ncq 4230 # - cfg: what used to be called "machine" really is "workplace", so fix 4231 # 4232 # Revision 1.162 2004/07/18 19:54:44 ncq 4233 # - improved logging for page change/veto debugging 4234 # 4235 # Revision 1.161 2004/07/18 19:49:07 ncq 4236 # - cleanup, commenting, better logging 4237 # - preparation for inner-frame notebook layout manager arrival 4238 # - use Python True, not wxWidgets true, as Python tells us to do 4239 # 4240 # Revision 1.160 2004/07/15 18:41:22 ncq 4241 # - cautiously go back to previous notebook plugin handling 4242 # avoiding to remove too much of Ian's new work 4243 # - store window size across sessions 4244 # - try a trick for veto()ing failing notebook page changes on broken platforms 4245 # 4246 # Revision 1.159 2004/07/15 14:02:43 ncq 4247 # - refactored out __set_GUI_size() from TopLevelFrame.__init__() 4248 # so cleanup will be easier 4249 # - added comment on layout managers 4250 # 4251 # Revision 1.158 2004/07/15 07:57:20 ihaywood 4252 # This adds function-key bindings to select notebook tabs 4253 # (Okay, it's a bit more than that, I've changed the interaction 4254 # between gmGuiMain and gmPlugin to be event-based.) 4255 # 4256 # Oh, and SOAPTextCtrl allows Ctrl-Enter 4257 # 4258 # Revision 1.157 2004/06/26 23:09:22 ncq 4259 # - better comments 4260 # 4261 # Revision 1.156 2004/06/25 14:39:35 ncq 4262 # - make right-click runtime load/drop of plugins work again 4263 # 4264 # Revision 1.155 2004/06/25 12:51:23 ncq 4265 # - InstPlugin() -> instantiate_plugin() 4266 # 4267 # Revision 1.154 2004/06/25 12:37:20 ncq 4268 # - eventually fix the import gmI18N issue 4269 # 4270 # Revision 1.153 2004/06/23 20:53:30 ncq 4271 # - don't break the i18n epydoc fixup, if you don't understand it then ask 4272 # 4273 # Revision 1.152 2004/06/22 07:58:47 ihaywood 4274 # minor bugfixes 4275 # let gmCfg cope with config files that are not real files 4276 # 4277 # Revision 1.151 2004/06/21 16:06:54 ncq 4278 # - fix epydoc i18n fix 4279 # 4280 # Revision 1.150 2004/06/21 14:48:26 sjtan 4281 # 4282 # restored some methods that gmContacts depends on, after they were booted 4283 # out from gmDemographicRecord with no home to go , works again ; 4284 # removed cCatFinder('occupation') instantiating in main module scope 4285 # which was a source of complaint , as it still will lazy load anyway. 4286 # 4287 # Revision 1.149 2004/06/20 16:01:05 ncq 4288 # - please epydoc more carefully 4289 # 4290 # Revision 1.148 2004/06/20 06:49:21 ihaywood 4291 # changes required due to Epydoc's OCD 4292 # 4293 # Revision 1.147 2004/06/13 22:31:48 ncq 4294 # - gb['main.toolbar'] -> gb['main.top_panel'] 4295 # - self.internal_name() -> self.__class__.__name__ 4296 # - remove set_widget_reference() 4297 # - cleanup 4298 # - fix lazy load in _on_patient_selected() 4299 # - fix lazy load in ReceiveFocus() 4300 # - use self._widget in self.GetWidget() 4301 # - override populate_with_data() 4302 # - use gb['main.notebook.raised_plugin'] 4303 # 4304 # Revision 1.146 2004/06/01 07:59:55 ncq 4305 # - comments improved 4306 # 4307 # Revision 1.145 2004/05/15 15:51:03 sjtan 4308 # 4309 # hoping to link this to organization widget. 4310 # 4311 # Revision 1.144 2004/03/25 11:03:23 ncq 4312 # - getActiveName -> get_names 4313 # 4314 # Revision 1.143 2004/03/12 13:22:02 ncq 4315 # - fix imports 4316 # 4317 # Revision 1.142 2004/03/04 19:46:54 ncq 4318 # - switch to package based import: from Gnumed.foo import bar 4319 # 4320 # Revision 1.141 2004/03/03 23:53:22 ihaywood 4321 # GUI now supports external IDs, 4322 # Demographics GUI now ALPHA (feature-complete w.r.t. version 1.0) 4323 # but happy to consider cosmetic changes 4324 # 4325 # Revision 1.140 2004/02/18 14:00:56 ncq 4326 # - moved encounter handling to gmClinicalRecord.__init__() 4327 # 4328 # Revision 1.139 2004/02/12 23:55:34 ncq 4329 # - different title bar on --slave 4330 # 4331 # Revision 1.138 2004/02/05 23:54:11 ncq 4332 # - wxCallAfter() 4333 # - start/stop scripting listener 4334 # 4335 # Revision 1.137 2004/01/29 16:12:18 ncq 4336 # - add check for DB account to staff member mapping 4337 # 4338 # Revision 1.136 2004/01/18 21:52:20 ncq 4339 # - stop backend listeners in clean_exit() 4340 # 4341 # Revision 1.135 2004/01/06 10:05:21 ncq 4342 # - question dialog on continuing previous encounter was incorrect 4343 # 4344 # Revision 1.134 2004/01/04 09:33:32 ihaywood 4345 # minor bugfixes, can now create new patients, but doesn't update properly 4346 # 4347 # Revision 1.133 2003/12/29 23:32:56 ncq 4348 # - reverted tolerance to missing db account <-> staff member mapping 4349 # - added comment as to why 4350 # 4351 # Revision 1.132 2003/12/29 20:44:16 uid67323 4352 # -fixed the bug that made gnumed crash if no staff entry was available for the current user 4353 # 4354 # Revision 1.131 2003/12/29 16:56:00 uid66147 4355 # - current user now handled by whoami 4356 # - updateTitle() has only one parameter left: anActivity, the others can be derived 4357 # 4358 # Revision 1.130 2003/11/30 01:09:10 ncq 4359 # - use gmGuiHelpers 4360 # 4361 # Revision 1.129 2003/11/29 01:33:23 ncq 4362 # - a bit of streamlining 4363 # 4364 # Revision 1.128 2003/11/21 19:55:32 hinnef 4365 # re-included patch from 1.116 that was lost before 4366 # 4367 # Revision 1.127 2003/11/19 14:45:32 ncq 4368 # - re-decrease excess logging on plugin load failure which 4369 # got dropped in Syans recent commit 4370 # 4371 # Revision 1.126 2003/11/19 01:22:24 ncq 4372 # - some cleanup, some local vars renamed 4373 # 4374 # Revision 1.125 2003/11/19 01:01:17 shilbert 4375 # - fix for new demographic API got lost 4376 # 4377 # Revision 1.124 2003/11/17 10:56:38 sjtan 4378 # 4379 # synced and commiting. 4380 # 4381 # Revision 1.123 2003/11/11 18:22:18 ncq 4382 # - fix longstanding bug in plugin loader error handler (duh !) 4383 # 4384 # Revision 1.122 2003/11/09 17:37:12 shilbert 4385 # - ['demographics'] -> ['demographic record'] 4386 # 4387 # Revision 1.121 2003/10/31 23:23:17 ncq 4388 # - make "attach to encounter ?" dialog more informative 4389 # 4390 # Revision 1.120 2003/10/27 15:53:10 ncq 4391 # - getDOB has changed 4392 # 4393 # Revision 1.119 2003/10/26 17:39:00 ncq 4394 # - cleanup 4395 # 4396 # Revision 1.118 2003/10/26 11:27:10 ihaywood 4397 # gmPatient is now the "patient stub", all demographics stuff in gmDemographics. 4398 # 4399 # syncing with main tree. 4400 # 4401 # Revision 1.1 2003/10/23 06:02:39 sjtan 4402 # 4403 # manual edit areas modelled after r.terry's specs. 4404 # 4405 # Revision 1.116 2003/10/22 21:34:42 hinnef 4406 # -changed string array for main.window.size into two separate integer parameters 4407 # 4408 # Revision 1.115 2003/10/19 12:17:16 ncq 4409 # - just cleanup 4410 # 4411 # Revision 1.114 2003/10/13 21:00:29 hinnef 4412 # -added main.window.size config parameter (will be set on startup) 4413 # 4414 # Revision 1.113 2003/09/03 17:32:41 hinnef 4415 # make use of gmWhoAmI 4416 # 4417 # Revision 1.112 2003/07/21 21:05:56 ncq 4418 # - actually set database client encoding from config file, warn if missing 4419 # 4420 # Revision 1.111 2003/07/07 08:34:31 ihaywood 4421 # bugfixes on gmdrugs.sql for postgres 7.3 4422 # 4423 # Revision 1.110 2003/06/26 22:28:50 ncq 4424 # - need to define self.nb before using it 4425 # - reordered __init__ for clarity 4426 # 4427 # Revision 1.109 2003/06/26 21:38:28 ncq 4428 # - fatal->verbose 4429 # - ignore system-to-database locale mismatch if user so desires 4430 # 4431 # Revision 1.108 2003/06/25 22:50:30 ncq 4432 # - large cleanup 4433 # - lots of comments re method call order on application closing 4434 # - send application_closing() from _clean_exit() 4435 # - add OnExit() handler, catch/log session management events 4436 # - add helper __show_question() 4437 # 4438 # Revision 1.107 2003/06/24 12:55:40 ncq 4439 # - typo: it's qUestion, not qestion 4440 # 4441 # Revision 1.106 2003/06/23 22:29:59 ncq 4442 # - in on_patient_selected() add code to attach to a 4443 # previous encounter or create one if necessary 4444 # - show_error/quesion() helper 4445 # 4446 # Revision 1.105 2003/06/19 15:27:53 ncq 4447 # - also process wx.EVT_NOTEBOOK_PAGE_CHANGING 4448 # - veto() page change if can_receive_focus() is false 4449 # 4450 # Revision 1.104 2003/06/17 22:30:41 ncq 4451 # - some cleanup 4452 # 4453 # Revision 1.103 2003/06/10 09:55:34 ncq 4454 # - don't import handler_loader anymore 4455 # 4456 # Revision 1.102 2003/06/01 14:34:47 sjtan 4457 # 4458 # hopefully complies with temporary model; not using setData now ( but that did work). 4459 # Please leave a working and tested substitute (i.e. select a patient , allergy list 4460 # will change; check allergy panel allows update of allergy list), if still 4461 # not satisfied. I need a working model-view connection ; trying to get at least 4462 # a basically database updating version going . 4463 # 4464 # Revision 1.101 2003/06/01 12:36:40 ncq 4465 # - no way cluttering INFO level log files with arbitrary patient data 4466 # 4467 # Revision 1.100 2003/06/01 01:47:33 sjtan 4468 # 4469 # starting allergy connections. 4470 # 4471 # Revision 1.99 2003/05/12 09:13:31 ncq 4472 # - SQL ends with ";", cleanup 4473 # 4474 # Revision 1.98 2003/05/10 18:47:08 hinnef 4475 # - set 'currentUser' in GuiBroker-dict 4476 # 4477 # Revision 1.97 2003/05/03 14:16:33 ncq 4478 # - we don't use OnIdle(), so don't hook it 4479 # 4480 # Revision 1.96 2003/04/28 12:04:09 ncq 4481 # - use plugin.internal_name() 4482 # 4483 # Revision 1.95 2003/04/25 13:03:07 ncq 4484 # - just some silly whitespace fix 4485 # 4486 # Revision 1.94 2003/04/08 21:24:14 ncq 4487 # - renamed gmGP_Toolbar -> gmTopPanel 4488 # 4489 # Revision 1.93 2003/04/04 20:43:47 ncq 4490 # - take advantage of gmCurrentPatient() 4491 # 4492 # Revision 1.92 2003/04/03 13:50:21 ncq 4493 # - catch more early results of connection failures ... 4494 # 4495 # Revision 1.91 2003/04/01 15:55:24 ncq 4496 # - fix setting of db lang, better message if no lang set yet 4497 # 4498 # Revision 1.90 2003/04/01 12:26:04 ncq 4499 # - add menu "Reference" 4500 # 4501 # Revision 1.89 2003/03/30 00:24:00 ncq 4502 # - typos 4503 # - (hopefully) less confusing printk()s at startup 4504 # 4505 # Revision 1.88 2003/03/29 14:12:35 ncq 4506 # - set minimum size to 320x240 4507 # 4508 # Revision 1.87 2003/03/29 13:48:42 ncq 4509 # - cleanup, clarify, improve sizer use 4510 # 4511 # Revision 1.86 2003/03/24 17:15:05 ncq 4512 # - slightly speed up startup by using pre-calculated system_locale_level dict 4513 # 4514 # Revision 1.85 2003/03/23 11:46:14 ncq 4515 # - remove extra debugging statements 4516 # 4517 # Revision 1.84 2003/02/17 16:20:38 ncq 4518 # - streamline imports 4519 # - comment out app_init signal dispatch since it breaks 4520 # 4521 # Revision 1.83 2003/02/14 00:05:36 sjtan 4522 # 4523 # generated files more usable. 4524 # 4525 # Revision 1.82 2003/02/13 08:21:18 ihaywood 4526 # bugfix for MSW 4527 # 4528 # Revision 1.81 2003/02/12 23:45:49 sjtan 4529 # 4530 # removing dead code. 4531 # 4532 # Revision 1.80 2003/02/12 23:37:58 sjtan 4533 # 4534 # now using gmDispatcher and gmSignals for initialization and cleanup. 4535 # Comment out the import handler_loader in gmGuiMain will restore back 4536 # to prototype GUI stage. 4537 # 4538 # Revision 1.79 2003/02/11 12:21:19 sjtan 4539 # 4540 # one more dependency formed , at closing , to implement saving of persistence objects. 4541 # this should be temporary, if a periodic save mechanism is implemented 4542 # 4543 # Revision 1.78 2003/02/09 20:02:55 ncq 4544 # - rename main.notebook.numbers to main.notebook.plugins 4545 # 4546 # Revision 1.77 2003/02/09 12:44:43 ncq 4547 # - fixed my typo 4548 # 4549 # Revision 1.76 2003/02/09 09:47:38 sjtan 4550 # 4551 # handler loading placed here. 4552 # 4553 # Revision 1.75 2003/02/09 09:05:30 michaelb 4554 # renamed 'icon_gui_main' to 'icon_serpent', added icon to loading-plugins-progress-dialog box 4555 # 4556 # Revision 1.74 2003/02/07 22:57:59 ncq 4557 # - fixed extra (% cmd) 4558 # 4559 # Revision 1.73 2003/02/07 14:30:33 ncq 4560 # - setting the db language now works 4561 # 4562 # Revision 1.72 2003/02/07 08:57:39 ncq 4563 # - fixed type 4564 # 4565 # Revision 1.71 2003/02/07 08:37:13 ncq 4566 # - fixed some fallout from SJT's work 4567 # - don't die if select lang from i18n_curr_lang returns None 4568 # 4569 # Revision 1.70 2003/02/07 05:13:59 sjtan 4570 # 4571 # took out the myLog temporary so not broken when I'm running to see if hooks work. 4572 # 4573 # Revision 1.69 2003/02/06 14:02:47 ncq 4574 # - some more logging to catch the set_db_lang problem 4575 # 4576 # Revision 1.68 2003/02/06 12:44:06 ncq 4577 # - curr_locale -> system_locale 4578 # 4579 # Revision 1.67 2003/02/05 12:15:01 ncq 4580 # - not auto-sets the database level language if so desired and possible 4581 # 4582 # Revision 1.66 2003/02/02 09:11:19 ihaywood 4583 # gmDemographics will connect, search and emit patient_selected 4584 # 4585 # Revision 1.65 2003/02/01 21:59:42 michaelb 4586 # moved 'About GnuMed' into module; gmGuiMain version no longer displayed in about box 4587 # 4588 # Revision 1.64 2003/02/01 11:57:56 ncq 4589 # - display gmGuiMain version in About box 4590 # 4591 # Revision 1.63 2003/02/01 07:10:50 michaelb 4592 # fixed scrolling problem 4593 # 4594 # Revision 1.61 2003/01/29 04:26:37 michaelb 4595 # removed import images_gnuMedGP_TabbedLists.py 4596 # 4597 # Revision 1.60 2003/01/14 19:36:04 ncq 4598 # - frame.Maximize() works on Windows ONLY 4599 # 4600 # Revision 1.59 2003/01/14 09:10:19 ncq 4601 # - maybe icons work better now ? 4602 # 4603 # Revision 1.58 2003/01/13 06:30:16 michaelb 4604 # the serpent window-icon was added 4605 # 4606 # Revision 1.57 2003/01/12 17:31:10 ncq 4607 # - catch failing plugins better 4608 # 4609 # Revision 1.56 2003/01/12 01:46:57 ncq 4610 # - coding style cleanup 4611 # 4612 # Revision 1.55 2003/01/11 22:03:30 hinnef 4613 # removed gmConf 4614 # 4615 # Revision 1.54 2003/01/05 10:03:30 ncq 4616 # - code cleanup 4617 # - use new plugin config storage infrastructure 4618 # 4619 # Revision 1.53 2003/01/04 07:43:55 ihaywood 4620 # Popup menus on notebook tabs 4621 # 4622 # Revision 1.52 2002/12/26 15:50:39 ncq 4623 # - title bar fine-tuning 4624 # 4625 # Revision 1.51 2002/11/30 11:09:55 ncq 4626 # - refined title bar 4627 # - comments 4628 # 4629 # Revision 1.50 2002/11/13 10:07:25 ncq 4630 # - export updateTitle() via guibroker 4631 # - internally set title according to template 4632 # 4633 # Revision 1.49 2002/11/12 21:24:51 hherb 4634 # started to use dispatcher signals 4635 # 4636 # Revision 1.48 2002/11/09 18:14:38 hherb 4637 # Errors / delay caused by loading plugin progess bar fixed 4638 # 4639 # Revision 1.47 2002/09/30 10:57:56 ncq 4640 # - make GnuMed consistent spelling in user-visible strings 4641 # 4642 # Revision 1.46 2002/09/26 13:24:15 ncq 4643 # - log version 4644 # 4645 # Revision 1.45 2002/09/12 23:21:38 ncq 4646 # - fix progress bar 4647 # 4648 # Revision 1.44 2002/09/10 12:25:33 ncq 4649 # - gimmicks rule :-) 4650 # - display plugin_nr/nr_of_plugins on load in progress bar 4651 # 4652 # Revision 1.43 2002/09/10 10:26:03 ncq 4653 # - properly i18n() strings 4654 # 4655 # Revision 1.42 2002/09/10 09:08:49 ncq 4656 # - set a useful window title and add a comment regarding this item 4657 # 4658 # Revision 1.41 2002/09/09 10:07:48 ncq 4659 # - long initial string so module names fit into progress bar display 4660 # 4661 # Revision 1.40 2002/09/09 00:52:55 ncq 4662 # - show progress bar on plugin load :-) 4663 # 4664 # Revision 1.39 2002/09/08 23:17:37 ncq 4665 # - removed obsolete reference to gmLogFrame.py 4666 # 4667 # @change log: 4668 # 10.06.2001 hherb initial implementation, untested 4669 # 01.11.2001 hherb comments added, modified for distributed servers 4670 # make no mistake: this module is still completely useless! 4671