Package Gnumed :: Package wxpython :: Package gui :: Module gmConfigRegistry |
[frames] | no frames]

# Source Code for Module Gnumed.wxpython.gui.gmConfigRegistry

      1  """GNUmed configuration editor.
      2
      3  Works quite similar to the Windows Registry editor (but is
      4  a clean-room implementation).
      5
      6  @license: GPL"""
      7  #================================================================
      8  # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gui/gmConfigRegistry.py,v $
      9  __version__ = "$Revision: 1.43 $"
     10  __author__ = "H.Berger, S.Hilbert, K.Hilbert"
     11
     12  import sys, os, string, types
     13
     14  _log = gmLog.gmDefLog
     15  if __name__ == '__main__':
     16          _log.SetAllLogLevels(gmLog.lData)
     17
     18  from Gnumed.pycommon import gmCfg, gmConfigCommon, gmI18N
     19  from Gnumed.wxpython import gmPlugin, gmGuiHelpers, gmRegetMixin
     20  from Gnumed.business import gmPerson, gmSurgery
     21
     22  import wx
     23
     24  _cfg = gmCfg.gmDefCfgFile
     25
     26  _log.Log(gmLog.lInfo, __version__)
     27
     28  [       ConfigTreeCtrlID,
     29          ConfigTreeBoxID,
     30          ParamBoxID,
     31          ConfigEntryParamCtrlID,
     32          ButtonParamApplyID,
     33          ButtonParamRevertID,
     34          DescriptionBoxID,
     35          ConfigDescriptionTextID
     36  ] = map(lambda _init_ctrls: wx.NewId(), range(8))
     37
     38  #================================================================
     39 -class cConfTree(wx.TreeCtrl):
     40          """This wx.TreeCtrl derivative displays a tree view of configuration
     41          parameter names.
     42          """
     43 -        def __init__(self, parent, id, size=wx.DefaultSize,pos=wx.DefaultPosition,
     44                                  style=None,configSources = None,rootLabel = "",paramWidgets=None):
     45                  """Set up our specialised tree."""
     46
     47                  self.paramTextCtrl = paramWidgets[0]
     48                  self.paramDescription = paramWidgets[1]
     49                  self.mConfSources = configSources
     50                  for src in configSources:
     51                          _log.Log(gmLog.lData, 'config source: [%s]' % str(src))
     52                  self.rootLabel = rootLabel
     53
     54                  wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
     55
     56                  self.root = None
     57                  self.param_list = None
     58                  # currently selected parameter/subtree
     59                  self.currSelParam = None
     60                  self.currSelSubtree = None
     61
     62                  # connect handler
     63                  wx.EVT_TREE_ITEM_ACTIVATED (self, self.GetId(), self.OnActivate)
     64                  wx.EVT_RIGHT_DOWN(self,self.OnRightDown)
     65
     66          #------------------------------------------------------------------------
     67 -        def update(self):
     68
     69                  if self.param_list is not None:
     70                          del self.param_list
     71
     72                  if self.__populate_tree() is None:
     73                          return None
     74
     75                  return True
     76          #------------------------------------------------------------------------
     77 -        def __populate_tree(self):
     78                  # FIXME: TODO ALL !
     79
     80                  # clean old tree
     81                  if not self.root is None:
     82                          self.DeleteAllItems()
     83
     84                  # init new tree
     85                  self.root = self.AddRoot(self.rootLabel, -1, -1)
     86                  self.SetPyData(self.root, {'type': 'root', 'name': self.rootLabel})
     87                  self.SetItemHasChildren(self.root, False)
     88
     89                  # now get subtrees for four maingroups (see __init__)
     90
     91                  for nodeDescription in (self.mConfSources.keys()):
     92
     93                          _log.Log(gmLog.lData, 'adding first level node: [%s]' % nodeDescription)
     94                          node = self.AppendItem(self.root, nodeDescription)
     95                          self.SetPyData(node, {'type': 'defaultSubtree', 'name': nodeDescription})
     96
     97                          # add subtree if any
     98                          subTree = self.__getSubTree(nodeDescription)
     99                          if subTree is None:
    100                                  self.SetItemHasChildren(node, False)
    101                                  _log.Log(gmLog.lData, 'node has no children')
    102                                  continue
    103                          self.__addSubTree(node, subTree)
    104
    105                          self.SortChildren(node)
    106                          self.SetItemHasChildren(node, True)
    107
    108                  self.SetItemHasChildren(self.root, True)
    109                  self.SortChildren(self.root)
    110                  # and uncollapse
    111                  self.Expand(self.root)
    112
    113                  return True
    114          #------------------------------------------------------------------------
    115          # this must be reentrant as we will iterate over the tree branches
    116 -        def __addSubTree(self,aNode=None, aSubTree=None):
    117                  """
    118                  Adds a subtree of parameter names to an existing tree.
    119                  Returns resulting tree.
    120                  """
    121                  _log.Log(gmLog.lData, 'adding sub tree: [%s]' % str(aSubTree))
    122
    123                  # check if subtree is empty
    124                  if aSubTree[1] == {}:
    125                          return None
    126
    127                  # check if subtree has children
    128                  childrenList = aSubTree[1].keys()
    129                  if childrenList is None:
    130                          return None
    131                  self.SetItemHasChildren(aNode, True)
    132
    133                  # add every child as new node, add child-subtrees as subtree
    134                  # reiterating this method
    135                  for subTreeNode in childrenList:
    136                          nodeEntry = aSubTree[1][subTreeNode]
    137                          nodeName = nodeEntry[2]
    138                          node = self.AppendItem(aNode, nodeName)
    139                          self.SetPyData(node, nodeEntry[0])
    140                          self.SetItemHasChildren(node, False)
    141                          # now add subTrees
    142                          if not nodeEntry[1] == {}:
    143                                  self.__addSubTree(node,nodeEntry)
    144                                  self.SortChildren(node)
    145
    146          #------------------------------------------------------------------------
    147 -        def __getSubTree(self,nodeDescription):
    148                  """
    149                  get a subtree from the backend via ConfigData layer.
    150                  the subtree must have a special structure (see addTreeItem).
    151                  """
    152                  # if the subtree config data source is null, return empty subtree
    153                  if self.mConfSources[nodeDescription] is None:
    154                          return None
    155
    156                  # get all parameter names
    157                  tmpParamList = self.mConfSources[nodeDescription].getAllParamNames()
    158                  if tmpParamList is None:
    159                          return None
    160
    161                  # convert name list to a tree structure
    162                  currSubTree = [None,{},""]
    163                  # add each item
    164                  # attach parameter name (= reference for ConfigData) and subtree as object
    165                  for paramName in tmpParamList:
    166                          self.__addTreeItem (
    167                                  currSubTree,
    168                                  paramName,
    169                                  {'type': 'parameter', 'ref': paramName, 'subtree': nodeDescription}
    170                          )
    171
    172                  return currSubTree
    173          #------------------------------------------------------------------------
    174 -        def __addTreeItem(self,aSubTree, aStructuredName,object=None):
    175                  """
    176                  adds a name of the form "a.b.c.d" to a dict so that
    177                  dict['a']['b']['c']['d'] is a valid tree entry
    178                  each subTree entry consists of a 3 element list:
    179                  element [0] is an optional arbitrary object that should be connected
    180                  with the tree element(ID, etc.), element [1] a dictionary
    181                  holding the children of this element (again elements of type subTree)
    182                  list element [3] is the branch name
    183                  """
    184                  nameParts = string.split(str(aStructuredName), '.')
    185
    186                  tmpFunc = "aSubTree"
    187                  tmpDict = None
    188                  branchName = ""
    189                  # loop through all name parts
    190                  for part in (nameParts):
    191                          #recreate branch name
    192                          if branchName == "":
    193                                  branchName = branchName + part
    194                          else:
    195                                  branchName = branchName + "." + part
    196                          # get subtree dict
    197                          tmpDict = eval(tmpFunc)[1]
    198                          if not tmpDict.has_key(part):
    199                                  # initialize new branch
    200                                  tmpDict[part]=[]
    201                                  tmpDict[part].append({ 'type': 'branch', 'name': branchName })
    202                                  tmpDict[part].append({})
    203                                  tmpDict[part].append(part)
    204                          # set new start for branching nodes
    205                          tmpFunc = tmpFunc + "[1]['%s']" % part
    206                  # set object
    207                  eval(tmpFunc)[0]=object
    208                  return aSubTree
    209          #------------------------------------------------------------------------
    210 -        def SaveCurrParam(self):
    211                  """save parameter dialog"""
    212                  # self.currSelParam is the name of the parameter with optional
    213                  # cookie part appended, defParamName the name without cookie part !
    214                  # you must use the latter to access config definitions !
    215
    216                  if not (self.currSelParam is None or self.currSelSubtree is None):
    217
    218                          # get new value
    219                          val = self.paramTextCtrl.GetValue()
    220
    221                          currConfSource = self.mConfSources[self.currSelSubtree]
    222                          newValue = currConfSource.castType(self.currSelParam,val)
    223
    224                          if newValue is None:
    225                                  gmGuiHelpers.gm_show_error (
    226                                          _('Type of entered value is not compatible with type expected.'),
    227                                          _('saving configuration')
    228                                  )
    229
    230                          # a particular config definition refers to a parameter name
    231                          # without the cookie part. we have to strip the
    232                          # cookie off get the correct parameter
    233                          defParamName = currConfSource.getRawName(self.currSelParam)
    234
    235                          # config definition object
    236                          confDefinition = currConfSource.hasDefinition()
    237
    238                          # if there is no config definition, ask the user if the
    239                          # new value should be stored unchecked
    240
    241                          if not confDefinition or not currConfSource.hasParameterDefinition(defParamName):
    242                                  if gmGuiHelpers.gm_show_question (
    243                                          _("There is no config definition for this parameter.\nThus it can't be checked for validity.\n\nSave anyway ?"),
    244                                          _('saving configuration')):
    245                                          currConfSource.setConfigData( self.currSelParam,newValue)
    246
    247                                          # reshow new data to mark it non modified
    248                                          self.__show_parameter(self.currSelSubtree,self.currSelParam)
    249                                  return
    250
    251                          # else check parameter for validity
    252
    253                          if currConfSource.isValid(defParamName,newValue):
    254                                  currConfSource.setConfigData(self.currSelParam,newValue)
    255
    256                                  # reshow new data to mark it non modified
    257                                  self.__show_parameter(self.currSelSubtree,self.currSelParam)
    258                          else:
    259                                  # TODO: display some hint on what could be wrong
    260                                  gmGuiHelpers.gm_show_error (
    261                                          _('Entered value is not valid.'),
    262                                          _('saving configuration')
    263                                  )
    264
    265          #------------------------------------------------------------------------
    266 -        def OnActivate (self, event):
    267                  item = event.GetItem()
    268                  data = self.GetPyData(item)
    269
    270                  self.paramDescription.Clear()
    271                  self.paramTextCtrl.SetEditable(0)
    272                  type = data['type']
    273                  if type == 'parameter':
    274                          # ref is the parameter name for use in ConfigData Object
    275                          self.currSelParam = data['ref']
    276                          # Config Data subtree
    277                          self.currSelSubtree = data ['subtree']
    278                          self.__show_parameter(self.currSelSubtree,self.currSelParam)
    279                          return 1
    280                  elif type == 'branch':
    281                          message=_("(Branch)")
    282                  elif type == 'defaultSubtree':
    283                          message=_("(Subtree root)")
    284                  elif type == 'root':
    285                          message=_("<Options for current/default user and workplace>")
    286                  # show message
    287                  self.paramTextCtrl.ShowMessage(message)
    288                  # expand/unexpand node if it has children
    289                  if self.ItemHasChildren(item):
    290                          if self.IsExpanded(item):
    291                                  self.Collapse(item)
    292                          else:
    293                                  self.Expand(item)
    294                  return True
    295
    296          #--------------------------------------------------------
    297 -        def OnRightDown(self,event):
    298                  position = event.GetPosition()
    299                  (item,flags) = self.HitTest(position)
    300  #               if flags & (wx.TREE_HITTEST_ONITEMLABEL) == True:
    301                  self.SelectItem(item)
    302          #------------------------------------------------------------------------
    303 -        def __show_parameter(self,aSubtree=None, aParam=None):
    304                          # get the parameter value
    305                          value = self.mConfSources[aSubtree].getConfigData(aParam)
    306                          currType = self.mConfSources[aSubtree].getParamType(aParam)
    307                          # get description
    308                          description = self.mConfSources[aSubtree].getDescription(aParam)
    309  #                       print "showing parameter:"
    310  #                       print "param:", aParam
    311  #                       print "val  :", value
    312  #                       print "type :", currType
    313  #                       print "desc :", description
    314                          self.paramTextCtrl.ShowParam(aParam,currType,value)
    315                          self.paramTextCtrl.SetEditable(1)
    316                          self.paramDescription.SetValue(description)
    317  ###############################################################################
    318 -class cParamCtrl(wx.TextCtrl):
    319 -        def __init__(self, parent, id,value,pos,size,style,type ):
    320                  wx.TextCtrl.__init__(self, parent, -1, value="",style=style)
    321                  self.parent = parent
    322
    323 -        def ShowParam(self,aParam=None,aType=None,aValue=None):
    324                  self.Clear()
    325                  if aParam is None:
    326                          return
    327                  # store current parameter for later use
    328                  self.currParam = aParam
    329                  self.value = aValue
    330                  self.type = aType
    331
    332                  if self.type == 'string':
    333                          self.SetValue(self.value)
    334                  elif self.type == 'str_array':
    335                  # we can't use AppendText here because that would mark the value
    336                  # as modified
    337                          first = 1
    338                          all = ''
    339                          for line in (self.value):
    340                                  if first:
    341                                          first = 0
    342                                  else:
    343                                          all = all + '\n'
    344                                  all = all + line
    345                          self.SetValue(all)
    346                  elif self.type == 'numeric':
    347                          self.SetValue(str(self.value))
    348
    349 -        def ShowMessage(self,aMessage=""):
    350                  self.currParam = None
    351                  self.Clear()
    352                  self.SetValue(aMessage)
    353
    354
    355 -        def RevertToSaved(self):
    356                  if not self.currParam is None:
    357                          self.ShowParam(self.currParam, self.type, self.value)
    358
    359  ###############################################################################
    360  # TODO: -a MenuBar allowing for import, export and options
    361  #               -open a connection to backend via gmCfg
    362 -class gmConfigEditorPanel(wx.Panel):
    363 -        def __init__(self, parent, aUser,aWorkplace, plugin = 1):
    364                  """aUser and aWorkplace can be set such that an admin
    365                     could potentially edit another user ...
    366                  """
    367                  wx.Panel.__init__(self, parent, -1)
    368
    369                  self.currUser = aUser
    370                  self.currWorkplace = aWorkplace
    371                  # init data structures
    372                  # initialize the objects holding data on the subtrees
    373                  # add default subtrees root nodes if possible
    374                  #   default entries in root:
    375                  #        -default config file (usually ~/.gnumed/gnumed.conf)
    376                  #        -current user, current workplace
    377                  #        -current user, default workplace
    378                  #        -default user, current workplace
    379                  #        -default user, default workplace
    380                  self.mConfSources = {}
    381
    382                  # if we pass no config file name, we get the default cfg file
    383                  cfgFileDefault = gmConfigCommon.ConfigSourceFile("gnumed.conf")
    384                  cfgFileName = cfgFileDefault.GetFullPath()
    385                  # if the file was not found, we display some error message
    386                  if cfgFileName is None:
    387                          cfgFileName = "gnumed.conf not found"
    388                  # now get the absolute path of the default cfg file
    389                  self.mConfSources['FILE:%s' % cfgFileName] = cfgFileDefault
    390                  try:
    391                          if not (self.currUser is None or self.currWorkplace is None):
    392                                  self.mConfSources['DB:CURRENT_USER_CURRENT_WORKPLACE'] = gmConfigCommon.ConfigSourceDB('DB:CURRENT_USER_CURRENT_WORKPLACE',aWorkplace=self.currWorkplace)
    393                  except: pass
    394                  try:
    395                          if not (self.currUser is None) :
    396                                  self.mConfSources['DB:CURRENT_USER_DEFAULT_WORKPLACE'] = gmConfigCommon.ConfigSourceDB('DB:CURRENT_USER_DEFAULT_WORKPLACE')
    397                  except: pass
    398                  try:
    399                          if not (self.currWorkplace is None):
    400                                  self.mConfSources['DB:DEFAULT_USER_CURRENT_WORKPLACE'] = gmConfigCommon.ConfigSourceDB('DB:DEFAULT_USER_CURRENT_WORKPLACE',aUser='xxxDEFAULTxxx',aWorkplace=self.currWorkplace)
    401                  except: pass
    402                  try:
    403                          # this should always work
    404                          self.mConfSources['DB:DEFAULT_USER_DEFAULT_WORKPLACE'] = gmConfigCommon.ConfigSourceDB('DB:DEFAULT_USER_DEFAULT_WORKPLACE',aUser='xxxDEFAULTxxx')
    405                  except:
    406                          pass
    407  # main sizers
    408                  self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
    409                  self.rightSizer = wx.BoxSizer(wx.VERTICAL)
    410
    411  # selected parameter
    412                  self.configEntryParamBox = wx.StaticBox( self, ParamBoxID, _("Parameters") )
    413                  self.configEntryParamBoxSizer = wx.StaticBoxSizer( self.configEntryParamBox, wx.HORIZONTAL )
    414
    415                  self.configEntryParamCtrl = cParamCtrl( parent = self,
    416                                                  id = ConfigEntryParamCtrlID,
    417                                                  pos = wx.DefaultPosition,
    418                                                  size = wx.Size(250,200),
    419                                                  value = "" ,
    420                                                  style = wx.LB_SINGLE,
    421                                                  type = None)
    422
    423                  self.paramButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
    424                  self.paramCtrlSizer = wx.BoxSizer(wx.VERTICAL)
    425                  self.buttonApply = wx.Button(parent=self,
    426                                          id = ButtonParamApplyID,
    427                                          label = "Apply changes" )
    428                  self.buttonRevert = wx.Button(parent=self,
    429                                          id = ButtonParamRevertID,
    430                                          label = "Revert to saved" )
    431
    432                  wx.EVT_BUTTON(self,ButtonParamApplyID,self.ApplyChanges)
    433                  wx.EVT_BUTTON(self,ButtonParamRevertID,self.RevertChanges)
    434
    435
    436  # parameter description
    437                  self.configEntryDescriptionBox = wx.StaticBox( self, DescriptionBoxID, _("Parameter Description") )
    438                  self.configEntryDescriptionBoxSizer = wx.StaticBoxSizer( self.configEntryDescriptionBox, wx.HORIZONTAL )
    439
    440                  self.configEntryDescription = wx.TextCtrl(parent = self,
    441                                                          id = ConfigDescriptionTextID,
    442                                                          pos = wx.DefaultPosition,
    443                                                          size = wx.Size(250,100),
    444                                                          style = wx.TE_READONLY | wx.LB_SINGLE,
    445                                                          value ="" )
    446                  self.configEntryDescriptionBoxSizer.Add( self.configEntryDescription, 1, wx.ALIGN_CENTRE|wx.ALL|wx.EXPAND, 2 )
    447  # static box for config tree
    448                  self.configTreeBox = wx.StaticBox( self, ConfigTreeBoxID, _("Config Options") )
    449                  self.configTreeBoxSizer = wx.StaticBoxSizer( self.configTreeBox, wx.HORIZONTAL )
    450
    451  # config tree
    452                  rootLabel = "%s@%s" % (self.currUser,self.currWorkplace)
    453                  self.configTree = cConfTree( parent = self,
    454                                                  id = ConfigTreeCtrlID ,
    455                                                  pos = wx.Point(0, 0),
    456                                                  size = wx.Size(200, 300),
    457                                                  style = wx.TR_HAS_BUTTONS|wx.TAB_TRAVERSAL,
    458                                                  configSources = self.mConfSources,
    459                                                  rootLabel = rootLabel,
    460                                                  paramWidgets=(self.configEntryParamCtrl,self.configEntryDescription)
    461                                                  )
    462                  self.configTree.SetFocus()
    463                  self.configTreeBoxSizer.Add( self.configTree, 1, wx.ALIGN_CENTRE|wx.ALL|wx.EXPAND, 5 )
    464
    465                  self.paramCtrlSizer.Add(self.configEntryParamCtrl,1,wx.ALIGN_CENTRE|wx.ALL|wx.EXPAND, 2 )
    466                  self.paramButtonSizer.Add(self.buttonApply,1,wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 2 )
    467                  self.paramButtonSizer.Add(self.buttonRevert,1,wx.ALIGN_RIGHT|wx.ALL|wx.EXPAND, 2 )
    468                  self.paramCtrlSizer.Add(self.paramButtonSizer,0,wx.ALIGN_BOTTOM, 2 )
    469                  self.configEntryParamBoxSizer.Add(self.paramCtrlSizer , 1, wx.ALIGN_CENTRE|wx.ALL|wx.EXPAND, 2 )
    470
    471  # add right panels to right sizer
    472                  self.rightSizer.Add(self.configEntryParamBoxSizer, 1, wx.EXPAND, 0)
    473                  self.rightSizer.Add(self.configEntryDescriptionBoxSizer, 1, wx.EXPAND, 0)
    474
    475  # add widgets to main sizer
    476                  self.mainSizer.Add(self.configTreeBoxSizer, 1, wx.EXPAND, 0)
    477                  self.mainSizer.Add(self.rightSizer, 1, wx.EXPAND, 0)
    478                  self.SetAutoLayout(1)
    479                  self.SetSizer(self.mainSizer)
    480                  self.mainSizer.Fit(self)
    481                  self.mainSizer.SetSizeHints(self)
    482                  self.Layout()
    483
    484 -        def ApplyChanges(self,event):
    485                  if self.configEntryParamCtrl.IsModified():
    486                          self.configTree.SaveCurrParam()
    487
    488 -        def RevertChanges(self,event):
    489                  self.configEntryParamCtrl.RevertToSaved()
    490
    491 -        def repopulate_ui(self):
    492                  self.configTree.update()
    493
    494  #================================================================
    495  # MAIN
    496  #----------------------------------------------------------------
    497  if __name__ == '__main__':
    498          from Gnumed.wx.python import gmPlugin
    499          _log.Log (gmLog.lInfo, "starting config browser")
    500
    501          workplace = raw_input("Please enter a workplace name: ")
    502          # catch all remaining exceptions
    503          try:
    504                  application = wx.PyWidgetTester(size=(640,480))
    505                  application.SetWidget(gmConfigEditorPanel,"any-doc",workplace, 0)
    506                  application.MainLoop()
    507          except:
    508                  _log.LogException("unhandled exception caught !", sys.exc_info(), verbose=0)
    509                  # but re-raise them
    510                  raise
    511
    512          _log.Log (gmLog.lInfo, "closing config browser")
    513
    514  else:
    515 -        class gmConfigRegistry(gmPlugin.cNotebookPlugin):
    516                  """Class to load this module from an environment that wants a notebook plugin
    517                  """
    518 -                def name (self):
    519                          return _("Setup")
    520
    521 -                def GetWidget (self, parent):
    522                          # get current workplace name
    523                          workplace = gmSurgery.gmCurrentPractice().active_workplace
    524                          currUser = gmStaff.gmCurrentProvider()['db_user']
    525                          _log.Log (gmLog.lInfo, "ConfigReg: %s@%s" % (currUser,workplace))
    526                          self._widget = gmConfigEditorPanel(parent,currUser,workplace)
    527                          return self._widget
    528
    529 -                def MenuInfo (self):
    530                          return ('tools', _('&ConfigRegistry'))
    531
    532 -        def Setup(parent):
    533                  """Wrapper to load this module from an environment that wants a panel
    534                  """
    535                  currUser = gmStaff.gmCurrentProvider()['db_user']
    536                  workplace = gmSurgery.gmCurrentPractice().active_workplace
    537                  return gmConfigEditorPanel(parent,currUser,workplace)
    538
    539  #------------------------------------------------------------
    540