1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
|
"""gmPlugin - base classes for GnuMed Horst space notebook plugins.
@copyright: author
"""
#==================================================================
# $Source: /sources/gnumed/gnumed/gnumed/client/wxpython/gmPlugin.py,v $
# $Id: gmPlugin.py,v 1.75 2007/11/23 23:35:47 ncq Exp $
__version__ = "$Revision: 1.75 $"
__author__ = "H.Herb, I.Haywood, K.Hilbert"
__license__ = 'GPL (details at http://www.gnu.org)'
import os, sys, re, glob
import wx
if __name__ == '__main__':
sys.path.insert(0, '../../')
from Gnumed.pycommon import gmExceptions, gmGuiBroker, gmLog, gmCfg, gmDispatcher, gmSignals, gmTools
from Gnumed.business import gmPerson, gmSurgery
_log = gmLog.gmDefLog
_log.Log(gmLog.lInfo, __version__)
#==============================================================================
class cLoadProgressBar (wx.ProgressDialog):
def __init__(self, nr_plugins):
wx.ProgressDialog.__init__(
self,
title = _("GNUmed: configuring [%s] (%s plugins)") % (gmSurgery.gmCurrentPractice().active_workplace, nr_plugins),
message = _("loading list of plugins "),
maximum = nr_plugins,
parent = None,
style = wx.PD_ELAPSED_TIME
)
# set window icon
paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
png_fname = os.path.join(paths.system_app_data_dir, 'bitmaps', 'serpent.png')
icon = wx.EmptyIcon()
try:
icon.LoadFile(png_fname, wx.BITMAP_TYPE_PNG)
except:
_log.Log(gmLog.lWarn, 'wx.Icon.LoadFile() not supported')
self.SetIcon(icon)
self.idx = 0
self.nr_plugins = nr_plugins
self.prev_plugin = ""
#----------------------------------------------------------
def Update (self, result, plugin):
if result == -1:
result = ""
elif result == 0:
result = _("failed")
else:
result = _("success")
wx.ProgressDialog.Update (self,
self.idx,
_("previous: %s (%s)\ncurrent (%s/%s): %s") % (
self.prev_plugin,
result,
(self.idx+1),
self.nr_plugins,
plugin))
self.prev_plugin = plugin
self.idx += 1
#==================================================================
# This is for NOTEBOOK plugins. Please write other base
# classes for other types of plugins.
#==================================================================
class cNotebookPlugin:
"""Base class for plugins which provide a full notebook page.
"""
def __init__(self):
self.gb = gmGuiBroker.GuiBroker()
self._set = 'gui'
self._widget = None
self.__register_events()
#-----------------------------------------------------
# plugin load API
#-----------------------------------------------------
def register(self):
"""Register ourselves with the main notebook widget."""
_log.Log(gmLog.lInfo, "set: [%s] class: [%s] name: [%s]" % (self._set, self.__class__.__name__, self.name()))
# create widget
nb = self.gb['horstspace.notebook']
widget = self.GetWidget(nb)
# create toolbar
top_panel = self.gb['horstspace.top_panel']
tb = top_panel.CreateBar()
self.populate_toolbar(tb, widget)
tb.Realize()
# create menu item
menu_info = self.MenuInfo()
# place bar in top panel
# (pages that don't want a toolbar must install a blank one
# otherwise the previous page's toolbar would be visible)
top_panel.AddBar(key=self.__class__.__name__, bar=tb)
self.gb['toolbar.%s' % self.__class__.__name__] = tb
# add ourselves to the main notebook
nb.AddPage(widget, self.name())
# and put ourselves into the menu structure if so
if menu_info is not None:
name_of_menu, menu_item_name = menu_info
menu = self.gb['main.%smenu' % name_of_menu]
self.menu_id = wx.NewId()
# FIXME: this shouldn't be self.name() but rather self.menu_help_string()
menu.Append (self.menu_id, menu_item_name, self.name()) # (id, item name, help string)
wx.EVT_MENU (self.gb['main.frame'], self.menu_id, self._on_raise_by_menu)
# so notebook can find this widget
self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__] = self
self.gb['horstspace.notebook.pages'].append(self)
return True
#-----------------------------------------------------
def unregister(self):
"""Remove ourselves."""
del self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__]
_log.Log(gmLog.lInfo, "plugin: [%s] (class: [%s]) set: [%s]" % (self.name(), self.__class__.__name__, self._set))
# delete menu item
menu_info = self.MenuInfo()
if menu_info is not None:
menu = self.gb['main.%smenu' % menu_info[0]]
menu.Delete(self.menu_id)
# delete toolbar
top_panel = self.gb['main.top_panel']
top_panel.DeleteBar(self.__class__.__name__)
# correct the notebook page list
nb_pages = self.gb['horstspace.notebook.pages']
nb_page_num = nb_pages.index(self)
del nb_pages[nb_page_num]
# delete notebook page
nb = self.gb['horstspace.notebook']
nb.DeletePage(nb_page_num)
#-----------------------------------------------------
def name(self):
return 'plugin %s' % self.__class__.__name__
#-----------------------------------------------------
def MenuInfo(self):
"""Return tuple of (menuname, menuitem).
None: no menu entry wanted
"""
return None
#-----------------------------------------------------
def populate_toolbar (self, tb, widget):
"""Populates the toolbar for this widget.
- tb is the toolbar to populate
- widget is the widget returned by GetWidget() # FIXME: is this really needed ?
"""
pass
#-----------------------------------------------------
# activation API
#-----------------------------------------------------
def can_receive_focus(self):
"""Called when this plugin is *about to* receive focus.
If None returned from here (or from overriders) the
plugin activation will be veto()ed (if it can be).
"""
# FIXME: fail if locked
return True
#-----------------------------------------------------
def receive_focus(self):
"""We *are* receiving focus via wx.EVT_NotebookPageChanged.
This can be used to populate the plugin widget on receiving focus.
"""
if hasattr(self._widget, 'repopulate_ui'):
self._widget.repopulate_ui()
# else apparently it doesn't need it
return True
#-----------------------------------------------------
def _verify_patient_avail(self):
"""Check for patient availability.
- convenience method for your can_receive_focus() handlers
"""
# fail if no patient selected
pat = gmPerson.gmCurrentPatient()
if not pat.is_connected():
# FIXME: people want an optional red backgound here
gmDispatcher.send('statustext', msg = _('Cannot switch to [%s]: no patient selected') % self.name())
return None
return 1
#-----------------------------------------------------
def Raise(self):
"""Raise ourselves."""
nb_pages = self.gb['horstspace.notebook.pages']
plugin_page = nb_pages.index(self)
nb = self.gb['horstspace.notebook']
nb.SetSelection(plugin_page)
return True
#-----------------------------------------------------
def _on_raise_by_menu(self, event):
if not self.can_receive_focus():
return False
self.Raise()
return True
#-----------------------------------------------------
def _on_raise_by_signal(self, **kwds):
# does this signal concern us ?
if kwds['name'] != self.__class__.__name__:
return False
return self._on_raise_by_menu(None)
# -----------------------------------------------------
# event handlers for the popup window
def on_load (self, evt):
# FIXME: talk to the configurator so we're loaded next time
self.register()
# FIXME: raise ?
# -----------------------------------------------------
def OnShow (self, evt):
self.register() # register without changing configuration
# -----------------------------------------------------
def __register_events(self):
gmDispatcher.connect(signal = 'display_widget', receiver = self._on_raise_by_signal)
#==================================================================
class cPatientChange_PluginMixin:
"""This mixin adds listening to patient change signals."""
def __init__(self):
gmDispatcher.connect(self._pre_patient_selection, gmSignals.pre_patient_selection())
gmDispatcher.connect(self._post_patient_selection, gmSignals.post_patient_selection())
# -----------------------------------------------------
def _pre_patient_selection(self, **kwds):
print "%s._pre_patient_selection() not implemented" % self.__class__.__name__
print "should usually be used to commit unsaved data"
# -----------------------------------------------------
def _post_patient_selection(self, **kwds):
print "%s._post_patient_selection() not implemented" % self.__class__.__name__
print "should usually be used to initialize state"
#==================================================================
class cNotebookPluginOld(cNotebookPlugin):
def __init__(self, set=None):
print "%s: class cNotebookPluginOld used, please convert" % self.__class__.__name__
cNotebookPlugin.__init__(self)
# make sure there's a raised_plugin entry
try:
tmp = self.gb['main.notebook.raised_plugin']
except KeyError:
self.gb['main.notebook.raised_plugin'] = 'none'
#-----------------------------------------------------
def populate_with_data(self):
_log.Log(gmLog.lInfo, '%s: outdated populate_with_data() missing' % self.__class__.__name__)
#-----------------------------------------------------
def receive_focus(self):
"""We *are* receiving focus now."""
self.gb['main.notebook.raised_plugin'] = self.__class__.__name__
self.populate_with_data()
#-----------------------------------------------------
def Raise(self):
"""Raise ourselves."""
# already raised ?
if self.gb['main.notebook.raised_plugin'] == self.__class__.__name__:
return True
cNotebookPlugin.Raise(self)
return True
#==================================================================
# some convenience functions
#------------------------------------------------------------------
def raise_notebook_plugin(plugin_name = None):
"""plugin_name is a plugin internal name"""
print "gmPlugin.raise_notebook_plugin() deprecated, use <display_widget> signal instead"
gb = gmGuiBroker.GuiBroker()
try:
plugin = gb['horstspace.notebook.gui'][plugin_name]
except KeyError:
_log.LogException("cannot raise [%s], plugin not available" % plugin_name, verbose=0)
return None
if plugin.can_receive_focus():
plugin.Raise()
return True
return False
#------------------------------------------------------------------
def __gm_import(module_name):
"""Import a module.
I am not sure *why* we need this. But the docs
and Google say so. It's got something to do with
package imports returning the toplevel package name."""
try:
mod = __import__(module_name)
except ImportError:
_log.LogException ('Cannot __import__() module [%s].' % module_name, verbose=0)
return None
components = module_name.split('.')
for component in components[1:]:
mod = getattr(mod, component)
return mod
#------------------------------------------------------------------
def instantiate_plugin(aPackage='xxxDEFAULTxxx', plugin_name='xxxDEFAULTxxx'):
"""Instantiates a plugin object from a package directory, returning the object.
NOTE: it does NOT call register() for you !!!!
- "set" specifies the subdirectory in which to find the plugin
- this knows nothing of databases, all it does is instantiate a named plugin
There will be a general 'gui' directory for large GUI
components: prescritions, etc., then several others for more
specific types: export/import filters, crypto algorithms
guibroker, dbbroker are broker objects provided
defaults are the default set of plugins to be loaded
FIXME: we should inform the user about failing plugins
"""
# we do need brokers, else we are useless
gb = gmGuiBroker.GuiBroker()
# bean counting ! -> loaded plugins
if not ('horstspace.notebook.%s' % aPackage) in gb.keylist():
gb['horstspace.notebook.%s' % aPackage] = {}
if not 'horstspace.notebook.pages' in gb.keylist():
gb['horstspace.notebook.pages'] = []
module_from_package = __gm_import('Gnumed.wxpython.%s.%s' % (aPackage, plugin_name))
# find name of class of plugin (must be the same as the plugin module filename)
plugin_class = module_from_package.__dict__[plugin_name]
if not issubclass(plugin_class, cNotebookPlugin):
_log.Log(gmLog.lErr, "[%s] not a subclass of cNotebookPlugin" % plugin_name)
return None
_log.Log(gmLog.lInfo, plugin_name)
try:
plugin = plugin_class()
except:
_log.LogException ('Cannot open module "%s.%s".' % (aPackage, plugin_name), verbose=0)
return None
return plugin
#------------------------------------------------------------------
def get_installed_plugins(plugin_dir=''):
"""Looks for installed plugins in the filesystem.
The first directory in sys.path which contains a wxpython/gui/
is considered the one -- because that's where the import will
get it from.
"""
search_path = None
for path in sys.path:
tmp = os.path.join(path, 'Gnumed', 'wxpython', plugin_dir)
if os.path.exists(tmp):
search_path = tmp
break
if search_path is None:
_log.Log(gmLog.lErr, 'unable to find any candidate directory matching [$candidate/Gnumed/wxpython/%s/]' % plugin_dir)
_log.Log(gmLog.lErr, 'candidates: %s' % str(sys.path))
return []
_log.Log(gmLog.lInfo, "scanning plugin directory [%s]" % search_path)
files = glob.glob(os.path.join(search_path, 'gm*.py'))
plugins = []
for file in files:
path, fname = os.path.split(file)
mod_name, ext = os.path.splitext(fname)
plugins.append(mod_name)
_log.Log(gmLog.lData, "plugins found: %s" % str(plugins))
return plugins
#------------------------------------------------------------------
def GetPluginLoadList(option, plugin_dir = '', defaults = None, workplace=None):
"""Get a list of plugins to load.
1) from database if option is not None
2) from list of defaults
3) if 2 is None, from source directory (then stored in database)
FIXME: look at gmRichardSpace to see how to load plugins
FIXME: NOT from files in directories (important for py2exe)
"""
if workplace is None:
workplace = gmSurgery.gmCurrentPractice().active_workplace
p_list = None
if option is not None:
dbcfg = gmCfg.cCfgSQL()
p_list = dbcfg.get2 (
option = option,
workplace = workplace,
bias = 'workplace',
default = defaults
)
if p_list is not None:
return p_list
if defaults is None:
p_list = get_installed_plugins(plugin_dir = plugin_dir)
if (len(p_list) == 0):
_log.Log(gmLog.lErr, 'cannot find plugins by scanning plugin directory ?!?')
return defaults
else:
p_list = defaults
# store for current user/current workplace
dbcfg.set (
option = option,
value = p_list,
workplace = workplace
)
_log.Log(gmLog.lData, "plugin load list stored: %s" % str(p_list))
return p_list
#------------------------------------------------------------------
def UnloadPlugin (set, name):
"""
Unloads the named plugin
"""
gb = gmGuiBroker.GuiBroker()
plugin = gb['horstspace.notebook.%s' % set][name]
plugin.unregister()
#==================================================================
# Main
#------------------------------------------------------------------
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'test':
print get_installed_plugins('gui')
#==================================================================
# $Log: gmPlugin.py,v $
# Revision 1.75 2007/11/23 23:35:47 ncq
# - cleanup
# - fix get_installed_plugins()
# - add workplace option to GetPluginLoadList()
# - rig test suite
#
# Revision 1.74 2007/10/29 13:18:35 ncq
# - only call repopulate_ui on widgets if they have one as some
# won't need it as they update ON_PAINT
#
# Revision 1.73 2007/10/08 13:07:19 ncq
# - factor out get_installed_plugins() even though it doesn't work yet
#
# Revision 1.72 2007/08/12 00:12:41 ncq
# - no more gmSignals.py
#
# Revision 1.71 2007/08/07 21:42:40 ncq
# - cPaths -> gmPaths
#
# Revision 1.70 2007/05/08 11:16:32 ncq
# - need to import gmTools
#
# Revision 1.69 2007/05/07 12:35:20 ncq
# - improve use of gmTools.cPaths()
#
# Revision 1.68 2007/04/11 20:47:13 ncq
# - no more 'resource dir' and 'gnumed_dir'
#
# Revision 1.67 2007/03/02 15:40:58 ncq
# - status text now set by signal
#
# Revision 1.66 2007/02/17 14:13:11 ncq
# - gmPerson.gmCurrentProvider().workplace now property
#
# Revision 1.65 2006/11/07 00:34:52 ncq
# - fix logic error in _on_raise_by_signal()
#
# Revision 1.64 2006/10/08 11:07:01 ncq
# - simplify wx import
# - properly use db cfg in GetPluginLoadList()
#
# Revision 1.63 2006/07/19 20:29:50 ncq
# - import cleanup
#
# Revision 1.62 2006/05/28 15:59:16 ncq
# - cleanup
# - receive_focus() now calls self._widget.repopulate_ui()
#
# Revision 1.61 2006/05/20 18:54:49 ncq
# - provide default receive_focus() and document it
# - remove get_instance()
#
# Revision 1.60 2006/05/15 13:38:52 ncq
# - remove "set" argument from notebook plugin __init__
# - cPatientChange_PluginMixin
# - inherit from this to listen to patient change signals
# - add depreciation warning to raise_notebook_plugin()
#
# Revision 1.59 2006/05/15 07:05:07 ncq
# - must import gmPerson now
#
# Revision 1.58 2006/05/14 21:44:22 ncq
# - add get_workplace() to gmPerson.gmCurrentProvider and make use thereof
# - remove use of gmWhoAmI.py
#
# Revision 1.57 2006/05/12 22:01:02 ncq
# - add _on_raise_by_signal()
# - connect to "display_widget" signal
#
# Revision 1.56 2006/05/12 12:18:11 ncq
# - whoami -> whereami cleanup
# - use gmCurrentProvider()
#
# Revision 1.55 2005/12/26 08:57:26 sjtan
#
# repaint may not be signalled on some platforms ( gtk ? ); repaint occurs if 1) the emrbrowser is the selected notebook page AND
# 2) the frame is re-sized. This suggests repaint is best done on notebook page changed. This workaround goes to
# the demographic page on a new patient select - let's the user confirm they have selected the right patient; then when
# switch to emrbrowser, this signals data_reget. seems to work.
#
# Revision 1.54 2005/11/01 08:51:43 ncq
# - wx.python -> wx.python
#
# Revision 1.53 2005/09/28 21:27:30 ncq
# - a lot of wx2.6-ification
#
# Revision 1.52 2005/09/28 15:57:48 ncq
# - a whole bunch of wx.Foo -> wx.Foo
#
# Revision 1.51 2005/09/26 18:01:51 ncq
# - use proper way to import wx26 vs wx2.4
# - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES
# - time for fixup
#
# Revision 1.50 2005/08/14 16:20:44 ncq
# - missing "Gnumed" directory
#
# Revision 1.49 2005/08/14 16:03:00 ncq
# - improved logging in case of error
#
# Revision 1.48 2005/08/14 15:00:08 ncq
# - fix plugin directory scanning
#
# Revision 1.47 2005/07/21 16:21:29 ncq
# - remove debugging cruft
#
# Revision 1.46 2005/07/18 17:13:38 ncq
# - improved import works but... better do what the docs tell us to do
#
# Revision 1.45 2005/07/18 16:48:26 ncq
# - hopefully improve __import__ of modules
#
# Revision 1.44 2005/07/16 22:49:52 ncq
# - cleanup
#
# Revision 1.43 2005/06/30 10:11:51 cfmoro
# String corrections
#
# Revision 1.42 2005/06/12 22:17:24 ncq
# - raise by menu only if activatable
#
# Revision 1.41 2005/03/29 07:28:20 ncq
# - add FIXME on plugin scanning
#
# Revision 1.40 2005/02/01 10:16:07 ihaywood
# refactoring of gmDemographicRecord and follow-on changes as discussed.
#
# gmTopPanel moves to gmHorstSpace
# gmRichardSpace added -- example code at present, haven't even run it myself
# (waiting on some icon .pngs from Richard)
#
# Revision 1.39 2005/01/31 10:37:26 ncq
# - gmPatient.py -> gmPerson.py
#
# Revision 1.38 2004/11/21 20:56:14 ncq
# - remove cruft
#
# Revision 1.37 2004/10/14 12:14:51 ncq
# - rearrange register() internally so we won't end up with
# half-baked but registered plugins
#
# Revision 1.36 2004/09/13 19:27:27 ncq
# - load "horstspace.notebook.plugin_load_order" instead of
# "plugin load order" with cookie "gui"
#
# Revision 1.35 2004/09/13 09:25:46 ncq
# - fix plugin raise code
#
# Revision 1.34 2004/09/06 22:23:03 ncq
# - properly use setDBParam()
#
# Revision 1.33 2004/08/20 13:34:48 ncq
# - getFirstMatchingDBSet() -> getDBParam()
#
# Revision 1.32 2004/08/04 17:16:02 ncq
# - wxNotebookPlugin -> cNotebookPlugin
# - derive cNotebookPluginOld from cNotebookPlugin
# - make cNotebookPluginOld warn on use and implement old
# explicit "main.notebook.raised_plugin"/ReceiveFocus behaviour
# - ReceiveFocus() -> receive_focus()
#
# Revision 1.31 2004/07/24 17:21:49 ncq
# - some cleanup, also re from wxPython import wx
# - factored out Horst space layout manager into it's own
# wx.Panel child class
# - subsequently renamed
# 'main.notebook.plugins' -> 'horstspace.notebook.pages'
# 'modules.gui' -> 'horstspace.notebook.gui' (to be renamed horstspace.notebook.plugins later)
# - adapt to said changes
#
# Revision 1.30 2004/07/19 16:17:55 ncq
# - missing GuiBroker reference added
#
# Revision 1.29 2004/07/19 13:54:25 ncq
# - simplify getPluginLoadList()
#
# Revision 1.28 2004/07/19 11:50:43 ncq
# - cfg: what used to be called "machine" really is "workplace", so fix
#
# Revision 1.27 2004/07/18 19:51:42 ncq
# - better logging
#
# Revision 1.26 2004/07/15 20:37:56 ncq
# - I really believe we should keep plugin code nicely separated
# - go back to plain notebook plugins, not super-plugins again
#
# Revision 1.24 2004/07/15 06:15:55 ncq
# - fixed typo patch -> path
#
# Revision 1.23 2004/07/15 05:17:43 ncq
# - better/correct logging in GetPluginLoadList()
#
# Revision 1.22 2004/06/26 23:09:22 ncq
# - better comments
#
# Revision 1.21 2004/06/25 14:39:35 ncq
# - make right-click runtime load/drop of plugins work again
#
# Revision 1.20 2004/06/25 13:28:00 ncq
# - logically separate notebook and clinical window plugins completely
#
# Revision 1.19 2004/06/25 12:51:23 ncq
# - InstPlugin() -> instantiate_plugin()
#
# Revision 1.18 2004/06/13 22:14:39 ncq
# - extensive cleanup/comments
# - deprecate self.internal_name in favour of self.__class__.__name__
# - introduce gb['main.notebook.raised_plugin']
# - add populate_with_data()
# - DoToolbar() -> populate_toolbar()
# - remove set_widget_reference()
#
# Revision 1.17 2004/03/10 13:57:45 ncq
# - unconditionally do shadow
#
# Revision 1.16 2004/03/10 12:56:01 ihaywood
# fixed sudden loss of main.shadow
# more work on referrals,
#
# Revision 1.15 2004/03/04 19:23:24 ncq
# - moved here from pycommon
#
# Revision 1.1 2004/02/25 09:30:13 ncq
# - moved here from python-common
#
# Revision 1.68 2004/02/12 23:54:39 ncq
# - add wx.Bell to can_receive_focus()
# - move raise_plugin out of class gmPlugin
#
# Revision 1.67 2004/01/17 10:37:24 ncq
# - don't ShowBar() in Raise() as GuiMain.OnNotebookPageChanged()
# takes care of that
#
# Revision 1.66 2004/01/17 09:59:02 ncq
# - enable Raise() to raise arbitrary plugins
#
# Revision 1.65 2004/01/06 23:44:40 ncq
# - __default__ -> xxxDEFAULTxxx
#
# Revision 1.64 2003/12/29 16:33:23 uid66147
# - use whoami.get_workplace()/gmPG.run_commit()
#
# Revision 1.63 2003/11/18 23:29:57 ncq
# - remove duplicate Version line
#
# Revision 1.62 2003/11/18 19:06:26 hinnef
# gmTmpPatient->gmPatient, again
#
# Revision 1.61 2003/11/17 10:56:37 sjtan
#
# synced and commiting.
#
# Revision 1.60 2003/11/09 14:26:41 ncq
# - if we have set_status_txt() do use it, too
#
# Revision 1.59 2003/11/08 10:48:36 shilbert
# - added convenience function _set_status_txt()
#
# Revision 1.58 2003/10/26 01:38:06 ncq
# - gmTmpPatient -> gmPatient, cleanup
#
# Revision 1.57 2003/09/24 10:32:54 ncq
# - whitespace cleanup
#
# Revision 1.56 2003/09/03 17:31:05 hinnef
# cleanup in GetPluginLoadList, make use of gmWhoAmI
#
# Revision 1.55 2003/07/21 20:57:42 ncq
# - cleanup
#
# Revision 1.54 2003/06/29 14:20:45 ncq
# - added TODO item
#
# Revision 1.53 2003/06/26 21:35:23 ncq
# - fatal->verbose
#
# Revision 1.52 2003/06/19 15:26:02 ncq
# - cleanup bits
# - add can_receive_focus() helper to wxNotebookPlugin()
# - in default can_receive_focus() veto() plugin activation on "no patient selected"
#
# Revision 1.51 2003/04/28 12:03:15 ncq
# - introduced internal_name() helper, adapted to use thereof
# - leaner logging
#
# Revision 1.50 2003/04/20 15:38:50 ncq
# - clean out some excessive logging
#
# Revision 1.49 2003/04/09 13:06:03 ncq
# - some cleanup
#
# Revision 1.48 2003/04/05 01:09:03 ncq
# - forgot that one in the big patient -> clinical clean up
#
# Revision 1.47 2003/02/24 12:35:55 ncq
# - renamed some function local variables to further my understanding of the code
#
# Revision 1.46 2003/02/17 16:18:29 ncq
# - fix whitespace on comments
#
# Revision 1.45 2003/02/13 12:58:05 sjtan
#
# remove unneded import.
#
# Revision 1.44 2003/02/11 18:23:39 ncq
# - removed unneeded import
#
# Revision 1.43 2003/02/11 12:27:07 sjtan
#
# suspect this is not the preferred way to get a handle on the plugin. Probably from guiBroker?
#
# Revision 1.42 2003/02/09 20:00:06 ncq
# - on notebook plugins rename Shown() to ReceiveFocus() as that's what this does, not only display itself
#
# Revision 1.41 2003/02/09 11:52:28 ncq
# - just one more silly cvs keyword
#
# Revision 1.40 2003/02/09 09:41:57 sjtan
#
# clean up new code, make it less intrusive.
#
# Revision 1.39 2003/02/07 12:47:15 sjtan
#
# using gmGuiBroker for more dynamic handler loading. (e.g. can use subclassed instances of EditAreaHandler classes).
# ~
#
# Revision 1.38 2003/02/07 08:16:16 ncq
# - some cosmetics
#
# Revision 1.37 2003/02/07 05:08:08 sjtan
#
# added few lines to hook in the handler classes from EditAreaHandler.
# EditAreaHandler was generated with editarea_gen_listener in wxPython directory.
#
# Revision 1.36 2003/01/16 14:45:04 ncq
# - debianized
#
# Revision 1.35 2003/01/16 09:18:11 ncq
# - cleanup
#
# Revision 1.34 2003/01/12 17:30:19 ncq
# - consistently return None if no plugins found by GetPluginLoadList()
#
# Revision 1.33 2003/01/12 01:45:12 ncq
# - typo, "IS None" not "== None"
#
# Revision 1.32 2003/01/11 22:03:30 hinnef
# removed gmConf
#
# Revision 1.31 2003/01/06 12:53:26 ncq
# - some cleanup bits
#
# Revision 1.30 2003/01/06 04:52:55 ihaywood
# resurrected gmDemographics.py
#
# Revision 1.29 2003/01/05 10:00:38 ncq
# - better comments
# - implement database plugin configuration loading/storing
#
# Revision 1.28 2003/01/04 07:43:55 ihaywood
# Popup menus on notebook tabs
#
# Revision 1.27 2002/11/13 09:14:17 ncq
# - document a few more todo's but don't do them before OSHCA
#
# Revision 1.26 2002/11/12 23:03:25 hherb
# further changes towards customization of plugin loading order
#
# Revision 1.25 2002/11/12 20:30:10 hherb
# Uses an optional config file in each plugin directory determining the order plugins are loaded as well as which plugins are loaded
#
# Revision 1.24 2002/09/26 13:10:43 ncq
# - silly ommitance
#
# Revision 1.23 2002/09/26 13:08:51 ncq
# - log version on import
# - TODO -> FIXME
#
# Revision 1.22 2002/09/09 00:50:28 ncq
# - return success or failure on LoadPlugin()
#
# @change log:
# 08.03.2002 hherb first draft, untested
|