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

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

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