Changeset 1971


Ignore:
Timestamp:
Sep 7, 2015 1:25:13 PM (6 years ago)
Author:
toby
Message:

Add preferences option to edit configuration options

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/GSASII.py

    r1970 r1971  
    16181618            refList[generalData['Name']] = []
    16191619        return # success
     1620       
     1621    def OnPreferences(self,event):
     1622        'Edit the GSAS-II configuration variables'
     1623        dlg = G2G.SelectConfigSetting(self)
     1624        dlg.ShowModal() == wx.ID_OK
     1625        dlg.Destroy()
    16201626
    16211627    def _Add_ImportMenu_smallangle(self,parent):
     
    20182024        self._Add_ImportMenu_Sfact(Import)
    20192025        self._Add_ImportMenu_smallangle(Import)
     2026        item = File.Append(wx.ID_PREFERENCES, text = "&Preferences")
     2027        self.Bind(wx.EVT_MENU, self.OnPreferences, item)
     2028
    20202029        #======================================================================
    20212030        # Code to help develop/debug an importer, much is hard-coded below
  • trunk/GSASIIctrls.py

    r1946 r1971  
    1717
    1818'''
     19import os
     20import sys
    1921import wx
    2022import wx.grid as wg
     
    2527import copy
    2628# import cPickle
    27 import sys
    28 import os
    2929# import numpy as np
    3030# import numpy.ma as ma
     
    347347                self._IndicateValidity()
    348348
    349         elif isinstance(val,str) or isinstance(val,unicode):
     349        elif isinstance(val,str) or isinstance(val,unicode) or typeHint is str:
    350350            if self.CIFinput:
    351351                wx.TextCtrl.__init__(
    352                     self,parent,wx.ID_ANY,val,
     352                    self,parent,wx.ID_ANY,
    353353                    validator=ASCIIValidator(result=loc,key=key),
    354354                    **kw)
    355355            else:
    356                 wx.TextCtrl.__init__(self,parent,wx.ID_ANY,val,**kw)
     356                wx.TextCtrl.__init__(self,parent,wx.ID_ANY,**kw)
     357            if val is not None:
     358                self.SetValue(val)
    357359            if notBlank:
    358360                self.Bind(wx.EVT_CHAR,self._onStringKey)
     
    518520            self._setValue(self.result[self.key])
    519521        elif self.result is not None: # show formatted result, as Bob wants
    520             self._setValue(self.result[self.key])
     522            if not self.invalid: # don't update an invalid expression
     523                self._setValue(self.result[self.key])
    521524        if self.OnLeave: self.OnLeave(invalid=self.invalid,
    522525                                      value=self.result[self.key],
     
    31263129       
    31273130################################################################################
     3131class SelectConfigSetting(wx.Dialog):
     3132    '''Dialog to select configuration variables and set associated values.
     3133    '''
     3134    def __init__(self,parent=None):
     3135        import config_example
     3136        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
     3137        wx.Dialog.__init__(self, parent, wx.ID_ANY, 'Set Config Variable', style=style)
     3138        self.sizer = wx.BoxSizer(wx.VERTICAL)
     3139        self.vars = self.GetVarDocs(config_example.__file__)
     3140       
     3141        label = wx.StaticText(
     3142            self,  wx.ID_ANY,
     3143            'Select a GSAS-II configuration variable to change'
     3144            )
     3145        self.sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3146        self.choice = {}
     3147        btn = G2ChoiceButton(self, sorted(self.vars.keys(),key=lambda s: s.lower()),
     3148                                 strLoc=self.choice,strKey=0,
     3149                                 onChoice=self.OnSelection)
     3150        btn.SetLabel("")
     3151        self.sizer.Add(btn)
     3152
     3153        self.varsizer = wx.BoxSizer(wx.VERTICAL)
     3154        self.sizer.Add(self.varsizer,1,wx.ALL|wx.EXPAND,1)
     3155       
     3156        self.doclbl = wx.StaticBox(self, wx.ID_ANY, "")
     3157        self.doclblsizr = wx.StaticBoxSizer(self.doclbl)
     3158        self.docinfo = wx.StaticText(self,  wx.ID_ANY, "")
     3159        self.doclblsizr.Add(self.docinfo, 0, wx.ALIGN_LEFT|wx.ALL, 5)
     3160        self.sizer.Add(self.doclblsizr, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5)
     3161        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
     3162        self.saveBtn = wx.Button(self,-1,"Save current settings")
     3163        btnsizer.Add(self.saveBtn, 0, wx.ALL, 2)
     3164        self.saveBtn.Bind(wx.EVT_BUTTON, self.OnSave)
     3165        self.saveBtn.Enable(False)
     3166        self.applyBtn = wx.Button(self,-1,"Use current (no save)")
     3167        btnsizer.Add(self.applyBtn, 0, wx.ALL, 2)
     3168        self.applyBtn.Bind(wx.EVT_BUTTON, self.OnApplyChanges)
     3169        self.applyBtn.Enable(False)
     3170       
     3171        btn = wx.Button(self,wx.ID_CANCEL)
     3172        btnsizer.Add(btn, 0, wx.ALL, 2)
     3173        self.sizer.Add(btnsizer, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3174               
     3175        self.SetSizer(self.sizer)
     3176        self.sizer.Fit(self)
     3177        self.CenterOnParent()
     3178
     3179    def GetVarDocs(self,fname):
     3180        '''Read the module referenced in fname (often <module>.__file__) and return a
     3181        dict with names of global variables as keys.
     3182        For each global variable, the value contains four items:
     3183          item 0: the default value
     3184          item 1: the current value
     3185          item 2: the initial value (starts same as item 1)
     3186          item 3: the "docstring" that follows variable definition
     3187        '''
     3188        import config_example
     3189        import ast
     3190        fname = os.path.splitext(fname)[0]+'.py' # convert .pyc to .py
     3191        with open(fname, 'r') as f:
     3192            fstr = f.read()
     3193        fstr = fstr.replace('\r\n', '\n').replace('\r', '\n')
     3194        if not fstr.endswith('\n'):
     3195            fstr += '\n'
     3196        tree = ast.parse(fstr)
     3197        d = {}
     3198        key = None
     3199        for node in ast.walk(tree):
     3200            if isinstance(node,ast.Assign):
     3201                key = node.targets[0].id
     3202                d[key] = [config_example.__dict__.get(key),
     3203                          GSASIIpath.configDict.get(key),
     3204                          GSASIIpath.configDict.get(key),'']
     3205            elif isinstance(node,ast.Expr) and key:
     3206                d[key][3] = node.value.s.strip()
     3207            else:
     3208                key = None
     3209        return d
     3210   
     3211    def OnChange(self,event=None):
     3212        ''' Check if anything been changed. Turn the save button on/off.
     3213        '''
     3214        for var in self.vars:
     3215            if self.vars[var][0] is None and self.vars[var][1] is not None:
     3216                # make blank strings into None, if that is the default
     3217                if self.vars[var][1].strip() == '': self.vars[var][1] = None
     3218            if self.vars[var][1] != self.vars[var][2]:
     3219                #print 'changed',var,self.vars[var][:3]
     3220                self.saveBtn.Enable(True)
     3221                self.applyBtn.Enable(True)
     3222                break
     3223        else:
     3224            self.saveBtn.Enable(False)
     3225            self.applyBtn.Enable(False)
     3226        try:
     3227            self.resetBtn.Enable(True)
     3228        except:
     3229            pass
     3230       
     3231    def OnApplyChanges(self,event=None):
     3232        'Set config variables to match the current settings'
     3233        GSASIIpath.SetConfigValue(self.vars)
     3234        self.EndModal(wx.ID_OK)
     3235       
     3236    def OnSave(self,event):
     3237        '''Write the config variables to config.py and then set them
     3238        as the current settings
     3239        '''
     3240        # try to write to where an old config file is located
     3241        try:
     3242            import config
     3243            savefile = config.__file__
     3244        except ImportError: # no config.py file yet
     3245            savefile = os.path.join(GSASIIpath.path2GSAS2,'config.py')
     3246        # try to open file for write
     3247        try:
     3248            savefile = os.path.splitext(savefile)[0]+'.py' # convert .pyc to .py
     3249            fp = open(savefile,'w')
     3250        except IOError:  # can't write there, write in local mods directory
     3251            # create a local mods directory, if needed
     3252            if not os.path.exists(os.path.expanduser('~/.G2local/')):
     3253                print('Creating directory '+os.path.expanduser('~/.G2local/'))
     3254                os.mkdir(os.path.expanduser('~/.G2local/'))
     3255                sys.path.insert(0,os.path.expanduser('~/.G2local/'))
     3256            savefile = os.path.join(os.path.expanduser('~/.G2local/'),'config.py')
     3257            try:
     3258                fp = open(savefile,'w')
     3259            except IOError:
     3260                G2MessageBox(self,
     3261                                 'Error trying to write configuration to '+savefile,
     3262                                 'Unable to save')
     3263                return
     3264        import datetime
     3265        fp.write("'''\n")
     3266        fp.write("*config.py: Configuration options*\n----------------------------------\n")
     3267        fp.write("This file created in SelectConfigSetting on {:%d %b %Y %H:%M}\n".
     3268                 format(datetime.datetime.now()))
     3269        fp.write("'''\n\n")
     3270        fp.write("import os.path\n")
     3271        fp.write("import GSASIIpath\n\n")
     3272        for var in sorted(self.vars.keys(),key=lambda s: s.lower()):
     3273            if self.vars[var][1] is None: continue
     3274            if self.vars[var][1] == '': continue
     3275            if self.vars[var][0] == self.vars[var][1]: continue
     3276            try:
     3277                float(self.vars[var][1]) # test for number
     3278                fp.write(var + ' = ' + str(self.vars[var][1])+'\n')
     3279            except:
     3280                try:
     3281                    eval(self.vars[var][1]) # test for an expression
     3282                    fp.write(var + ' = ' + str(self.vars[var][1])+'\n')
     3283                except: # must be a string
     3284                    fp.write(var + ' = "' + str(self.vars[var][1])+'"\n')
     3285            if self.vars[var][3]:
     3286                fp.write("'''" + str(self.vars[var][3]) + "\n'''\n\n")
     3287        fp.close()
     3288        print('wrote file '+savefile)
     3289        # force a reload of the config settings
     3290        self.OnApplyChanges()
     3291        self.EndModal(wx.ID_OK)
     3292
     3293    def OnBoolSelect(self,event):
     3294        'Respond to a change in a True/False variable'
     3295        rb = event.GetEventObject()
     3296        var = self.choice[0]
     3297        self.vars[var][1] = (rb.GetSelection() == 0)
     3298        self.OnChange()
     3299        wx.CallAfter(self.OnSelection)
     3300       
     3301    def onSelDir(self,event):
     3302        'Select a directory from a menu'
     3303        dlg = wx.DirDialog(self, "Choose a directory:",style=wx.DD_DEFAULT_STYLE)
     3304        if dlg.ShowModal() == wx.ID_OK:
     3305            var = self.choice[0]
     3306            self.vars[var][1] = dlg.GetPath()
     3307            self.strEd.SetValue(self.vars[var][1])
     3308            self.OnChange()
     3309        dlg.Destroy()
     3310       
     3311    def OnSelection(self):
     3312        'show a selected variable'
     3313        self.varsizer.DeleteWindows()
     3314        var = self.choice[0]
     3315        showdef = True
     3316        if var not in self.vars:
     3317            raise Exception,"How did this happen?"
     3318        if type(self.vars[var][0]) is int:
     3319            ed = ValidatedTxtCtrl(self,self.vars[var],1,typeHint=int,OKcontrol=self.OnChange)
     3320            self.varsizer.Add(ed, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3321        elif type(self.vars[var][0]) is float:
     3322            ed = ValidatedTxtCtrl(self,self.vars[var],1,typeHint=float,OKcontrol=self.OnChange)
     3323            self.varsizer.Add(ed, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3324        elif type(self.vars[var][0]) is bool:
     3325            showdef = False
     3326            lbl = "value for "+var
     3327            ch = []
     3328            for i,v in enumerate((True,False)):
     3329                s = str(v)
     3330                if v == self.vars[var][0]:
     3331                    defopt = i
     3332                    s += ' (default)'
     3333                ch += [s]
     3334            rb = wx.RadioBox(
     3335                    self, wx.ID_ANY, lbl, wx.DefaultPosition, wx.DefaultSize,
     3336                    ch, 1, wx.RA_SPECIFY_COLS
     3337            )
     3338            # set initial value
     3339            if self.vars[var][1] is None:
     3340                rb.SetSelection(defopt)
     3341            elif self.vars[var][1]:
     3342                rb.SetSelection(0)
     3343            else:
     3344                rb.SetSelection(1)
     3345            rb.Bind(wx.EVT_RADIOBOX,self.OnBoolSelect)
     3346            self.varsizer.Add(rb, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3347        else:
     3348            if var.endswith('_directory') or var.endswith('_location'):
     3349                btn = wx.Button(self,wx.ID_ANY,'Select from dialog...')
     3350                sz = (400,-1)
     3351            else:
     3352                btn = None
     3353                sz = (250,-1)
     3354            self.strEd = ValidatedTxtCtrl(self,self.vars[var],1,typeHint=str,OKcontrol=self.OnChange,
     3355                                              size=sz)
     3356            if self.vars[var][1] is not None:
     3357                self.strEd.SetValue(self.vars[var][1])
     3358            self.varsizer.Add(self.strEd, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3359            if btn:
     3360                btn.Bind(wx.EVT_BUTTON,self.onSelDir)
     3361                self.varsizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3362        # button for reset to default value
     3363        lbl = "Reset to Default"
     3364        if showdef: # spell out default when needed
     3365            lbl += ' (='+str(self.vars[var][0])+')'
     3366            #label = wx.StaticText(self,  wx.ID_ANY, 'Default value = '+str(self.vars[var][0]))
     3367            #self.varsizer.Add(label, 0, wx.ALIGN_LEFT|wx.ALL, 5)
     3368        self.resetBtn = wx.Button(self,-1,lbl)
     3369        self.resetBtn.Bind(wx.EVT_BUTTON, self.OnClear)
     3370        if self.vars[var][1] is not None and self.vars[var][1] != '': # show current value, if one
     3371            #label = wx.StaticText(self,  wx.ID_ANY, 'Current value = '+str(self.vars[var][1]))
     3372            #self.varsizer.Add(label, 0, wx.ALIGN_LEFT|wx.ALL, 5)
     3373            self.resetBtn.Enable(True)
     3374        else:
     3375            self.resetBtn.Enable(False)
     3376        self.varsizer.Add(self.resetBtn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
     3377        # show meaning, if defined
     3378        self.doclbl.SetLabel("Description of "+str(var))
     3379        if self.vars[var][3]:
     3380            self.docinfo.SetLabel(self.vars[var][3])
     3381        else:
     3382            self.docinfo.SetLabel("(not documented)")
     3383        self.sizer.Fit(self)
     3384        self.CenterOnParent()
     3385        wx.CallAfter(self.SendSizeEvent)
     3386
     3387    def OnClear(self, event):
     3388        var = self.choice[0]
     3389        self.vars[var][1] = self.vars[var][0]
     3390        self.OnChange()
     3391        wx.CallAfter(self.OnSelection)
     3392       
     3393################################################################################
    31283394class downdate(wx.Dialog):
    31293395    '''Dialog to allow a user to select a version of GSAS-II to install
  • trunk/GSASIIpath.py

    r1729 r1971  
    2020import sys
    2121import platform
     22# see if a directory for local modifications is defined. If so, stick that in the path
     23if os.path.exists(os.path.expanduser('~/.G2local/')):
     24    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
     25    import glob
     26    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
     27    files = ""
     28    prev = None
     29    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
     30        f = os.path.split(f)[1]
     31        if os.path.splitext(f)[0] == prev: continue
     32        prev = os.path.splitext(f)[0]
     33        if files: files += ", "
     34        files += f
     35    if files:
     36        print("*"*75)
     37        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
     38        print("  "+files)
     39        print("*"*75)
     40           
     41
    2242# determine a binary path for the pyd files based on the host OS and the python version, 
    2343# path is relative to location of the script that is called as well as this file
     
    82102    return configDict.get(key,default)
    83103
     104def SetConfigValue(parmdict):
     105    '''Set configuration variables from a dictionary where elements are lists
     106    First item in list is the default value and second is the value to use.
     107    '''
     108    global configDict
     109    for var in parmdict:
     110        if var in configDict:
     111            del configDict[var]
     112        if parmdict[var][1] is None: continue
     113        if parmdict[var][1] == '': continue
     114        if parmdict[var][0] == parmdict[var][1]: continue
     115        configDict[var] = parmdict[var][1]
     116
    84117# routines for looking a version numbers in files
    85118version = -1
  • trunk/config_example.py

    r1823 r1971  
    99########### SVN repository information ###################
    1010'''
    11 *config.py: Configuration options*
    12 ----------------------------------
     11*config_example.py: Configuration options*
     12-------------------------------------------
    1313
    14 This file contains optional configuration options for GSAS-II. Note that
    15 this file is not required to be present and code should be written to
    16 provide the default behavior if the file is not present or if any configuration
    17 variable is not set, but please do place a docstring here for every used
    18 config variable explaining what it does. Access these variables using
    19 :func:`GSASIIpath.GetConfigValue`.
     14This file contains optional configuration options for GSAS-II. The variables
     15in this file can be copied to file config.py, which is imported if present.
     16Access these variables using :func:`GSASIIpath.GetConfigValue`, which returns
     17None if the variable is not set. Note that a config.py file need not
     18be present, but if in use it will typically be found with the GSAS-II source
     19directory (GSASIIpath.Path2GSAS2) or a directory for local GSAS-II
     20modifications (~/.G2local/ or /Documents and Settings/<User>/.G2local/). 
     21
     22When defining new config variables for GSAS-II, define them here with a
     23default value: use None or a string for strings, or use integers or real
     24values. Include a doc string after each variable is defined to explain
     25what it does. Use names ending in _location or _directory for items
     26that will contain directory names.
     27
     28For example::
     29
     30    test_int = 0
     31    test_float = 0.0
     32    test_string = None (or)
     33    test_string = 'value'
    2034'''
    2135
    2236debug = False
    23 '''Set to True to turn on debugging mode. This enables use of IPython on
     37'''Set to True to turn on debugging mode.This enables use of IPython on
    2438exceptions and on calls to :func:`GSASIIpath.IPyBreak`. Calls to
    2539:func:`GSASIIpath.pdbBreak` will invoke pdb at that location.
    26 If debug is false calls to :func:`GSASIIpath.IPyBreak` and
     40
     41If debug is False, calls to :func:`GSASIIpath.IPyBreak` and
    2742:func:`GSASIIpath.pdbBreak` are ignored.
    2843'''
    2944
    3045Enable_logging = False
    31 'Set to True to enable use of command logging'
     46'Set to True to enable use of command logging (under development.)'
    3247
    3348logging_debug = False
    34 'Set to True to enable debug for logging'
     49'Set to True to enable debug for logging (under development.)'
    3550
    36 Help_mode = None
    37 'Set to "internal" to use a Python-based web viewer rather than a web browser'
     51Help_mode = "browser"
     52'''Set to "internal" to use a Python-based web viewer to display
     53help documentation and tutorials. If set to the default ("browser")
     54the default web browser is used.
     55'''
    3856
    3957Tutorial_location = None
    4058'''Change this to place tutorials by in a different spot. If None, this defaults to
    41 ~/My Documents/G2tutorials (on windows) or ~/G2tutorials. If you want to use a different
    42 location (such as to use the GSASII installation directory), this can be set here.
    43 As an example, to always use ~/G2tutorials do this::
     59<user>/My Documents/G2tutorials (on windows) or <user>/G2tutorials. If you want to
     60use a different location, this can be set here. To install into the location where
     61GSAS-II is installed, use this::
    4462
    45     import os.path
    46     Tutorial_location = os.path.join(os.path.expanduser('~'),'G2tutorials')
    47 
    48 To install into the location where GSAS-II is installed, use this::
    49 
    50     import GSASIIpath
    5163    Tutorial_location = GSASIIpath.path2GSAS2
    5264
     65As another example, to use ~/.G2tutorials do this::
     66
     67    Tutorial_location = os.path.expanduser('~/.G2tutorials')
     68
     69Note that os.path and GSASIIpath are imported inside config.py; other imports will
     70require manual editing of the file.
    5371'''
    5472
     
    5876
    5977Import_directory=None
    60 '''Specifies a default location for starting GSAS-II
     78'''Specifies a default location for finding exercise files used for
     79Tutorials.
    6180'''
    6281
    63 wxInspector = None
    64 '''If set to True, the wxInspector widget is displayed
     82wxInspector = False
     83'''If set to True, the wxInspector widget is displayed when
     84GSAS-II is started.
    6585'''
Note: See TracChangeset for help on using the changeset viewer.