settings.py 42 KB

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