settings.py 46 KB

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