settings.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. """
  2. @package core.settings
  3. @brief Default GUI settings
  4. List of classes:
  5. - settings::Settings
  6. Usage:
  7. @code
  8. from core.settings import UserSettings
  9. @endcode
  10. (C) 2007-2017 by the GRASS Development Team
  11. This program is free software under the GNU General Public License
  12. (>=v2). Read the file COPYING that comes with GRASS for details.
  13. @author Martin Landa <landa.martin gmail.com>
  14. @author Luca Delucchi <lucadeluge gmail.com> (language choice)
  15. """
  16. from __future__ import print_function
  17. import os
  18. import sys
  19. import copy
  20. import wx
  21. import json
  22. from core import globalvar
  23. from core.gcmd import GException, GError
  24. from core.utils import GetSettingsPath, PathJoin, rgb2str
  25. class SettingsJSONEncoder(json.JSONEncoder):
  26. """Custom JSON encoder.
  27. Encodes color represented internally as tuple
  28. to hexadecimal color (tuple is represented as
  29. list in JSON, however GRASS expects tuple for colors).
  30. """
  31. def default(self, obj):
  32. """Encode not automatically serializable objects."""
  33. # we could use dictionary mapping as in wxplot
  34. if isinstance(obj, (wx.FontFamily, wx.FontStyle, wx.FontWeight)):
  35. return int(obj)
  36. return json.JSONEncoder.default(self, obj)
  37. def iterencode(self, obj):
  38. """Encode color tuple"""
  39. def color(item):
  40. if isinstance(item, tuple):
  41. if len(item) == 3:
  42. return "#{0:02x}{1:02x}{2:02x}".format(*item)
  43. if len(item) == 4:
  44. return "#{0:02x}{1:02x}{2:02x}{3:02x}".format(*item)
  45. if isinstance(item, list):
  46. return [color(e) for e in item]
  47. if isinstance(item, dict):
  48. return {key: color(value) for key, value in item.items()}
  49. else:
  50. return item
  51. return super(SettingsJSONEncoder, self).iterencode(color(obj))
  52. def settings_JSON_decode_hook(obj):
  53. """Decode hex color saved in settings into tuple"""
  54. def colorhex2tuple(hexcode):
  55. hexcode = hexcode.lstrip("#")
  56. return tuple(int(hexcode[i : i + 2], 16) for i in range(0, len(hexcode), 2))
  57. for k, v in obj.items():
  58. if isinstance(v, str) and v.startswith("#") and len(v) in [7, 9]:
  59. obj[k] = colorhex2tuple(v)
  60. return obj
  61. class Settings:
  62. """Generic class where to store settings"""
  63. def __init__(self):
  64. # settings file
  65. self.filePath = os.path.join(GetSettingsPath(), "wx.json")
  66. self.legacyFilePath = os.path.join(GetSettingsPath(), "wx")
  67. # key/value separator
  68. self.sep = ";"
  69. # define default settings
  70. self._defaultSettings() # -> self.defaultSettings
  71. # read settings from the file
  72. self.userSettings = copy.deepcopy(self.defaultSettings)
  73. try:
  74. self.ReadSettingsFile()
  75. except GException as e:
  76. print(e.value, file=sys.stderr)
  77. # define internal settings
  78. self._internalSettings() # -> self.internalSettings
  79. def _generateLocale(self):
  80. """Generate locales"""
  81. try:
  82. self.locs = os.listdir(os.path.join(os.environ["GISBASE"], "locale"))
  83. self.locs.append("en") # GRASS doesn't ship EN po files
  84. self.locs.sort()
  85. # Add a default choice to not override system locale
  86. self.locs.insert(0, "system")
  87. except:
  88. # No NLS
  89. self.locs = ["system"]
  90. return "system"
  91. def _defaultSettings(self):
  92. """Define default settings"""
  93. try:
  94. projFile = PathJoin(os.environ["GRASS_PROJSHARE"], "epsg")
  95. except KeyError:
  96. projFile = ""
  97. id_loc = self._generateLocale()
  98. self.defaultSettings = {
  99. #
  100. # general
  101. #
  102. "general": {
  103. # use default window layout (layer manager, displays, ...)
  104. "defWindowPos": {
  105. "enabled": True,
  106. "dim": "1,1,%d,%d,%d,1,%d,%d"
  107. % (
  108. globalvar.GM_WINDOW_SIZE[0],
  109. globalvar.GM_WINDOW_SIZE[1],
  110. globalvar.GM_WINDOW_SIZE[0] + 1,
  111. globalvar.MAP_WINDOW_SIZE[0],
  112. globalvar.MAP_WINDOW_SIZE[1],
  113. ),
  114. },
  115. # workspace
  116. "workspace": {
  117. "posDisplay": {"enabled": False},
  118. "posManager": {"enabled": False},
  119. },
  120. # region
  121. "region": {
  122. "resAlign": {"enabled": False},
  123. },
  124. },
  125. #
  126. # datacatalog
  127. #
  128. "datacatalog": {
  129. # grassdb string
  130. "grassdbs": {"listAsString": ""},
  131. "lazyLoading": {"enabled": False, "asked": False},
  132. },
  133. "manager": {
  134. # show opacity level widget
  135. "changeOpacityLevel": {"enabled": False},
  136. # ask when removing layer from layer tree
  137. "askOnRemoveLayer": {"enabled": True},
  138. # ask when quiting wxGUI or closing display
  139. "askOnQuit": {"enabled": True},
  140. # hide tabs
  141. "hideTabs": {
  142. "search": False,
  143. "pyshell": False,
  144. },
  145. "copySelectedTextToClipboard": {"enabled": False},
  146. },
  147. #
  148. # appearance
  149. #
  150. "appearance": {
  151. "outputfont": {
  152. "type": "Courier New",
  153. "size": 10,
  154. },
  155. # expand/collapse element list
  156. "elementListExpand": {"selection": 0},
  157. "menustyle": {"selection": 1},
  158. "gSelectPopupHeight": {"value": 200},
  159. "iconTheme": {"type": "grass"},
  160. "commandNotebook": {
  161. "selection": 0 if sys.platform in ("win32", "darwin") else 1
  162. },
  163. },
  164. #
  165. # language
  166. #
  167. "language": {"locale": {"lc_all": id_loc}},
  168. #
  169. # display
  170. #
  171. "display": {
  172. "font": {
  173. "type": "",
  174. "encoding": "UTF-8",
  175. },
  176. "driver": {"type": "cairo"},
  177. "alignExtent": {"enabled": True},
  178. "compResolution": {"enabled": False},
  179. "autoRendering": {"enabled": True},
  180. "autoZooming": {"enabled": False},
  181. "showCompExtent": {"enabled": True},
  182. "statusbarMode": {"selection": 0},
  183. "bgcolor": {
  184. "color": (255, 255, 255, 255),
  185. },
  186. "mouseWheelZoom": {
  187. "selection": 1,
  188. },
  189. "scrollDirection": {
  190. "selection": 0,
  191. },
  192. "nvizDepthBuffer": {
  193. "value": 16,
  194. },
  195. },
  196. #
  197. # projection
  198. #
  199. "projection": {
  200. "statusbar": {
  201. "proj4": "",
  202. "epsg": "",
  203. "projFile": projFile,
  204. },
  205. "format": {
  206. "ll": "DMS",
  207. "precision": 2,
  208. },
  209. },
  210. #
  211. # Attribute Table Manager
  212. #
  213. "atm": {
  214. "highlight": {
  215. "color": (255, 255, 0, 255),
  216. "width": 2,
  217. "auto": True,
  218. },
  219. "leftDbClick": {"selection": 1}, # draw selected
  220. "askOnDeleteRec": {"enabled": True},
  221. "keycolumn": {"value": "cat"},
  222. "encoding": {
  223. "value": "",
  224. },
  225. },
  226. #
  227. # Command
  228. #
  229. "cmd": {
  230. "overwrite": {"enabled": False},
  231. "closeDlg": {"enabled": False},
  232. "verbosity": {"selection": "grassenv"},
  233. "addNewLayer": {
  234. "enabled": True,
  235. },
  236. "interactiveInput": {
  237. "enabled": True,
  238. },
  239. },
  240. #
  241. # d.rast
  242. #
  243. "rasterLayer": {
  244. "opaque": {"enabled": False},
  245. "colorTable": {"enabled": False, "selection": "rainbow"},
  246. },
  247. #
  248. # d.vect
  249. #
  250. "vectorLayer": {
  251. "featureColor": {
  252. "color": (0, 29, 57),
  253. "transparent": {"enabled": False},
  254. },
  255. "areaFillColor": {
  256. "color": (0, 103, 204),
  257. "transparent": {"enabled": False},
  258. },
  259. "line": {
  260. "width": 0,
  261. },
  262. "point": {
  263. "symbol": "basic/x",
  264. "size": 5,
  265. },
  266. "showType": {
  267. "point": {"enabled": True},
  268. "line": {"enabled": True},
  269. "centroid": {"enabled": False},
  270. "boundary": {"enabled": False},
  271. "area": {"enabled": True},
  272. "face": {"enabled": True},
  273. },
  274. "randomColors": {
  275. "enabled": False,
  276. },
  277. },
  278. #
  279. # vdigit
  280. #
  281. "vdigit": {
  282. # symbology
  283. "symbol": {
  284. "newSegment": {"enabled": None, "color": (255, 0, 0, 255)}, # red
  285. "newLine": {
  286. "enabled": None,
  287. "color": (0, 86, 45, 255),
  288. }, # dark green
  289. "highlight": {
  290. "enabled": None,
  291. "color": (255, 255, 0, 255),
  292. }, # yellow
  293. "highlightDupl": {
  294. "enabled": None,
  295. "color": (255, 72, 0, 255),
  296. }, # red
  297. "point": {"enabled": True, "color": (0, 0, 0, 255)}, # black
  298. "line": {"enabled": True, "color": (0, 0, 0, 255)}, # black
  299. "boundaryNo": {
  300. "enabled": True,
  301. "color": (126, 126, 126, 255),
  302. }, # grey
  303. "boundaryOne": {
  304. "enabled": True,
  305. "color": (0, 255, 0, 255),
  306. }, # green
  307. "boundaryTwo": {
  308. "enabled": True,
  309. "color": (255, 135, 0, 255),
  310. }, # orange
  311. "centroidIn": {"enabled": True, "color": (0, 0, 255, 255)}, # blue
  312. "centroidOut": {
  313. "enabled": True,
  314. "color": (165, 42, 42, 255),
  315. }, # brown
  316. "centroidDup": {
  317. "enabled": True,
  318. "color": (156, 62, 206, 255),
  319. }, # violet
  320. "nodeOne": {"enabled": True, "color": (255, 0, 0, 255)}, # red
  321. "nodeTwo": {
  322. "enabled": True,
  323. "color": (0, 86, 45, 255),
  324. }, # dark green
  325. "vertex": {
  326. "enabled": False,
  327. "color": (255, 20, 147, 255),
  328. }, # deep pink
  329. "area": {"enabled": True, "color": (217, 255, 217, 255)}, # green
  330. "direction": {"enabled": False, "color": (255, 0, 0, 255)}, # red
  331. },
  332. # display
  333. "lineWidth": {"value": 2, "units": "screen pixels"},
  334. # snapping
  335. "snapping": {
  336. "value": 10,
  337. "unit": 0, # new
  338. "units": "screen pixels", # old for backwards comp.
  339. },
  340. "snapToVertex": {"enabled": True},
  341. # digitize new record
  342. "addRecord": {"enabled": True},
  343. "layer": {"value": 1},
  344. "category": {"value": 1},
  345. "categoryMode": {"selection": 0},
  346. # delete existing feature(s)
  347. "delRecord": {"enabled": True},
  348. # query tool
  349. "query": {"selection": 0, "box": True},
  350. "queryLength": {"than-selection": 0, "thresh": 0},
  351. "queryDangle": {"than-selection": 0, "thresh": 0},
  352. # select feature (point, line, centroid, boundary)
  353. "selectType": {
  354. "point": {"enabled": True},
  355. "line": {"enabled": True},
  356. "centroid": {"enabled": True},
  357. "boundary": {"enabled": True},
  358. },
  359. "selectThresh": {
  360. "value": 10,
  361. "unit": 0, # new
  362. "units": "screen pixels", # old for backwards comp.
  363. },
  364. "checkForDupl": {"enabled": False},
  365. "selectInside": {"enabled": False},
  366. # exit
  367. "saveOnExit": {
  368. "enabled": False,
  369. },
  370. # break lines on intersection
  371. "breakLines": {
  372. "enabled": True,
  373. },
  374. # close boundary (snap to the first node)
  375. "closeBoundary": {
  376. "enabled": False,
  377. },
  378. },
  379. #
  380. # plots for profiles, histograms, and scatterplots
  381. #
  382. "profile": {
  383. "raster": {
  384. "pcolor": (0, 0, 255, 255), # line color
  385. "pwidth": 1, # line width
  386. "pstyle": "solid", # line pen style
  387. "datatype": "cell", # raster type
  388. },
  389. "font": {
  390. "titleSize": 12,
  391. "axisSize": 11,
  392. "legendSize": 10,
  393. "defaultSize": 11,
  394. "family": wx.FONTFAMILY_SWISS,
  395. "style": wx.FONTSTYLE_NORMAL,
  396. "weight": wx.FONTWEIGHT_NORMAL,
  397. },
  398. "marker": {
  399. "color": (0, 0, 0, 255),
  400. "fill": "transparent",
  401. "size": 2,
  402. "type": "triangle",
  403. "legend": _("Segment break"),
  404. },
  405. "grid": {
  406. "color": (200, 200, 200, 255),
  407. "enabled": True,
  408. },
  409. "x-axis": {
  410. "type": "auto", # axis format
  411. "min": 0, # axis min for custom axis range
  412. "max": 0, # axis max for custom axis range
  413. "log": False,
  414. },
  415. "y-axis": {
  416. "type": "auto", # axis format
  417. "min": 0, # axis min for custom axis range
  418. "max": 0, # axis max for custom axis range
  419. "log": False,
  420. },
  421. "legend": {"enabled": True},
  422. },
  423. "histogram": {
  424. "raster": {
  425. "pcolor": (0, 0, 255, 255), # line color
  426. "pwidth": 1, # line width
  427. "pstyle": "solid", # line pen style
  428. "datatype": "cell", # raster type
  429. },
  430. "font": {
  431. "titleSize": 12,
  432. "axisSize": 11,
  433. "legendSize": 10,
  434. "defaultSize": 11,
  435. "family": wx.FONTFAMILY_SWISS,
  436. "style": wx.FONTSTYLE_NORMAL,
  437. "weight": wx.FONTWEIGHT_NORMAL,
  438. },
  439. "grid": {
  440. "color": (200, 200, 200, 255),
  441. "enabled": True,
  442. },
  443. "x-axis": {
  444. "type": "auto", # axis format
  445. "min": 0, # axis min for custom axis range
  446. "max": 0, # axis max for custom axis range
  447. "log": False,
  448. },
  449. "y-axis": {
  450. "type": "auto", # axis format
  451. "min": 0, # axis min for custom axis range
  452. "max": 0, # axis max for custom axis range
  453. "log": False,
  454. },
  455. "legend": {"enabled": True},
  456. },
  457. "scatter": {
  458. "raster": {
  459. "pcolor": (0, 0, 255, 255),
  460. "pfill": "solid",
  461. "psize": 1,
  462. "ptype": "dot",
  463. # FIXME: this is only a quick fix
  464. # using also names used in a base class for compatibility
  465. # probably used only for initialization
  466. # base should be rewritten to not require this
  467. "pwidth": 1, # required by wxplot/base, maybe useless here
  468. "pstyle": "dot", # line pen style
  469. "plegend": _("Data point"),
  470. 0: {"datatype": "CELL"},
  471. 1: {"datatype": "CELL"},
  472. },
  473. "font": {
  474. "titleSize": 12,
  475. "axisSize": 11,
  476. "legendSize": 10,
  477. "defaultSize": 11,
  478. "family": wx.FONTFAMILY_SWISS,
  479. "style": wx.FONTSTYLE_NORMAL,
  480. "weight": wx.FONTWEIGHT_NORMAL,
  481. },
  482. "grid": {
  483. "color": (200, 200, 200, 255),
  484. "enabled": True,
  485. },
  486. "x-axis": {
  487. "type": "auto", # axis format
  488. "min": 0, # axis min for custom axis range
  489. "max": 0, # axis max for custom axis range
  490. "log": False,
  491. },
  492. "y-axis": {
  493. "type": "auto", # axis format
  494. "min": 0, # axis min for custom axis range
  495. "max": 0, # axis max for custom axis range
  496. "log": False,
  497. },
  498. "legend": {"enabled": True},
  499. },
  500. "gcpman": {
  501. "rms": {
  502. "highestonly": True,
  503. "sdfactor": 1,
  504. },
  505. "symbol": {
  506. "color": (0, 0, 255, 255),
  507. "hcolor": (255, 0, 0, 255),
  508. "scolor": (0, 255, 0, 255),
  509. "ucolor": (255, 165, 0, 255),
  510. "unused": True,
  511. "size": 8,
  512. "width": 2,
  513. },
  514. "map": {
  515. "overwrite": False,
  516. },
  517. },
  518. "nviz": {
  519. "view": {
  520. "persp": {
  521. "value": 20,
  522. "step": 2,
  523. },
  524. "position": {
  525. "x": 0.84,
  526. "y": 0.16,
  527. },
  528. "twist": {
  529. "value": 0,
  530. },
  531. "z-exag": {
  532. "min": 0,
  533. "max": 10,
  534. "value": 1,
  535. },
  536. "background": {
  537. "color": (255, 255, 255, 255), # white
  538. },
  539. },
  540. "fly": {
  541. "exag": {
  542. "move": 5,
  543. "turn": 5,
  544. }
  545. },
  546. "animation": {"fps": 24, "prefix": _("animation")},
  547. "surface": {
  548. "shine": {
  549. "map": False,
  550. "value": 60.0,
  551. },
  552. "color": {
  553. "map": True,
  554. "value": (100, 100, 100, 255), # constant: grey
  555. },
  556. "draw": {
  557. "wire-color": (136, 136, 136, 255),
  558. "mode": 1, # fine
  559. "style": 1, # surface
  560. "shading": 1, # gouraud
  561. "res-fine": 6,
  562. "res-coarse": 9,
  563. },
  564. "position": {
  565. "x": 0,
  566. "y": 0,
  567. "z": 0,
  568. },
  569. },
  570. "constant": {
  571. "color": (100, 100, 100, 255),
  572. "value": 0.0,
  573. "transp": 0,
  574. "resolution": 6,
  575. },
  576. "vector": {
  577. "lines": {
  578. "show": False,
  579. "width": 2,
  580. "color": (0, 0, 0, 255),
  581. "flat": False,
  582. "height": 0,
  583. "rgbcolumn": None,
  584. "sizecolumn": None,
  585. },
  586. "points": {
  587. "show": False,
  588. "size": 100,
  589. "autosize": True,
  590. "width": 2,
  591. "marker": 2,
  592. "color": (0, 0, 0, 255),
  593. "height": 0,
  594. "rgbcolumn": None,
  595. "sizecolumn": None,
  596. },
  597. },
  598. "volume": {
  599. "color": {
  600. "map": True,
  601. "value": (100, 100, 100, 255), # constant: grey
  602. },
  603. "draw": {
  604. "mode": 0, # isosurfaces
  605. "shading": 1, # gouraud
  606. "resolution": 3, # polygon resolution
  607. "box": False, # draw wire box
  608. },
  609. "shine": {
  610. "map": False,
  611. "value": 60,
  612. },
  613. "topo": {"map": None, "value": 0.0},
  614. "transp": {"map": None, "value": 0},
  615. "mask": {"map": None, "value": ""},
  616. "slice_position": {
  617. "x1": 0,
  618. "x2": 1,
  619. "y1": 0,
  620. "y2": 1,
  621. "z1": 0,
  622. "z2": 1,
  623. "axis": 0,
  624. },
  625. },
  626. "cplane": {
  627. "shading": 4,
  628. "rotation": {"rot": 180, "tilt": 0},
  629. "position": {"x": 0, "y": 0, "z": 0},
  630. },
  631. "light": {
  632. "position": {
  633. "x": 0.68,
  634. "y": -0.68,
  635. "z": 80,
  636. },
  637. "bright": 80,
  638. "color": (255, 255, 255, 255), # white
  639. "ambient": 20,
  640. },
  641. "fringe": {
  642. "elev": 55,
  643. "color": (128, 128, 128, 255), # grey
  644. },
  645. "arrow": {
  646. "color": (0, 0, 0),
  647. },
  648. "scalebar": {
  649. "color": (0, 0, 0),
  650. },
  651. },
  652. "modeler": {
  653. "disabled": {
  654. "color": (211, 211, 211, 255), # light grey
  655. },
  656. "action": {
  657. "color": {
  658. "valid": (180, 234, 154, 255), # light green
  659. "invalid": (255, 255, 255, 255), # white
  660. "running": (255, 0, 0, 255), # red
  661. },
  662. "size": {
  663. "width": 125,
  664. "height": 50,
  665. },
  666. "width": {
  667. "parameterized": 2,
  668. "default": 1,
  669. },
  670. },
  671. "data": {
  672. "color": {
  673. "raster": (215, 215, 248, 255), # light blue
  674. "raster3d": (215, 248, 215, 255), # light green
  675. "vector": (248, 215, 215, 255), # light red
  676. "dbtable": (255, 253, 194, 255), # light yellow
  677. },
  678. "size": {
  679. "width": 175,
  680. "height": 50,
  681. },
  682. },
  683. "loop": {
  684. "color": {
  685. "valid": (234, 226, 154, 255), # dark yellow
  686. },
  687. "size": {
  688. "width": 175,
  689. "height": 40,
  690. },
  691. },
  692. "if-else": {
  693. "size": {
  694. "width": 150,
  695. "height": 40,
  696. },
  697. },
  698. "comment": {
  699. "color": (255, 233, 208, 255), # light yellow
  700. "size": {
  701. "width": 200,
  702. "height": 100,
  703. },
  704. },
  705. },
  706. "mapswipe": {
  707. "cursor": {
  708. "color": (0, 0, 0, 255),
  709. "size": 12,
  710. "width": 1,
  711. "type": {
  712. "selection": 0,
  713. },
  714. },
  715. },
  716. "animation": {
  717. "bgcolor": {
  718. "color": (255, 255, 255, 255),
  719. },
  720. "nprocs": {
  721. "value": -1,
  722. },
  723. "font": {
  724. "bgcolor": (255, 255, 255, 255),
  725. "fgcolor": (0, 0, 0, 255),
  726. },
  727. "temporal": {
  728. "format": "%Y-%m-%d %H:%M:%S",
  729. "nodata": {"enable": False},
  730. },
  731. },
  732. }
  733. # quick fix, http://trac.osgeo.org/grass/ticket/1233
  734. # TODO
  735. if sys.platform == "darwin":
  736. self.defaultSettings["general"]["defWindowPos"]["enabled"] = False
  737. def _internalSettings(self):
  738. """Define internal settings (based on user settings)"""
  739. self.internalSettings = {}
  740. for group in list(self.userSettings.keys()):
  741. self.internalSettings[group] = {}
  742. for key in list(self.userSettings[group].keys()):
  743. self.internalSettings[group][key] = {}
  744. # self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath()
  745. self.internalSettings["appearance"]["elementListExpand"]["choices"] = (
  746. _("Collapse all except PERMANENT and current"),
  747. _("Collapse all except PERMANENT"),
  748. _("Collapse all except current"),
  749. _("Collapse all"),
  750. _("Expand all"),
  751. )
  752. self.internalSettings["language"]["locale"]["choices"] = tuple(self.locs)
  753. self.internalSettings["atm"]["leftDbClick"]["choices"] = (
  754. _("Edit selected record"),
  755. _("Display selected"),
  756. )
  757. self.internalSettings["cmd"]["verbosity"]["choices"] = (
  758. "grassenv",
  759. "verbose",
  760. "quiet",
  761. )
  762. self.internalSettings["appearance"]["iconTheme"]["choices"] = ("grass",)
  763. self.internalSettings["appearance"]["menustyle"]["choices"] = (
  764. _("Classic (labels only)"),
  765. _("Combined (labels and module names)"),
  766. _("Expert (module names only)"),
  767. )
  768. self.internalSettings["appearance"]["gSelectPopupHeight"]["min"] = 50
  769. # there is also maxHeight given to TreeCtrlComboPopup.GetAdjustedSize
  770. self.internalSettings["appearance"]["gSelectPopupHeight"]["max"] = 1000
  771. self.internalSettings["appearance"]["commandNotebook"]["choices"] = (
  772. _("Basic top"),
  773. _("Basic left"),
  774. _("Fancy green"),
  775. _("List left"),
  776. )
  777. self.internalSettings["display"]["driver"]["choices"] = ["cairo", "png"]
  778. self.internalSettings["display"]["statusbarMode"][
  779. "choices"
  780. ] = None # set during MapFrame init
  781. self.internalSettings["display"]["mouseWheelZoom"]["choices"] = (
  782. _("Zoom and recenter"),
  783. _("Zoom to mouse cursor"),
  784. _("Nothing"),
  785. )
  786. self.internalSettings["display"]["scrollDirection"]["choices"] = (
  787. _("Scroll forward to zoom in"),
  788. _("Scroll back to zoom in"),
  789. )
  790. self.internalSettings["nviz"]["view"] = {}
  791. self.internalSettings["nviz"]["view"]["twist"] = {}
  792. self.internalSettings["nviz"]["view"]["twist"]["min"] = -180
  793. self.internalSettings["nviz"]["view"]["twist"]["max"] = 180
  794. self.internalSettings["nviz"]["view"]["persp"] = {}
  795. self.internalSettings["nviz"]["view"]["persp"]["min"] = 1
  796. self.internalSettings["nviz"]["view"]["persp"]["max"] = 100
  797. self.internalSettings["nviz"]["view"]["height"] = {}
  798. self.internalSettings["nviz"]["view"]["height"]["value"] = -1
  799. self.internalSettings["nviz"]["view"]["z-exag"] = {}
  800. self.internalSettings["nviz"]["view"]["z-exag"]["llRatio"] = 1
  801. self.internalSettings["nviz"]["view"]["rotation"] = None
  802. self.internalSettings["nviz"]["view"]["focus"] = {}
  803. self.internalSettings["nviz"]["view"]["focus"]["x"] = -1
  804. self.internalSettings["nviz"]["view"]["focus"]["y"] = -1
  805. self.internalSettings["nviz"]["view"]["focus"]["z"] = -1
  806. self.internalSettings["nviz"]["view"]["dir"] = {}
  807. self.internalSettings["nviz"]["view"]["dir"]["x"] = -1
  808. self.internalSettings["nviz"]["view"]["dir"]["y"] = -1
  809. self.internalSettings["nviz"]["view"]["dir"]["z"] = -1
  810. self.internalSettings["nviz"]["view"]["dir"]["use"] = False
  811. for decor in ("arrow", "scalebar"):
  812. self.internalSettings["nviz"][decor] = {}
  813. self.internalSettings["nviz"][decor]["position"] = {}
  814. self.internalSettings["nviz"][decor]["position"]["x"] = 0
  815. self.internalSettings["nviz"][decor]["position"]["y"] = 0
  816. self.internalSettings["nviz"][decor]["size"] = 100
  817. self.internalSettings["nviz"]["vector"] = {}
  818. self.internalSettings["nviz"]["vector"]["points"] = {}
  819. self.internalSettings["nviz"]["vector"]["points"]["marker"] = (
  820. "x",
  821. _("box"),
  822. _("sphere"),
  823. _("cube"),
  824. _("diamond"),
  825. _("aster"),
  826. _("gyro"),
  827. _("histogram"),
  828. )
  829. self.internalSettings["vdigit"]["bgmap"] = {}
  830. self.internalSettings["vdigit"]["bgmap"]["value"] = ""
  831. self.internalSettings["mapswipe"]["cursor"]["type"] = {}
  832. self.internalSettings["mapswipe"]["cursor"]["type"]["choices"] = (
  833. _("cross"),
  834. _("box"),
  835. _("circle"),
  836. )
  837. def ReadSettingsFile(self, settings=None):
  838. """Reads settings file (mapset, location, gisdbase)"""
  839. if settings is None:
  840. settings = self.userSettings
  841. if os.path.exists(self.filePath):
  842. self._readFile(settings)
  843. elif os.path.exists(self.legacyFilePath):
  844. self._readLegacyFile(settings)
  845. # set environment variables
  846. font = self.Get(group="display", key="font", subkey="type")
  847. enc = self.Get(group="display", key="font", subkey="encoding")
  848. if font:
  849. os.environ["GRASS_FONT"] = font
  850. if enc:
  851. os.environ["GRASS_ENCODING"] = enc
  852. def _readFile(self, settings=None):
  853. """Read settings from file (wx.json) to dict,
  854. assumes file exists.
  855. :param settings: dict where to store settings (None for self.userSettings)
  856. """
  857. try:
  858. with open(self.filePath, "r") as f:
  859. settings.update(json.load(f, object_hook=settings_JSON_decode_hook))
  860. except json.JSONDecodeError as e:
  861. sys.stderr.write(
  862. _("Unable to read settings file <{path}>:\n{err}").format(
  863. path=self.filePath, err=e
  864. )
  865. )
  866. def _readLegacyFile(self, settings=None):
  867. """Read settings from legacy file (wx) to dict,
  868. assumes file exists.
  869. :param settings: dict where to store settings (None for self.userSettings)
  870. """
  871. if settings is None:
  872. settings = self.userSettings
  873. try:
  874. fd = open(self.legacyFilePath, "r")
  875. except IOError:
  876. sys.stderr.write(
  877. _("Unable to read settings file <%s>\n") % self.legacyFilePath
  878. )
  879. return
  880. try:
  881. line = ""
  882. for line in fd.readlines():
  883. line = line.rstrip("%s" % os.linesep)
  884. group, key = line.split(self.sep)[0:2]
  885. kv = line.split(self.sep)[2:]
  886. subkeyMaster = None
  887. if len(kv) % 2 != 0: # multiple (e.g. nviz)
  888. subkeyMaster = kv[0]
  889. del kv[0]
  890. idx = 0
  891. while idx < len(kv):
  892. if subkeyMaster:
  893. subkey = [subkeyMaster, kv[idx]]
  894. else:
  895. subkey = kv[idx]
  896. value = kv[idx + 1]
  897. value = self._parseValue(value, read=True)
  898. self.Append(settings, group, key, subkey, value)
  899. idx += 2
  900. except ValueError as e:
  901. print(
  902. _(
  903. "Error: Reading settings from file <%(file)s> failed.\n"
  904. "\t\tDetails: %(detail)s\n"
  905. "\t\tLine: '%(line)s'\n"
  906. )
  907. % {"file": self.legacyFilePath, "detail": e, "line": line},
  908. file=sys.stderr,
  909. )
  910. fd.close()
  911. fd.close()
  912. def SaveToFile(self, settings=None):
  913. """Save settings to the file"""
  914. if settings is None:
  915. settings = self.userSettings
  916. dirPath = GetSettingsPath()
  917. if not os.path.exists(dirPath):
  918. try:
  919. os.mkdir(dirPath)
  920. except:
  921. GError(_("Unable to create settings directory"))
  922. return
  923. try:
  924. with open(self.filePath, "w") as f:
  925. json.dump(settings, f, indent=2, cls=SettingsJSONEncoder)
  926. except IOError as e:
  927. raise GException(e)
  928. except Exception as e:
  929. raise GException(
  930. _(
  931. "Writing settings to file <%(file)s> failed."
  932. "\n\nDetails: %(detail)s"
  933. )
  934. % {"file": self.filePath, "detail": e}
  935. )
  936. return self.filePath
  937. def _parseValue(self, value, read=False):
  938. """Parse value to be store in settings file"""
  939. if read: # -> read settings (cast values)
  940. if value == "True":
  941. value = True
  942. elif value == "False":
  943. value = False
  944. elif value == "None":
  945. value = None
  946. elif ":" in value: # -> color
  947. try:
  948. value = tuple(map(int, value.split(":")))
  949. except ValueError: # -> string
  950. pass
  951. else:
  952. try:
  953. value = int(value)
  954. except ValueError:
  955. try:
  956. value = float(value)
  957. except ValueError:
  958. pass
  959. else: # -> write settings
  960. if isinstance(value, type(())): # -> color
  961. value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2])
  962. return value
  963. def Get(self, group, key=None, subkey=None, settings_type="user"):
  964. """Get value by key/subkey
  965. Raise KeyError if key is not found
  966. :param group: settings group
  967. :param key: (value, None)
  968. :param subkey: (value, list or None)
  969. :param settings_type: 'user', 'internal', 'default'
  970. :return: value
  971. """
  972. if settings_type == "user":
  973. settings = self.userSettings
  974. elif settings_type == "internal":
  975. settings = self.internalSettings
  976. else:
  977. settings = self.defaultSettings
  978. try:
  979. if subkey is None:
  980. if key is None:
  981. return settings[group]
  982. else:
  983. return settings[group][key]
  984. else:
  985. if isinstance(subkey, type(tuple())) or isinstance(
  986. subkey, type(list())
  987. ):
  988. return settings[group][key][subkey[0]][subkey[1]]
  989. else:
  990. return settings[group][key][subkey]
  991. except KeyError:
  992. print(
  993. "Settings: unable to get value '%s:%s:%s'\n" % (group, key, subkey),
  994. file=sys.stderr,
  995. )
  996. def Set(self, group, value, key=None, subkey=None, settings_type="user"):
  997. """Set value of key/subkey
  998. Raise KeyError if group/key is not found
  999. :param group: settings group
  1000. :param key: key (value, None)
  1001. :param subkey: subkey (value, list or None)
  1002. :param value: value
  1003. :param settings_type: 'user', 'internal', 'default'
  1004. """
  1005. if settings_type == "user":
  1006. settings = self.userSettings
  1007. elif settings_type == "internal":
  1008. settings = self.internalSettings
  1009. else:
  1010. settings = self.defaultSettings
  1011. try:
  1012. if subkey is None:
  1013. if key is None:
  1014. settings[group] = value
  1015. else:
  1016. settings[group][key] = value
  1017. else:
  1018. if isinstance(subkey, type(tuple())) or isinstance(
  1019. subkey, type(list())
  1020. ):
  1021. settings[group][key][subkey[0]][subkey[1]] = value
  1022. else:
  1023. settings[group][key][subkey] = value
  1024. except KeyError:
  1025. raise GException(
  1026. "%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey)
  1027. )
  1028. def Append(self, dict, group, key, subkey, value, overwrite=True):
  1029. """Set value of key/subkey
  1030. Create group/key/subkey if not exists
  1031. :param dict: settings dictionary to use
  1032. :param group: settings group
  1033. :param key: key
  1034. :param subkey: subkey (value or list)
  1035. :param value: value
  1036. :param overwrite: True to overwrite existing value
  1037. """
  1038. hasValue = True
  1039. if group not in dict:
  1040. dict[group] = {}
  1041. hasValue = False
  1042. if key not in dict[group]:
  1043. dict[group][key] = {}
  1044. hasValue = False
  1045. if isinstance(subkey, list):
  1046. # TODO: len(subkey) > 2
  1047. if subkey[0] not in dict[group][key]:
  1048. dict[group][key][subkey[0]] = {}
  1049. hasValue = False
  1050. if subkey[1] not in dict[group][key][subkey[0]]:
  1051. hasValue = False
  1052. try:
  1053. if overwrite or (not overwrite and not hasValue):
  1054. dict[group][key][subkey[0]][subkey[1]] = value
  1055. except TypeError:
  1056. print(
  1057. _("Unable to parse settings '%s'") % value
  1058. + " ("
  1059. + group
  1060. + ":"
  1061. + key
  1062. + ":"
  1063. + subkey[0]
  1064. + ":"
  1065. + subkey[1]
  1066. + ")",
  1067. file=sys.stderr,
  1068. )
  1069. else:
  1070. if subkey not in dict[group][key]:
  1071. hasValue = False
  1072. try:
  1073. if overwrite or (not overwrite and not hasValue):
  1074. dict[group][key][subkey] = value
  1075. except TypeError:
  1076. print(
  1077. _("Unable to parse settings '%s'") % value
  1078. + " ("
  1079. + group
  1080. + ":"
  1081. + key
  1082. + ":"
  1083. + subkey
  1084. + ")",
  1085. file=sys.stderr,
  1086. )
  1087. def GetDefaultSettings(self):
  1088. """Get default user settings"""
  1089. return self.defaultSettings
  1090. def Reset(self, key=None):
  1091. """Reset to default settings
  1092. :param key: key in settings dict (None for all keys)
  1093. """
  1094. if not key:
  1095. self.userSettings = copy.deepcopy(self.defaultSettings)
  1096. else:
  1097. self.userSettings[key] = copy.deepcopy(self.defaultSettings[key])
  1098. UserSettings = Settings()
  1099. def GetDisplayVectSettings():
  1100. settings = list()
  1101. if not UserSettings.Get(
  1102. group="vectorLayer", key="featureColor", subkey=["transparent", "enabled"]
  1103. ):
  1104. featureColor = UserSettings.Get(
  1105. group="vectorLayer", key="featureColor", subkey="color"
  1106. )
  1107. settings.append(
  1108. "color=%s" % rgb2str.get(featureColor, ":".join(map(str, featureColor)))
  1109. )
  1110. else:
  1111. settings.append("color=none")
  1112. if not UserSettings.Get(
  1113. group="vectorLayer", key="areaFillColor", subkey=["transparent", "enabled"]
  1114. ):
  1115. fillColor = UserSettings.Get(
  1116. group="vectorLayer", key="areaFillColor", subkey="color"
  1117. )
  1118. settings.append(
  1119. "fcolor=%s" % rgb2str.get(fillColor, ":".join(map(str, fillColor)))
  1120. )
  1121. else:
  1122. settings.append("fcolor=none")
  1123. settings.append(
  1124. "width=%s" % UserSettings.Get(group="vectorLayer", key="line", subkey="width")
  1125. )
  1126. settings.append(
  1127. "icon=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="symbol")
  1128. )
  1129. settings.append(
  1130. "size=%s" % UserSettings.Get(group="vectorLayer", key="point", subkey="size")
  1131. )
  1132. types = []
  1133. for ftype in ["point", "line", "boundary", "centroid", "area", "face"]:
  1134. if UserSettings.Get(
  1135. group="vectorLayer", key="showType", subkey=[ftype, "enabled"]
  1136. ):
  1137. types.append(ftype)
  1138. settings.append("type=%s" % ",".join(types))
  1139. if UserSettings.Get(group="vectorLayer", key="randomColors", subkey="enabled"):
  1140. settings.append("-c")
  1141. return settings