""" @package core.settings @brief Default GUI settings List of classes: - settings::Settings Usage: @code from core.settings import UserSettings @endcode (C) 2007-2017 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa @author Luca Delucchi (language choice) """ from __future__ import print_function import os import sys import copy import wx import json import collections.abc from core import globalvar from core.gcmd import GException, GError from core.utils import GetSettingsPath, PathJoin, rgb2str class SettingsJSONEncoder(json.JSONEncoder): """Custom JSON encoder. Encodes color represented internally as tuple to hexadecimal color (tuple is represented as list in JSON, however GRASS expects tuple for colors). """ def default(self, obj): """Encode not automatically serializable objects.""" # we could use dictionary mapping as in wxplot if isinstance(obj, (wx.FontFamily, wx.FontStyle, wx.FontWeight)): return int(obj) return json.JSONEncoder.default(self, obj) def iterencode(self, obj): """Encode color tuple""" def color(item): if isinstance(item, tuple): if len(item) == 3: return "#{0:02x}{1:02x}{2:02x}".format(*item) if len(item) == 4: return "#{0:02x}{1:02x}{2:02x}{3:02x}".format(*item) if isinstance(item, list): return [color(e) for e in item] if isinstance(item, dict): return {key: color(value) for key, value in item.items()} else: return item return super(SettingsJSONEncoder, self).iterencode(color(obj)) def settings_JSON_decode_hook(obj): """Decode hex color saved in settings into tuple""" def colorhex2tuple(hexcode): hexcode = hexcode.lstrip("#") return tuple(int(hexcode[i : i + 2], 16) for i in range(0, len(hexcode), 2)) for k, v in obj.items(): if isinstance(v, str) and v.startswith("#") and len(v) in [7, 9]: obj[k] = colorhex2tuple(v) return obj class Settings: """Generic class where to store settings""" def __init__(self): # settings file self.filePath = os.path.join(GetSettingsPath(), "wx.json") self.legacyFilePath = os.path.join(GetSettingsPath(), "wx") # key/value separator self.sep = ";" # define default settings self._defaultSettings() # -> self.defaultSettings # read settings from the file self.userSettings = copy.deepcopy(self.defaultSettings) try: self.ReadSettingsFile() except GException as e: print(e.value, file=sys.stderr) # define internal settings self._internalSettings() # -> self.internalSettings def _generateLocale(self): """Generate locales""" try: self.locs = os.listdir(os.path.join(os.environ["GISBASE"], "locale")) self.locs.append("en") # GRASS doesn't ship EN po files self.locs.sort() # Add a default choice to not override system locale self.locs.insert(0, "system") except: # No NLS self.locs = ["system"] return "system" def _defaultSettings(self): """Define default settings""" try: projFile = PathJoin(os.environ["GRASS_PROJSHARE"], "epsg") except KeyError: projFile = "" id_loc = self._generateLocale() self.defaultSettings = { # # general # "general": { # use default window layout (layer manager, displays, ...) "defWindowPos": { "enabled": True, "dim": "1,1,%d,%d,%d,1,%d,%d" % ( globalvar.GM_WINDOW_SIZE[0], globalvar.GM_WINDOW_SIZE[1], globalvar.GM_WINDOW_SIZE[0] + 1, globalvar.MAP_WINDOW_SIZE[0], globalvar.MAP_WINDOW_SIZE[1], ), }, # workspace "workspace": { "posDisplay": {"enabled": False}, "posManager": {"enabled": False}, }, # region "region": { "resAlign": {"enabled": False}, }, }, # # datacatalog # "datacatalog": { # grassdb string "grassdbs": {"listAsString": ""}, "lazyLoading": {"enabled": False, "asked": False}, }, "manager": { # show opacity level widget "changeOpacityLevel": {"enabled": False}, # ask when removing layer from layer tree "askOnRemoveLayer": {"enabled": True}, # ask when quiting wxGUI or closing display "askOnQuit": {"enabled": True}, # hide tabs "hideTabs": { "search": False, "pyshell": False, }, "copySelectedTextToClipboard": {"enabled": False}, }, # # appearance # "appearance": { "outputfont": { "type": "Courier New", "size": 10, }, # expand/collapse element list "elementListExpand": {"selection": 0}, "menustyle": {"selection": 1}, "gSelectPopupHeight": {"value": 200}, "iconTheme": {"type": "grass"}, "commandNotebook": { "selection": 0 if sys.platform in ("win32", "darwin") else 1 }, }, # # language # "language": {"locale": {"lc_all": id_loc}}, # # display # "display": { "font": { "type": "", "encoding": "UTF-8", }, "driver": {"type": "cairo"}, "alignExtent": {"enabled": True}, "compResolution": {"enabled": False}, "autoRendering": {"enabled": True}, "autoZooming": {"enabled": False}, "showCompExtent": {"enabled": True}, "statusbarMode": {"selection": 0}, "bgcolor": { "color": (255, 255, 255, 255), }, "mouseWheelZoom": { "selection": 1, }, "scrollDirection": { "selection": 0, }, "nvizDepthBuffer": { "value": 16, }, }, # # projection # "projection": { "statusbar": { "proj4": "", "epsg": "", "projFile": projFile, }, "format": { "ll": "DMS", "precision": 2, }, }, # # Attribute Table Manager # "atm": { "highlight": { "color": (255, 255, 0, 255), "width": 2, "auto": True, }, "leftDbClick": {"selection": 1}, # draw selected "askOnDeleteRec": {"enabled": True}, "keycolumn": {"value": "cat"}, "encoding": { "value": "", }, }, # # Command # "cmd": { "overwrite": {"enabled": False}, "closeDlg": {"enabled": False}, "verbosity": {"selection": "grassenv"}, "addNewLayer": { "enabled": True, }, "interactiveInput": { "enabled": True, }, }, # # d.rast # "rasterLayer": { "opaque": {"enabled": False}, "colorTable": {"enabled": False, "selection": "rainbow"}, }, # # d.vect # "vectorLayer": { "featureColor": { "color": (0, 29, 57), "transparent": {"enabled": False}, }, "areaFillColor": { "color": (0, 103, 204), "transparent": {"enabled": False}, }, "line": { "width": 0, }, "point": { "symbol": "basic/x", "size": 5, }, "showType": { "point": {"enabled": True}, "line": {"enabled": True}, "centroid": {"enabled": False}, "boundary": {"enabled": False}, "area": {"enabled": True}, "face": {"enabled": True}, }, "randomColors": { "enabled": False, }, }, # # vdigit # "vdigit": { # symbology "symbol": { "newSegment": {"enabled": None, "color": (255, 0, 0, 255)}, # red "newLine": { "enabled": None, "color": (0, 86, 45, 255), }, # dark green "highlight": { "enabled": None, "color": (255, 255, 0, 255), }, # yellow "highlightDupl": { "enabled": None, "color": (255, 72, 0, 255), }, # red "point": {"enabled": True, "color": (0, 0, 0, 255)}, # black "line": {"enabled": True, "color": (0, 0, 0, 255)}, # black "boundaryNo": { "enabled": True, "color": (126, 126, 126, 255), }, # grey "boundaryOne": { "enabled": True, "color": (0, 255, 0, 255), }, # green "boundaryTwo": { "enabled": True, "color": (255, 135, 0, 255), }, # orange "centroidIn": {"enabled": True, "color": (0, 0, 255, 255)}, # blue "centroidOut": { "enabled": True, "color": (165, 42, 42, 255), }, # brown "centroidDup": { "enabled": True, "color": (156, 62, 206, 255), }, # violet "nodeOne": {"enabled": True, "color": (255, 0, 0, 255)}, # red "nodeTwo": { "enabled": True, "color": (0, 86, 45, 255), }, # dark green "vertex": { "enabled": False, "color": (255, 20, 147, 255), }, # deep pink "area": {"enabled": True, "color": (217, 255, 217, 255)}, # green "direction": {"enabled": False, "color": (255, 0, 0, 255)}, # red }, # display "lineWidth": {"value": 2, "units": "screen pixels"}, # snapping "snapping": { "value": 10, "unit": 0, # new "units": "screen pixels", # old for backwards comp. }, "snapToVertex": {"enabled": True}, # digitize new record "addRecord": {"enabled": True}, "layer": {"value": 1}, "category": {"value": 1}, "categoryMode": {"selection": 0}, # delete existing feature(s) "delRecord": {"enabled": True}, # query tool "query": {"selection": 0, "box": True}, "queryLength": {"than-selection": 0, "thresh": 0}, "queryDangle": {"than-selection": 0, "thresh": 0}, # select feature (point, line, centroid, boundary) "selectType": { "point": {"enabled": True}, "line": {"enabled": True}, "centroid": {"enabled": True}, "boundary": {"enabled": True}, }, "selectThresh": { "value": 10, "unit": 0, # new "units": "screen pixels", # old for backwards comp. }, "checkForDupl": {"enabled": False}, "selectInside": {"enabled": False}, # exit "saveOnExit": { "enabled": False, }, # break lines on intersection "breakLines": { "enabled": True, }, # close boundary (snap to the first node) "closeBoundary": { "enabled": False, }, }, # # plots for profiles, histograms, and scatterplots # "profile": { "raster": { "pcolor": (0, 0, 255, 255), # line color "pwidth": 1, # line width "pstyle": "solid", # line pen style "datatype": "cell", # raster type }, "font": { "titleSize": 12, "axisSize": 11, "legendSize": 10, "defaultSize": 11, "family": wx.FONTFAMILY_SWISS, "style": wx.FONTSTYLE_NORMAL, "weight": wx.FONTWEIGHT_NORMAL, }, "marker": { "color": (0, 0, 0, 255), "fill": "transparent", "size": 2, "type": "triangle", "legend": _("Segment break"), }, "grid": { "color": (200, 200, 200, 255), "enabled": True, }, "x-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "y-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "legend": {"enabled": True}, }, "histogram": { "raster": { "pcolor": (0, 0, 255, 255), # line color "pwidth": 1, # line width "pstyle": "solid", # line pen style "datatype": "cell", # raster type }, "font": { "titleSize": 12, "axisSize": 11, "legendSize": 10, "defaultSize": 11, "family": wx.FONTFAMILY_SWISS, "style": wx.FONTSTYLE_NORMAL, "weight": wx.FONTWEIGHT_NORMAL, }, "grid": { "color": (200, 200, 200, 255), "enabled": True, }, "x-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "y-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "legend": {"enabled": True}, }, "scatter": { "raster": { "pcolor": (0, 0, 255, 255), "pfill": "solid", "psize": 1, "ptype": "dot", # FIXME: this is only a quick fix # using also names used in a base class for compatibility # probably used only for initialization # base should be rewritten to not require this "pwidth": 1, # required by wxplot/base, maybe useless here "pstyle": "dot", # line pen style "plegend": _("Data point"), 0: {"datatype": "CELL"}, 1: {"datatype": "CELL"}, }, "font": { "titleSize": 12, "axisSize": 11, "legendSize": 10, "defaultSize": 11, "family": wx.FONTFAMILY_SWISS, "style": wx.FONTSTYLE_NORMAL, "weight": wx.FONTWEIGHT_NORMAL, }, "grid": { "color": (200, 200, 200, 255), "enabled": True, }, "x-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "y-axis": { "type": "auto", # axis format "min": 0, # axis min for custom axis range "max": 0, # axis max for custom axis range "log": False, }, "legend": {"enabled": True}, }, "gcpman": { "rms": { "highestonly": True, "sdfactor": 1, }, "symbol": { "color": (0, 0, 255, 255), "hcolor": (255, 0, 0, 255), "scolor": (0, 255, 0, 255), "ucolor": (255, 165, 0, 255), "unused": True, "size": 8, "width": 2, }, "map": { "overwrite": False, }, }, "nviz": { "view": { "persp": { "value": 20, "step": 2, }, "position": { "x": 0.84, "y": 0.16, }, "twist": { "value": 0, }, "z-exag": { "min": 0, "max": 10, "value": 1, }, "background": { "color": (255, 255, 255, 255), # white }, }, "fly": { "exag": { "move": 5, "turn": 5, } }, "animation": {"fps": 24, "prefix": _("animation")}, "surface": { "shine": { "map": False, "value": 60.0, }, "color": { "map": True, "value": (100, 100, 100, 255), # constant: grey }, "draw": { "wire-color": (136, 136, 136, 255), "mode": 1, # fine "style": 1, # surface "shading": 1, # gouraud "res-fine": 6, "res-coarse": 9, }, "position": { "x": 0, "y": 0, "z": 0, }, }, "constant": { "color": (100, 100, 100, 255), "value": 0.0, "transp": 0, "resolution": 6, }, "vector": { "lines": { "show": False, "width": 2, "color": (0, 0, 0, 255), "flat": False, "height": 0, "rgbcolumn": None, "sizecolumn": None, }, "points": { "show": False, "size": 100, "autosize": True, "width": 2, "marker": 2, "color": (0, 0, 0, 255), "height": 0, "rgbcolumn": None, "sizecolumn": None, }, }, "volume": { "color": { "map": True, "value": (100, 100, 100, 255), # constant: grey }, "draw": { "mode": 0, # isosurfaces "shading": 1, # gouraud "resolution": 3, # polygon resolution "box": False, # draw wire box }, "shine": { "map": False, "value": 60, }, "topo": {"map": None, "value": 0.0}, "transp": {"map": None, "value": 0}, "mask": {"map": None, "value": ""}, "slice_position": { "x1": 0, "x2": 1, "y1": 0, "y2": 1, "z1": 0, "z2": 1, "axis": 0, }, }, "cplane": { "shading": 4, "rotation": {"rot": 180, "tilt": 0}, "position": {"x": 0, "y": 0, "z": 0}, }, "light": { "position": { "x": 0.68, "y": -0.68, "z": 80, }, "bright": 80, "color": (255, 255, 255, 255), # white "ambient": 20, }, "fringe": { "elev": 55, "color": (128, 128, 128, 255), # grey }, "arrow": { "color": (0, 0, 0), }, "scalebar": { "color": (0, 0, 0), }, }, "modeler": { "disabled": { "color": (211, 211, 211, 255), # light grey }, "action": { "color": { "valid": (180, 234, 154, 255), # light green "invalid": (255, 255, 255, 255), # white "running": (255, 0, 0, 255), # red }, "size": { "width": 125, "height": 50, }, "width": { "parameterized": 2, "default": 1, }, }, "data": { "color": { "raster": (215, 215, 248, 255), # light blue "raster3d": (215, 248, 215, 255), # light green "vector": (248, 215, 215, 255), # light red "dbtable": (255, 253, 194, 255), # light yellow }, "size": { "width": 175, "height": 50, }, }, "loop": { "color": { "valid": (234, 226, 154, 255), # dark yellow }, "size": { "width": 175, "height": 40, }, }, "if-else": { "size": { "width": 150, "height": 40, }, }, "comment": { "color": (255, 233, 208, 255), # light yellow "size": { "width": 200, "height": 100, }, }, }, "mapswipe": { "cursor": { "color": (0, 0, 0, 255), "size": 12, "width": 1, "type": { "selection": 0, }, }, }, "animation": { "bgcolor": { "color": (255, 255, 255, 255), }, "nprocs": { "value": -1, }, "font": { "bgcolor": (255, 255, 255, 255), "fgcolor": (0, 0, 0, 255), }, "temporal": { "format": "%Y-%m-%d %H:%M:%S", "nodata": {"enable": False}, }, }, } # quick fix, http://trac.osgeo.org/grass/ticket/1233 # TODO if sys.platform == "darwin": self.defaultSettings["general"]["defWindowPos"]["enabled"] = False def _internalSettings(self): """Define internal settings (based on user settings)""" self.internalSettings = {} for group in list(self.userSettings.keys()): self.internalSettings[group] = {} for key in list(self.userSettings[group].keys()): self.internalSettings[group][key] = {} # self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath() self.internalSettings["appearance"]["elementListExpand"]["choices"] = ( _("Collapse all except PERMANENT and current"), _("Collapse all except PERMANENT"), _("Collapse all except current"), _("Collapse all"), _("Expand all"), ) self.internalSettings["language"]["locale"]["choices"] = tuple(self.locs) self.internalSettings["atm"]["leftDbClick"]["choices"] = ( _("Edit selected record"), _("Display selected"), ) self.internalSettings["cmd"]["verbosity"]["choices"] = ( "grassenv", "verbose", "quiet", ) self.internalSettings["appearance"]["iconTheme"]["choices"] = ("grass",) self.internalSettings["appearance"]["menustyle"]["choices"] = ( _("Classic (labels only)"), _("Combined (labels and module names)"), _("Expert (module names only)"), ) self.internalSettings["appearance"]["gSelectPopupHeight"]["min"] = 50 # there is also maxHeight given to TreeCtrlComboPopup.GetAdjustedSize self.internalSettings["appearance"]["gSelectPopupHeight"]["max"] = 1000 self.internalSettings["appearance"]["commandNotebook"]["choices"] = ( _("Basic top"), _("Basic left"), _("Fancy green"), _("List left"), ) self.internalSettings["display"]["driver"]["choices"] = ["cairo", "png"] self.internalSettings["display"]["statusbarMode"][ "choices" ] = None # set during MapFrame init self.internalSettings["display"]["mouseWheelZoom"]["choices"] = ( _("Zoom and recenter"), _("Zoom to mouse cursor"), _("Nothing"), ) self.internalSettings["display"]["scrollDirection"]["choices"] = ( _("Scroll forward to zoom in"), _("Scroll back to zoom in"), ) self.internalSettings["nviz"]["view"] = {} self.internalSettings["nviz"]["view"]["twist"] = {} self.internalSettings["nviz"]["view"]["twist"]["min"] = -180 self.internalSettings["nviz"]["view"]["twist"]["max"] = 180 self.internalSettings["nviz"]["view"]["persp"] = {} self.internalSettings["nviz"]["view"]["persp"]["min"] = 1 self.internalSettings["nviz"]["view"]["persp"]["max"] = 100 self.internalSettings["nviz"]["view"]["height"] = {} self.internalSettings["nviz"]["view"]["height"]["value"] = -1 self.internalSettings["nviz"]["view"]["z-exag"] = {} self.internalSettings["nviz"]["view"]["z-exag"]["llRatio"] = 1 self.internalSettings["nviz"]["view"]["rotation"] = None self.internalSettings["nviz"]["view"]["focus"] = {} self.internalSettings["nviz"]["view"]["focus"]["x"] = -1 self.internalSettings["nviz"]["view"]["focus"]["y"] = -1 self.internalSettings["nviz"]["view"]["focus"]["z"] = -1 self.internalSettings["nviz"]["view"]["dir"] = {} self.internalSettings["nviz"]["view"]["dir"]["x"] = -1 self.internalSettings["nviz"]["view"]["dir"]["y"] = -1 self.internalSettings["nviz"]["view"]["dir"]["z"] = -1 self.internalSettings["nviz"]["view"]["dir"]["use"] = False for decor in ("arrow", "scalebar"): self.internalSettings["nviz"][decor] = {} self.internalSettings["nviz"][decor]["position"] = {} self.internalSettings["nviz"][decor]["position"]["x"] = 0 self.internalSettings["nviz"][decor]["position"]["y"] = 0 self.internalSettings["nviz"][decor]["size"] = 100 self.internalSettings["nviz"]["vector"] = {} self.internalSettings["nviz"]["vector"]["points"] = {} self.internalSettings["nviz"]["vector"]["points"]["marker"] = ( "x", _("box"), _("sphere"), _("cube"), _("diamond"), _("aster"), _("gyro"), _("histogram"), ) self.internalSettings["vdigit"]["bgmap"] = {} self.internalSettings["vdigit"]["bgmap"]["value"] = "" self.internalSettings["mapswipe"]["cursor"]["type"] = {} self.internalSettings["mapswipe"]["cursor"]["type"]["choices"] = ( _("cross"), _("box"), _("circle"), ) def ReadSettingsFile(self, settings=None): """Reads settings file (mapset, location, gisdbase)""" if settings is None: settings = self.userSettings if os.path.exists(self.filePath): self._readFile(settings) elif os.path.exists(self.legacyFilePath): self._readLegacyFile(settings) # set environment variables font = self.Get(group="display", key="font", subkey="type") enc = self.Get(group="display", key="font", subkey="encoding") if font: os.environ["GRASS_FONT"] = font if enc: os.environ["GRASS_ENCODING"] = enc def _readFile(self, settings=None): """Read settings from file (wx.json) to dict, assumes file exists. :param settings: dict where to store settings (None for self.userSettings) """ def update_nested_dict_by_dict(dictionary, update): """Recursively update nested dictionary by another nested dictionary""" for key, value in update.items(): if isinstance(value, collections.abc.Mapping): dictionary[key] = update_nested_dict_by_dict( dictionary.get(key, {}), value ) else: dictionary[key] = value return dictionary try: with open(self.filePath, "r") as f: update = json.load(f, object_hook=settings_JSON_decode_hook) update_nested_dict_by_dict(settings, update) except json.JSONDecodeError as e: sys.stderr.write( _("Unable to read settings file <{path}>:\n{err}").format( path=self.filePath, err=e ) ) def _readLegacyFile(self, settings=None): """Read settings from legacy file (wx) to dict, assumes file exists. :param settings: dict where to store settings (None for self.userSettings) """ if settings is None: settings = self.userSettings try: fd = open(self.legacyFilePath, "r") except IOError: sys.stderr.write( _("Unable to read settings file <%s>\n") % self.legacyFilePath ) return try: line = "" for line in fd.readlines(): line = line.rstrip("%s" % os.linesep) group, key = line.split(self.sep)[0:2] kv = line.split(self.sep)[2:] subkeyMaster = None if len(kv) % 2 != 0: # multiple (e.g. nviz) subkeyMaster = kv[0] del kv[0] idx = 0 while idx < len(kv): if subkeyMaster: subkey = [subkeyMaster, kv[idx]] else: subkey = kv[idx] value = kv[idx + 1] value = self._parseValue(value, read=True) self.Append(settings, group, key, subkey, value) idx += 2 except ValueError as e: print( _( "Error: Reading settings from file <%(file)s> failed.\n" "\t\tDetails: %(detail)s\n" "\t\tLine: '%(line)s'\n" ) % {"file": self.legacyFilePath, "detail": e, "line": line}, file=sys.stderr, ) fd.close() fd.close() def SaveToFile(self, settings=None): """Save settings to the file""" if settings is None: settings = self.userSettings dirPath = GetSettingsPath() if not os.path.exists(dirPath): try: os.mkdir(dirPath) except: GError(_("Unable to create settings directory")) return try: with open(self.filePath, "w") as f: json.dump(settings, f, indent=2, cls=SettingsJSONEncoder) except IOError as e: raise GException(e) except Exception as e: raise GException( _( "Writing settings to file <%(file)s> failed." "\n\nDetails: %(detail)s" ) % {"file": self.filePath, "detail": e} ) return self.filePath def _parseValue(self, value, read=False): """Parse value to be store in settings file""" if read: # -> read settings (cast values) if value == "True": value = True elif value == "False": value = False elif value == "None": value = None elif ":" in value: # -> color try: value = tuple(map(int, value.split(":"))) except ValueError: # -> string pass else: try: value = int(value) except ValueError: try: value = float(value) except ValueError: pass else: # -> write settings if isinstance(value, type(())): # -> color value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2]) return value def Get(self, group, key=None, subkey=None, settings_type="user"): """Get value by key/subkey Raise KeyError if key is not found :param group: settings group :param key: (value, None) :param subkey: (value, list or None) :param settings_type: 'user', 'internal', 'default' :return: value """ if settings_type == "user": settings = self.userSettings elif settings_type == "internal": settings = self.internalSettings else: settings = self.defaultSettings try: if subkey is None: if key is None: return settings[group] else: return settings[group][key] else: if isinstance(subkey, type(tuple())) or isinstance( subkey, type(list()) ): return settings[group][key][subkey[0]][subkey[1]] else: return settings[group][key][subkey] except KeyError: print( "Settings: unable to get value '%s:%s:%s'\n" % (group, key, subkey), file=sys.stderr, ) def Set(self, group, value, key=None, subkey=None, settings_type="user"): """Set value of key/subkey Raise KeyError if group/key is not found :param group: settings group :param key: key (value, None) :param subkey: subkey (value, list or None) :param value: value :param settings_type: 'user', 'internal', 'default' """ if settings_type == "user": settings = self.userSettings elif settings_type == "internal": settings = self.internalSettings else: settings = self.defaultSettings try: if subkey is None: if key is None: settings[group] = value else: settings[group][key] = value else: if isinstance(subkey, type(tuple())) or isinstance( subkey, type(list()) ): settings[group][key][subkey[0]][subkey[1]] = value else: settings[group][key][subkey] = value except KeyError: raise GException( "%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey) ) def Append(self, dict, group, key, subkey, value, overwrite=True): """Set value of key/subkey Create group/key/subkey if not exists :param dict: settings dictionary to use :param group: settings group :param key: key :param subkey: subkey (value or list) :param value: value :param overwrite: True to overwrite existing value """ hasValue = True if group not in dict: dict[group] = {} hasValue = False if key not in dict[group]: dict[group][key] = {} hasValue = False if isinstance(subkey, list): # TODO: len(subkey) > 2 if subkey[0] not in dict[group][key]: dict[group][key][subkey[0]] = {} hasValue = False if subkey[1] not in dict[group][key][subkey[0]]: hasValue = False try: if overwrite or (not overwrite and not hasValue): dict[group][key][subkey[0]][subkey[1]] = value except TypeError: print( _("Unable to parse settings '%s'") % value + " (" + group + ":" + key + ":" + subkey[0] + ":" + subkey[1] + ")", file=sys.stderr, ) else: if subkey not in dict[group][key]: hasValue = False try: if overwrite or (not overwrite and not hasValue): dict[group][key][subkey] = value except TypeError: print( _("Unable to parse settings '%s'") % value + " (" + group + ":" + key + ":" + subkey + ")", file=sys.stderr, ) def GetDefaultSettings(self): """Get default user settings""" return self.defaultSettings def Reset(self, key=None): """Reset to default settings :param key: key in settings dict (None for all keys) """ if not key: self.userSettings = copy.deepcopy(self.defaultSettings) else: self.userSettings[key] = copy.deepcopy(self.defaultSettings[key]) UserSettings = Settings() def GetDisplayVectSettings(): settings = list() if not UserSettings.Get( group="vectorLayer", key="featureColor", subkey=["transparent", "enabled"] ): featureColor = UserSettings.Get( group="vectorLayer", key="featureColor", subkey="color" ) settings.append( "color=%s" % rgb2str.get(featureColor, ":".join(map(str, featureColor))) ) else: settings.append("color=none") if not UserSettings.Get( group="vectorLayer", key="areaFillColor", subkey=["transparent", "enabled"] ): fillColor = UserSettings.Get( group="vectorLayer", key="areaFillColor", subkey="color" ) settings.append( "fcolor=%s" % rgb2str.get(fillColor, ":".join(map(str, fillColor))) ) else: settings.append("fcolor=none") settings.append( "width=%s" % UserSettings.Get(group="vectorLayer", key="line", subkey="width") ) settings.append( "icon=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="symbol") ) settings.append( "size=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="size") ) types = [] for ftype in ["point", "line", "boundary", "centroid", "area", "face"]: if UserSettings.Get( group="vectorLayer", key="showType", subkey=[ftype, "enabled"] ): types.append(ftype) settings.append("type=%s" % ",".join(types)) if UserSettings.Get(group="vectorLayer", key="randomColors", subkey="enabled"): settings.append("-c") return settings