preferences.py 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630
  1. """
  2. @package preferences
  3. @brief User preferences dialog
  4. Sets default display font, etc.
  5. Classes:
  6. - PreferencesDialog
  7. - SetDefaultFont
  8. - MapsetAccess
  9. (C) 2007-2008 by the GRASS Development Team
  10. This program is free software under the GNU General Public
  11. License (>=v2). Read the file COPYING that comes with GRASS
  12. for details.
  13. @author Michael Barton (Arizona State University)
  14. Martin Landa <landa.martin gmail.com>
  15. """
  16. import os
  17. import sys
  18. import copy
  19. import stat
  20. if os.name in ('posix', 'mac'):
  21. import pwd
  22. import wx
  23. import wx.lib.filebrowsebutton as filebrowse
  24. import wx.lib.colourselect as csel
  25. import wx.lib.mixins.listctrl as listmix
  26. from wx.lib.wordwrap import wordwrap
  27. import gcmd
  28. import grassenv
  29. import utils
  30. import globalvar
  31. from debug import Debug as Debug
  32. class Settings:
  33. """Generic class where to store settings"""
  34. def __init__(self):
  35. #
  36. # settings filename
  37. #
  38. self.fileName = ".grasswx7"
  39. self.filePath = None
  40. #
  41. # default settings
  42. #
  43. self.defaultSettings = {
  44. #
  45. # general
  46. #
  47. 'general': {
  48. # current mapset search path
  49. 'mapsetPath' : {
  50. 'selection' : 0
  51. },
  52. # use default window layout (layer manager, displays, ...)
  53. 'defWindowPos' : {
  54. 'enabled' : False,
  55. 'dim' : ''
  56. },
  57. # expand/collapse element list
  58. 'elementListExpand' : {
  59. 'selection' : 0
  60. },
  61. },
  62. 'manager' : {
  63. # show opacity level widget
  64. 'changeOpacityLevel' : {
  65. 'enabled' : False
  66. },
  67. # ask when removing layer from layer tree
  68. 'askOnRemoveLayer' : {
  69. 'enabled' : True
  70. },
  71. },
  72. #
  73. # display
  74. #
  75. 'display': {
  76. 'displayFont' : {
  77. 'value' : ''
  78. },
  79. 'driver': {
  80. 'type': 'default'
  81. },
  82. 'compResolution' : {
  83. 'enabled' : False
  84. },
  85. 'autoRendering': {
  86. 'enabled' : False
  87. },
  88. 'statusbarMode': {
  89. 'selection' : 0
  90. },
  91. },
  92. #
  93. # advanced
  94. #
  95. 'advanced' : {
  96. 'settingsFile' : {
  97. 'type' : 'home'
  98. }, # home, gisdbase, location, mapset
  99. 'digitInterface' : {
  100. 'type' : 'vdigit'
  101. }, # vedit, vdigit
  102. 'iconTheme' : {
  103. 'type' : 'silk'
  104. }, # grass, silk
  105. },
  106. #
  107. # Attribute Table Manager
  108. #
  109. 'atm' : {
  110. 'highlight' : {
  111. 'color' : (255, 255, 0, 255),
  112. 'width' : 2
  113. },
  114. 'leftDbClick' : {
  115. 'selection' : 0
  116. },
  117. },
  118. #
  119. # Command
  120. #
  121. 'cmd': {
  122. 'overwrite' : {
  123. 'enabled' : False
  124. },
  125. 'closeDlg' : {
  126. 'enabled' : False
  127. },
  128. 'verbosity' : {
  129. 'selection' : 'grassenv'
  130. },
  131. 'rasterOverlay' : {
  132. 'enabled' : False
  133. },
  134. },
  135. #
  136. # Workspace
  137. #
  138. 'workspace' : {
  139. 'posDisplay' : {
  140. 'enabled' : False
  141. },
  142. 'posManager' : {
  143. 'enabled' : False
  144. },
  145. },
  146. #
  147. # vdigit
  148. #
  149. 'vdigit' : {
  150. # symbology
  151. 'symbolHighlight' : {
  152. 'enabled' : None,
  153. 'color' : (255, 255, 0, 255)
  154. }, # yellow
  155. 'symbolHighlightDupl' : {
  156. 'enabled' : None,
  157. 'color' : (255, 72, 0, 255)
  158. }, # red
  159. 'symbolPoint' : {
  160. 'enabled' : True,
  161. 'color' : (0, 0, 0, 255)
  162. }, # black
  163. 'symbolLine' : {
  164. 'enabled' : True,
  165. 'color' : (0, 0, 0, 255)
  166. }, # black
  167. 'symbolBoundaryNo' : {
  168. 'enabled' : True,
  169. 'color' : (126, 126, 126, 255)
  170. }, # grey
  171. 'symbolBoundaryOne' : {
  172. 'enabled' : True,
  173. 'color' : (0, 255, 0, 255)
  174. }, # green
  175. 'symbolBoundaryTwo' : {
  176. 'enabled' : True,
  177. 'color' : (255, 135, 0, 255)
  178. }, # orange
  179. 'symbolCentroidIn' : {
  180. 'enabled' : True,
  181. 'color' : (0, 0, 255, 255)
  182. }, # blue
  183. 'symbolCentroidOut' : {
  184. 'enabled' : True,
  185. 'color' : (165, 42, 42, 255)
  186. }, # brown
  187. 'symbolCentroidDup' : {
  188. 'enabled' : True,
  189. 'color' : (156, 62, 206, 255)
  190. }, # violet
  191. 'symbolNodeOne' : {
  192. 'enabled' : True,
  193. 'color' : (255, 0, 0, 255)
  194. }, # red
  195. 'symbolNodeTwo' : {
  196. 'enabled' : True,
  197. 'color' : (0, 86, 45, 255)
  198. }, # dark green
  199. 'symbolVertex' : {
  200. 'enabled' : False,
  201. 'color' : (255, 20, 147, 255)
  202. }, # deep pink
  203. 'symbolDirection' : {
  204. 'enabled' : False,
  205. 'color' : (255, 0, 0, 255)
  206. }, # red
  207. # display
  208. 'lineWidth' : {
  209. 'value' : 2,
  210. 'units' : 'screen pixels'
  211. },
  212. # snapping
  213. 'snapping' : {
  214. 'value' : 10,
  215. 'units' : 'screen pixels'
  216. },
  217. 'snapToVertex' : {
  218. 'enabled' : False
  219. },
  220. 'backgroundMap' : {
  221. 'value' : ''
  222. },
  223. # digitize new record
  224. 'addRecord' : {
  225. 'enabled' : True
  226. },
  227. 'layer' :{
  228. 'value' : 1
  229. },
  230. 'category' : {
  231. 'value' : 1
  232. },
  233. 'categoryMode' : {
  234. 'selection' : 0
  235. },
  236. # delete existing feature(s)
  237. 'delRecord' : {
  238. 'enabled' : True
  239. },
  240. # query tool
  241. 'query' : {
  242. 'selection' : 0,
  243. 'box' : True
  244. },
  245. 'queryLength' : {
  246. 'than-selection' : 0,
  247. 'thresh' : 0
  248. },
  249. 'queryDangle' : {
  250. 'than-selection' : 0,
  251. 'thresh' : 0
  252. },
  253. # select feature (point, line, centroid, boundary)
  254. 'selectFeaturePoint' : {
  255. 'enabled' : True
  256. },
  257. 'selectFeatureLine' : {
  258. 'enabled' : True
  259. },
  260. 'selectFeatureCentroid' : {
  261. 'enabled' : True
  262. },
  263. 'selectFeatureBoundary' : {
  264. 'enabled' : True
  265. },
  266. 'selectThresh' : {
  267. 'value' : 10,
  268. 'units' : 'screen pixels'
  269. },
  270. 'checkForDupl' : {
  271. 'enabled' : False
  272. },
  273. # exit
  274. 'saveOnExit' : {
  275. 'enabled' : False
  276. },
  277. },
  278. 'profile': {
  279. 'raster0' : {
  280. 'pcolor' : (0, 0, 255, 255), # profile line color
  281. 'pwidth' : 1, # profile line width
  282. 'pstyle' : 'solid', # profile line pen style
  283. },
  284. 'raster1' : {
  285. 'pcolor' : (255, 0, 0, 255),
  286. 'pwidth' : 1,
  287. 'pstyle' : 'solid',
  288. },
  289. 'raster2' : {
  290. 'pcolor' : (0, 255, 0, 255),
  291. 'pwidth' : 1,
  292. 'pstyle' : 'solid',
  293. },
  294. 'font' : {
  295. 'titleSize' : 12,
  296. 'axisSize' : 11,
  297. 'legendSize' : 10,
  298. },
  299. 'marker' : {
  300. 'color' : wx.Colour(0, 0, 0),
  301. 'fill' : 'transparent',
  302. 'size' : 2,
  303. 'type' : 'triangle',
  304. 'legend' : _('Segment break'),
  305. },
  306. 'grid' : {
  307. 'color' : wx.Colour(200,200,200) ,
  308. 'enabled' : True,
  309. },
  310. 'x-axis' : {
  311. 'type' : 'auto', # axis format
  312. 'min' : 0, # axis min for custom axis range
  313. 'max': 0, # axis max for custom axis range
  314. 'log' : False,
  315. },
  316. 'y-axis' : {
  317. 'type' : 'auto', # axis format
  318. 'min' : 0, # axis min for custom axis range
  319. 'max': 0, # axis max for custom axis range
  320. 'log' : False,
  321. },
  322. 'legend' : {
  323. 'enabled' : True
  324. },
  325. },
  326. 'georect' : {
  327. 'symbol' : {
  328. 'color' : (0, 0, 255, 255),
  329. 'width' : 2,
  330. },
  331. },
  332. 'nviz' : {
  333. 'view' : {
  334. 'persp' : {
  335. 'value' : 40,
  336. 'step' : 5,
  337. },
  338. 'pos' : {
  339. 'x' : 0.85,
  340. 'y' : 0.85,
  341. },
  342. 'height' : {
  343. 'min' : -3000, # TODO: determine min/max height
  344. 'max' : 4000,
  345. 'step' : 100,
  346. },
  347. 'twist' : {
  348. 'value' : 0,
  349. 'step' : 5,
  350. },
  351. 'z-exag' : {
  352. 'value': 1,
  353. 'step' : 1,
  354. },
  355. },
  356. 'surface' : {
  357. 'shine': {
  358. 'map' : False,
  359. 'value' : 60.0,
  360. },
  361. 'color' : {
  362. 'map' : True,
  363. 'value' : (0, 0, 0, 255), # constant: black
  364. },
  365. 'draw' : {
  366. 'color' : (136, 136, 136, 255),
  367. 'mode' : 1, # fine
  368. 'style' : 1, # surface
  369. 'shading' : 1, # gouraud
  370. 'res-fine' : 6,
  371. 'res-coarse' : 9,
  372. },
  373. 'position' : {
  374. 'x' : 0,
  375. 'y' : 0,
  376. 'z' : 0,
  377. },
  378. },
  379. 'vector' : {
  380. 'lines' : {
  381. 'width' : 2,
  382. 'color' : (0, 0, 255, 255), # blue
  383. 'flat' : False,
  384. 'height' : 0,
  385. },
  386. },
  387. 'settings': {
  388. 'general' : {
  389. 'bgcolor' : (255, 255, 255, 255), # white
  390. },
  391. },
  392. },
  393. }
  394. #
  395. # user settings
  396. #
  397. self.userSettings = copy.deepcopy(self.defaultSettings)
  398. try:
  399. self.ReadSettingsFile()
  400. except gcmd.SettingsError, e:
  401. print >> sys.stderr, e.message
  402. #
  403. # internal settings (based on user settings)
  404. #
  405. self.internalSettings = {}
  406. for group in self.userSettings.keys():
  407. if group == 'vdigit':
  408. continue # skip digitization settings (separate window frame)
  409. self.internalSettings[group] = {}
  410. for key in self.userSettings[group].keys():
  411. self.internalSettings[group][key] = {}
  412. self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath()
  413. self.internalSettings['general']['mapsetPath']['choices'] = (_('Mapset search path'),
  414. _('All available mapsets'))
  415. self.internalSettings['general']['elementListExpand']['choices'] = (_("Collapse all except PERMANENT and current"),
  416. _("Collapse all except PERMANENT"),
  417. _("Collapse all"),
  418. _("Expand all"))
  419. self.internalSettings['atm']['leftDbClick']['choices'] = (_('Edit selected record'),
  420. _('Display selected'))
  421. self.internalSettings['advanced']['settingsFile']['choices'] = ('home',
  422. 'gisdbase',
  423. 'location',
  424. 'mapset')
  425. self.internalSettings['advanced']['iconTheme']['choices'] = ('grass',
  426. 'silk')
  427. self.internalSettings['advanced']['digitInterface']['choices'] = ('vedit',
  428. 'vdigit')
  429. self.internalSettings['cmd']['verbosity']['choices'] = ('grassenv',
  430. 'verbose',
  431. 'quiet')
  432. self.internalSettings['display']['driver']['choices'] = ['default']
  433. self.internalSettings['display']['statusbarMode']['choices'] = globalvar.MAP_DISPLAY_STATUSBAR_MODE
  434. self.internalSettings['nviz']['view'] = {}
  435. self.internalSettings['nviz']['view']['twist'] = {}
  436. self.internalSettings['nviz']['view']['twist']['min'] = -180
  437. self.internalSettings['nviz']['view']['twist']['max'] = 180
  438. self.internalSettings['nviz']['view']['persp'] = {}
  439. self.internalSettings['nviz']['view']['persp']['min'] = 1
  440. self.internalSettings['nviz']['view']['persp']['max'] = 100
  441. self.internalSettings['nviz']['view']['height'] = {}
  442. self.internalSettings['nviz']['view']['height']['value'] = -1
  443. def GetMapsetPath(self):
  444. """Store mapset search path"""
  445. all, access = utils.ListOfMapsets()
  446. if self.Get(group='general', key='mapsetPath', subkey='selection') == 0:
  447. return access
  448. else:
  449. return all
  450. def ReadSettingsFile(self, settings=None):
  451. """Reads settings file (mapset, location, gisdbase)"""
  452. if settings is None:
  453. settings = self.userSettings
  454. # look for settings file
  455. # -> mapser
  456. # -> location
  457. # -> gisdbase
  458. gisdbase = grassenv.GetGRASSVariable("GISDBASE")
  459. location_name = grassenv.GetGRASSVariable("LOCATION_NAME")
  460. mapset_name = grassenv.GetGRASSVariable("MAPSET")
  461. mapset_file = os.path.join(gisdbase, location_name, mapset_name, self.fileName)
  462. location_file = os.path.join(gisdbase, location_name, self.fileName)
  463. gisdbase_file = os.path.join(gisdbase, self.fileName)
  464. home_file = os.path.join(os.path.expanduser("~"), self.fileName) # MS Windows fix ?
  465. if os.path.isfile(mapset_file):
  466. self.filePath = mapset_file
  467. elif os.path.isfile(location_file):
  468. self.filePath = location_file
  469. elif os.path.isfile(gisdbase_file):
  470. self.filePath = gisdbase_file
  471. elif os.path.isfile(home_file):
  472. self.filePath = home_file
  473. if self.filePath:
  474. self.__ReadFile(self.filePath, settings)
  475. def __ReadFile(self, filename, settings=None):
  476. """Read settings from file to dict"""
  477. if settings is None:
  478. settings = self.userSettings
  479. try:
  480. file = open(filename, "r")
  481. for line in file.readlines():
  482. line = line.rstrip('%s' % os.linesep)
  483. group, key = line.split(':')[0:2]
  484. kv = line.split(':')[2:]
  485. subkeyMaster = None
  486. if len(kv) % 2 != 0: # multiple (e.g. nviz)
  487. subkeyMaster = kv[0]
  488. del kv[0]
  489. idx = 0
  490. while idx < len(kv):
  491. if subkeyMaster:
  492. subkey = [subkeyMaster, kv[idx]]
  493. else:
  494. subkey = kv[idx]
  495. value = kv[idx+1]
  496. if len(value) == 0:
  497. self.Append(settings, group, key, subkey, '')
  498. else:
  499. # casting
  500. if value == 'True':
  501. value = True
  502. elif value == 'False':
  503. value = False
  504. elif value == 'None':
  505. value = None
  506. elif value[0] == '(':
  507. tmp = value.replace('(','').replace(')', '').split(',')
  508. try:
  509. value = tuple(map(int, tmp))
  510. except ValueError:
  511. value = tuple(tmp)
  512. else:
  513. try:
  514. value = int(value)
  515. except ValueError:
  516. try:
  517. value = float(value)
  518. except ValueError:
  519. pass
  520. self.Append(settings, group, key, subkey, value)
  521. idx += 2
  522. finally:
  523. file.close()
  524. def SaveToFile(self, settings=None):
  525. """Save settings to the file"""
  526. if settings is None:
  527. settings = self.userSettings
  528. loc = self.Get(group='advanced', key='settingsFile', subkey='type')
  529. home = os.path.expanduser("~") # MS Windows fix ?
  530. gisdbase = grassenv.GetGRASSVariable("GISDBASE")
  531. location_name = grassenv.GetGRASSVariable("LOCATION_NAME")
  532. mapset_name = grassenv.GetGRASSVariable("MAPSET")
  533. filePath = None
  534. if loc == 'home':
  535. filePath = os.path.join(home, self.fileName)
  536. elif loc == 'gisdbase':
  537. filePath = os.path.join(gisdbase, self.fileName)
  538. elif loc == 'location':
  539. filePath = os.path.join(gisdbase, location_name, self.fileName)
  540. elif loc == 'mapset':
  541. filePath = os.path.join(gisdbase, location_name, mapset_name, self.fileName)
  542. if filePath is None:
  543. raise gcmd.SettingsError(_('Uknown settings file location.'))
  544. try:
  545. file = open(filePath, "w")
  546. for group in settings.keys():
  547. for key in settings[group].keys():
  548. file.write('%s:%s:' % (group, key))
  549. subkeys = settings[group][key].keys()
  550. for idx in range(len(subkeys)):
  551. value = settings[group][key][subkeys[idx]]
  552. if type(value) == type({}):
  553. if idx > 0:
  554. file.write('%s%s:%s:' % (os.linesep, group, key))
  555. file.write('%s:' % subkeys[idx])
  556. kvalues = settings[group][key][subkeys[idx]].keys()
  557. srange = range(len(kvalues))
  558. for sidx in srange:
  559. file.write('%s:%s' % (kvalues[sidx],
  560. settings[group][key][subkeys[idx]][kvalues[sidx]]))
  561. if sidx < len(kvalues) - 1:
  562. file.write(':')
  563. else:
  564. file.write('%s:%s' % (subkeys[idx], value))
  565. if idx < len(subkeys) - 1:
  566. file.write(':')
  567. file.write('%s' % os.linesep)
  568. except IOError, e:
  569. raise gcmd.SettingsError(e)
  570. except:
  571. raise gcmd.SettingsError('Writing settings to file <%s> failed.' % filePath)
  572. file.close()
  573. return filePath
  574. def Get(self, group, key=None, subkey=None, internal=False):
  575. """Get value by key/subkey
  576. Raise KeyError if key is not found
  577. @param group settings group
  578. @param key (value, None)
  579. @param subkey (value, list or None)
  580. @param internal use internal settings instead
  581. @return value
  582. """
  583. if internal is True:
  584. settings = self.internalSettings
  585. else:
  586. settings = self.userSettings
  587. try:
  588. if subkey is None:
  589. if key is None:
  590. return settings[group]
  591. else:
  592. return settings[group][key]
  593. else:
  594. if type(subkey) == type([]):
  595. return settings[group][key][subkey[0]][subkey[1]]
  596. else:
  597. return settings[group][key][subkey]
  598. except KeyError:
  599. raise gcmd.SettingsError("%s %s:%s:%s." % (_("Unable to get value"),
  600. group, key, subkey))
  601. def Set(self, group, value, key=None, subkey=None, internal=False):
  602. """Set value of key/subkey
  603. Raise KeyError if group/key is not found
  604. @param group settings group
  605. @param key key (value, None)
  606. @param subkey subkey (value, list or None)
  607. @param value value
  608. @param internal use internal settings instead
  609. """
  610. if internal is True:
  611. settings = self.internalSettings
  612. else:
  613. settings = self.userSettings
  614. try:
  615. if subkey is None:
  616. if key is None:
  617. settings[group] = value
  618. else:
  619. settings[group][key] = value
  620. else:
  621. if type(subkey) == type([]):
  622. settings[group][key][subkey[0]][subkey[1]] = value
  623. else:
  624. settings[group][key][subkey] = value
  625. except KeyError:
  626. raise gcmd.SettingsError("%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey))
  627. def Append(self, dict, group, key, subkey, value):
  628. """Set value of key/subkey
  629. Create group/key/subkey if not exists
  630. @param dict settings dictionary to use
  631. @param group settings group
  632. @param key key
  633. @param subkey subkey (value or list)
  634. @param value value
  635. """
  636. if not dict.has_key(group):
  637. dict[group] = {}
  638. if not dict[group].has_key(key):
  639. dict[group][key] = {}
  640. if type(subkey) == type([]):
  641. # TODO: len(subkey) > 2
  642. if not dict[group][key].has_key(subkey[0]):
  643. dict[group][key][subkey[0]] = {}
  644. dict[group][key][subkey[0]][subkey[1]] = value
  645. else:
  646. dict[group][key][subkey] = value
  647. def GetDefaultSettings(self):
  648. """Get default user settings"""
  649. return self.defaultSettings
  650. globalSettings = Settings()
  651. class PreferencesDialog(wx.Dialog):
  652. """User preferences dialog"""
  653. def __init__(self, parent, title=_("User GUI settings"),
  654. settings=globalSettings,
  655. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
  656. self.parent = parent # GMFrame
  657. self.title = title
  658. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title,
  659. style=style, size=(-1, -1))
  660. self.settings = settings
  661. # notebook
  662. notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
  663. # dict for window ids
  664. self.winId = {}
  665. # create notebook pages
  666. self.__CreateGeneralPage(notebook)
  667. self.__CreateDisplayPage(notebook)
  668. self.__CreateCmdPage(notebook)
  669. self.__CreateAttributeManagerPage(notebook)
  670. self.__CreateWorkspacePage(notebook)
  671. self.__CreateAdvancedPage(notebook)
  672. # buttons
  673. btnDefault = wx.Button(self, wx.ID_ANY, _("Set to default"))
  674. btnSave = wx.Button(self, wx.ID_SAVE)
  675. btnApply = wx.Button(self, wx.ID_APPLY)
  676. btnCancel = wx.Button(self, wx.ID_CANCEL)
  677. btnSave.SetDefault()
  678. # bindigs
  679. btnDefault.Bind(wx.EVT_BUTTON, self.OnDefault)
  680. btnDefault.SetToolTipString(_("Revert settings to default and apply changes"))
  681. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  682. btnApply.SetToolTipString(_("Apply changes for the current session"))
  683. btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
  684. btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
  685. btnSave.SetDefault()
  686. btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  687. btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
  688. # sizers
  689. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  690. btnSizer.Add(item=btnDefault, proportion=1,
  691. flag=wx.ALL, border=5)
  692. btnStdSizer = wx.StdDialogButtonSizer()
  693. btnStdSizer.AddButton(btnCancel)
  694. btnStdSizer.AddButton(btnSave)
  695. btnStdSizer.AddButton(btnApply)
  696. btnStdSizer.Realize()
  697. mainSizer = wx.BoxSizer(wx.VERTICAL)
  698. mainSizer.Add(item=notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  699. mainSizer.Add(item=btnSizer, proportion=0,
  700. flag=wx.EXPAND, border=0)
  701. mainSizer.Add(item=btnStdSizer, proportion=0,
  702. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  703. self.SetSizer(mainSizer)
  704. mainSizer.Fit(self)
  705. self.SetMinSize(self.GetBestSize())
  706. self.SetSize((500, 375))
  707. def __CreateGeneralPage(self, notebook):
  708. """Create notebook page for general settings"""
  709. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  710. notebook.AddPage(page=panel, text=_("General"))
  711. border = wx.BoxSizer(wx.VERTICAL)
  712. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("General settings"))
  713. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  714. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  715. gridSizer.AddGrowableCol(0)
  716. #
  717. # mapsets path
  718. #
  719. row = 0
  720. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  721. label=_("Mapsets path:")),
  722. flag=wx.ALIGN_LEFT |
  723. wx.ALIGN_CENTER_VERTICAL,
  724. pos=(row, 0))
  725. mapsetPath = wx.Choice(parent=panel, id=wx.ID_ANY, size=(200, -1),
  726. choices=self.settings.Get(group='general', key='mapsetPath',
  727. subkey='choices', internal=True),
  728. name="GetSelection")
  729. mapsetPath.SetSelection(self.settings.Get(group='general', key='mapsetPath', subkey='selection'))
  730. self.winId['general:mapsetPath:selection'] = mapsetPath.GetId()
  731. gridSizer.Add(item=mapsetPath,
  732. flag=wx.ALIGN_RIGHT |
  733. wx.ALIGN_CENTER_VERTICAL,
  734. pos=(row, 1))
  735. #
  736. # expand element list
  737. #
  738. row +=1
  739. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  740. label=_("Element list:")),
  741. flag=wx.ALIGN_LEFT |
  742. wx.ALIGN_CENTER_VERTICAL,
  743. pos=(row, 0))
  744. elementList = wx.Choice(parent=panel, id=wx.ID_ANY,
  745. choices=self.settings.Get(group='general', key='elementListExpand',
  746. subkey='choices', internal=True),
  747. name="GetSelection")
  748. elementList.SetSelection(self.settings.Get(group='general', key='elementListExpand',
  749. subkey='selection'))
  750. self.winId['general:elementListExpand:selection'] = elementList.GetId()
  751. gridSizer.Add(item=elementList,
  752. flag=wx.ALIGN_RIGHT |
  753. wx.ALIGN_CENTER_VERTICAL,
  754. pos=(row, 1))
  755. #
  756. # default window layout
  757. #
  758. row += 1
  759. defaultPos = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  760. label=_("Save current window layout as default"),
  761. name='IsChecked')
  762. defaultPos.SetValue(self.settings.Get(group='general', key='defWindowPos', subkey='enabled'))
  763. defaultPos.SetToolTip(wx.ToolTip (_("Save current position and size of Layer Manager window and opened "
  764. "Map Display window(s) and use as default for next sessions.")))
  765. self.winId['general:defWindowPos:enabled'] = defaultPos.GetId()
  766. gridSizer.Add(item=defaultPos,
  767. pos=(row, 0), span=(1, 2))
  768. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  769. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  770. #
  771. # Layer Manager settings
  772. #
  773. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Layer Manager settings"))
  774. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  775. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  776. gridSizer.AddGrowableCol(0)
  777. #
  778. # show opacily level
  779. #
  780. row = 0
  781. changeOpacityLevel = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  782. label=_("Opacity level editable"),
  783. name='IsChecked')
  784. changeOpacityLevel.SetValue(self.settings.Get(group='manager', key='changeOpacityLevel', subkey='enabled'))
  785. self.winId['manager:changeOpacityLevel:enabled'] = changeOpacityLevel.GetId()
  786. gridSizer.Add(item=changeOpacityLevel,
  787. pos=(row, 0), span=(1, 2))
  788. #
  789. # ask when removing map layer from layer tree
  790. #
  791. row += 1
  792. askOnRemoveLayer = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  793. label=_("Ask when removing map layer from layer tree"),
  794. name='IsChecked')
  795. askOnRemoveLayer.SetValue(self.settings.Get(group='manager', key='askOnRemoveLayer', subkey='enabled'))
  796. self.winId['manager:askOnRemoveLayer:enabled'] = askOnRemoveLayer.GetId()
  797. gridSizer.Add(item=askOnRemoveLayer,
  798. pos=(row, 0), span=(1, 2))
  799. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  800. border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
  801. panel.SetSizer(border)
  802. return panel
  803. def __CreateDisplayPage(self, notebook):
  804. """Create notebook page for display settings"""
  805. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  806. notebook.AddPage(page=panel, text=_("Display"))
  807. border = wx.BoxSizer(wx.VERTICAL)
  808. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Font settings"))
  809. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  810. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  811. gridSizer.AddGrowableCol(0)
  812. #
  813. # font settings
  814. #
  815. row = 0
  816. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  817. label=_("Default font for GRASS displays:")),
  818. flag=wx.ALIGN_LEFT |
  819. wx.ALIGN_CENTER_VERTICAL,
  820. pos=(row, 0))
  821. fontButton = wx.Button(parent=panel, id=wx.ID_ANY,
  822. label=_("Set font"), size=(100, -1))
  823. gridSizer.Add(item=fontButton,
  824. flag=wx.ALIGN_RIGHT |
  825. wx.ALIGN_CENTER_VERTICAL,
  826. pos=(row, 1))
  827. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  828. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  829. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Default display settings"))
  830. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  831. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  832. gridSizer.AddGrowableCol(0)
  833. #
  834. # display driver
  835. #
  836. row = 0
  837. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  838. label=_("Display driver:")),
  839. flag=wx.ALIGN_LEFT |
  840. wx.ALIGN_CENTER_VERTICAL,
  841. pos=(row, 0))
  842. listOfDrivers = self.settings.Get(group='display', key='driver', subkey='choices', internal=True)
  843. # check if cairo is available
  844. if 'cairo' not in listOfDrivers:
  845. for line in gcmd.Command(['d.mon', '-l']).ReadStdOutput():
  846. if 'cairo' in line:
  847. listOfDrivers.append('cairo')
  848. break
  849. driver = wx.Choice(parent=panel, id=wx.ID_ANY, size=(150, -1),
  850. choices=listOfDrivers,
  851. name="GetStringSelection")
  852. driver.SetStringSelection(self.settings.Get(group='display', key='driver', subkey='type'))
  853. self.winId['display:driver:type'] = driver.GetId()
  854. gridSizer.Add(item=driver,
  855. flag=wx.ALIGN_RIGHT,
  856. pos=(row, 1))
  857. #
  858. # Statusbar mode
  859. #
  860. row += 1
  861. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  862. label=_("Statusbar mode:")),
  863. flag=wx.ALIGN_LEFT |
  864. wx.ALIGN_CENTER_VERTICAL,
  865. pos=(row, 0))
  866. listOfModes = self.settings.Get(group='display', key='statusbarMode', subkey='choices', internal=True)
  867. statusbarMode = wx.Choice(parent=panel, id=wx.ID_ANY, size=(150, -1),
  868. choices=listOfModes,
  869. name="GetSelection")
  870. statusbarMode.SetSelection(self.settings.Get(group='display', key='statusbarMode', subkey='selection'))
  871. self.winId['display:statusbarMode:selection'] = statusbarMode.GetId()
  872. gridSizer.Add(item=statusbarMode,
  873. flag=wx.ALIGN_RIGHT,
  874. pos=(row, 1))
  875. #
  876. # Use computation resolution
  877. #
  878. row += 1
  879. compResolution = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  880. label=_("Constrain display resolution to computational settings"),
  881. name="IsChecked")
  882. compResolution.SetValue(self.settings.Get(group='display', key='compResolution', subkey='enabled'))
  883. self.winId['display:compResolution:enabled'] = compResolution.GetId()
  884. gridSizer.Add(item=compResolution,
  885. pos=(row, 0), span=(1, 2))
  886. #
  887. # auto-rendering
  888. #
  889. row += 1
  890. autoRendering = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  891. label=_("Enable auto-rendering"),
  892. name="IsChecked")
  893. autoRendering.SetValue(self.settings.Get(group='display', key='autoRendering', subkey='enabled'))
  894. self.winId['display:autoRendering:enabled'] = autoRendering.GetId()
  895. gridSizer.Add(item=autoRendering,
  896. pos=(row, 0), span=(1, 2))
  897. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  898. border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
  899. panel.SetSizer(border)
  900. # bindings
  901. fontButton.Bind(wx.EVT_BUTTON, self.OnSetFont)
  902. return panel
  903. def __CreateCmdPage(self, notebook):
  904. """Create notebook page for commad dialog settings"""
  905. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  906. notebook.AddPage(page=panel, text=_("Command"))
  907. border = wx.BoxSizer(wx.VERTICAL)
  908. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Command dialog settings"))
  909. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  910. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  911. gridSizer.AddGrowableCol(0)
  912. #
  913. # command dialog settings
  914. #
  915. row = 0
  916. # overwrite
  917. overwrite = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  918. label=_("Allow output files to overwrite existing files"),
  919. name="IsChecked")
  920. overwrite.SetValue(self.settings.Get(group='cmd', key='overwrite', subkey='enabled'))
  921. self.winId['cmd:overwrite:enabled'] = overwrite.GetId()
  922. gridSizer.Add(item=overwrite,
  923. pos=(row, 0), span=(1, 2))
  924. row += 1
  925. # close
  926. close = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  927. label=_("Close dialog on finish"),
  928. name="IsChecked")
  929. close.SetValue(self.settings.Get(group='cmd', key='closeDlg', subkey='enabled'))
  930. self.winId['cmd:closeDlg:enabled'] = close.GetId()
  931. gridSizer.Add(item=close,
  932. pos=(row, 0), span=(1, 2))
  933. row += 1
  934. # verbosity
  935. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  936. label=_("Verbosity level:")),
  937. flag=wx.ALIGN_LEFT |
  938. wx.ALIGN_CENTER_VERTICAL,
  939. pos=(row, 0))
  940. verbosity = wx.Choice(parent=panel, id=wx.ID_ANY, size=(200, -1),
  941. choices=self.settings.Get(group='cmd', key='verbosity', subkey='choices', internal=True),
  942. name="GetStringSelection")
  943. verbosity.SetStringSelection(self.settings.Get(group='cmd', key='verbosity', subkey='selection'))
  944. self.winId['cmd:verbosity:selection'] = verbosity.GetId()
  945. gridSizer.Add(item=verbosity,
  946. pos=(row, 1))
  947. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  948. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  949. #
  950. # raster settings
  951. #
  952. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Raster settings"))
  953. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  954. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  955. gridSizer.AddGrowableCol(0)
  956. #
  957. # raster overlay
  958. #
  959. row = 0
  960. rasterOverlay = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  961. label=_("Overlay raster maps"),
  962. name='IsChecked')
  963. rasterOverlay.SetValue(self.settings.Get(group='cmd', key='rasterOverlay', subkey='enabled'))
  964. self.winId['cmd:rasterOverlay:enabled'] = rasterOverlay.GetId()
  965. gridSizer.Add(item=rasterOverlay,
  966. pos=(row, 0), span=(1, 2))
  967. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  968. border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3)
  969. panel.SetSizer(border)
  970. return panel
  971. def __CreateAttributeManagerPage(self, notebook):
  972. """Create notebook page for 'Attribute Table Manager' settings"""
  973. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  974. notebook.AddPage(page=panel, text=_("Attributes"))
  975. pageSizer = wx.BoxSizer(wx.VERTICAL)
  976. #
  977. # highlighting
  978. #
  979. highlightBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
  980. label=" %s " % _("Highlighting"))
  981. highlightSizer = wx.StaticBoxSizer(highlightBox, wx.VERTICAL)
  982. flexSizer = wx.FlexGridSizer (cols=2, hgap=5, vgap=5)
  983. flexSizer.AddGrowableCol(0)
  984. label = wx.StaticText(parent=panel, id=wx.ID_ANY, label="Color")
  985. hlColor = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
  986. colour=self.settings.Get(group='atm', key='highlight', subkey='color'),
  987. size=(25, 25))
  988. self.winId['atm:highlight:color'] = hlColor.GetId()
  989. flexSizer.Add(label, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  990. flexSizer.Add(hlColor, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  991. label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width (in pixels)"))
  992. hlWidth = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(50, -1),
  993. initial=self.settings.Get(group='atm', key='highlight',subkey='width'),
  994. min=1, max=1e6)
  995. self.winId['atm:highlight:width'] = hlWidth.GetId()
  996. flexSizer.Add(label, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  997. flexSizer.Add(hlWidth, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  998. highlightSizer.Add(item=flexSizer,
  999. proportion=0,
  1000. flag=wx.ALL | wx.EXPAND,
  1001. border=5)
  1002. pageSizer.Add(item=highlightSizer,
  1003. proportion=0,
  1004. flag=wx.ALL | wx.EXPAND,
  1005. border=5)
  1006. #
  1007. # data browser related settings
  1008. #
  1009. dataBrowserBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
  1010. label=" %s " % _("Data browser"))
  1011. dataBrowserSizer = wx.StaticBoxSizer(dataBrowserBox, wx.VERTICAL)
  1012. flexSizer = wx.FlexGridSizer (cols=2, hgap=5, vgap=5)
  1013. flexSizer.AddGrowableCol(0)
  1014. label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Left mouse double click"))
  1015. leftDbClick = wx.Choice(parent=panel, id=wx.ID_ANY,
  1016. choices=self.settings.Get(group='atm', key='leftDbClick', subkey='choices', internal=True),
  1017. name="GetSelection")
  1018. leftDbClick.SetSelection(self.settings.Get(group='atm', key='leftDbClick', subkey='selection'))
  1019. self.winId['atm:leftDbClick:selection'] = leftDbClick.GetId()
  1020. flexSizer.Add(label, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1021. flexSizer.Add(leftDbClick, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  1022. dataBrowserSizer.Add(item=flexSizer,
  1023. proportion=0,
  1024. flag=wx.ALL | wx.EXPAND,
  1025. border=5)
  1026. pageSizer.Add(item=dataBrowserSizer,
  1027. proportion=0,
  1028. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
  1029. border=3)
  1030. panel.SetSizer(pageSizer)
  1031. return panel
  1032. def __CreateWorkspacePage(self, notebook):
  1033. """Create notebook page for workspace settings"""
  1034. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  1035. notebook.AddPage(page=panel, text=_("Workspace"))
  1036. border = wx.BoxSizer(wx.VERTICAL)
  1037. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Loading workspace"))
  1038. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1039. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  1040. gridSizer.AddGrowableCol(0)
  1041. row = 0
  1042. #
  1043. # positioning
  1044. #
  1045. posDisplay = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  1046. label=_("Suppress positioning Map Display Window(s)"),
  1047. name='IsChecked')
  1048. posDisplay.SetValue(self.settings.Get(group='workspace', key='posDisplay', subkey='enabled'))
  1049. self.winId['workspace:posDisplay:enabled'] = posDisplay.GetId()
  1050. gridSizer.Add(item=posDisplay,
  1051. pos=(row, 0), span=(1, 2))
  1052. row +=1
  1053. posManager = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  1054. label=_("Suppress positioning Layer Manager window"),
  1055. name='IsChecked')
  1056. posManager.SetValue(self.settings.Get(group='workspace', key='posManager', subkey='enabled'))
  1057. self.winId['workspace:posManager:enabled'] = posManager.GetId()
  1058. gridSizer.Add(item=posManager,
  1059. pos=(row, 0), span=(1, 2))
  1060. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  1061. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  1062. panel.SetSizer(border)
  1063. return panel
  1064. def __CreateAdvancedPage(self, notebook):
  1065. """Create notebook page for advanced settings"""
  1066. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  1067. notebook.AddPage(page=panel, text=_("Advanced"))
  1068. border = wx.BoxSizer(wx.VERTICAL)
  1069. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Advanced settings"))
  1070. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1071. gridSizer = wx.GridBagSizer (hgap=3, vgap=3)
  1072. gridSizer.AddGrowableCol(0)
  1073. row = 0
  1074. #
  1075. # place where to store settings
  1076. #
  1077. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  1078. label=_("Place where to store settings:")),
  1079. flag=wx.ALIGN_LEFT |
  1080. wx.ALIGN_CENTER_VERTICAL,
  1081. pos=(row, 0))
  1082. settingsFile = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1083. choices=self.settings.Get(group='advanced', key='settingsFile',
  1084. subkey='choices', internal=True),
  1085. name='GetStringSelection')
  1086. settingsFile.SetStringSelection(self.settings.Get(group='advanced', key='settingsFile', subkey='type'))
  1087. self.winId['advanced:settingsFile:type'] = settingsFile.GetId()
  1088. gridSizer.Add(item=settingsFile,
  1089. flag=wx.ALIGN_RIGHT |
  1090. wx.ALIGN_CENTER_VERTICAL,
  1091. pos=(row, 1))
  1092. row += 1
  1093. #
  1094. # icon theme
  1095. #
  1096. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  1097. label=_("Icon theme:")),
  1098. flag=wx.ALIGN_LEFT |
  1099. wx.ALIGN_CENTER_VERTICAL,
  1100. pos=(row, 0))
  1101. iconTheme = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1102. choices=self.settings.Get(group='advanced', key='iconTheme',
  1103. subkey='choices', internal=True),
  1104. name="GetStringSelection")
  1105. iconTheme.SetStringSelection(self.settings.Get(group='advanced', key='iconTheme', subkey='type'))
  1106. self.winId['advanced:iconTheme:type'] = iconTheme.GetId()
  1107. gridSizer.Add(item=iconTheme,
  1108. flag=wx.ALIGN_RIGHT |
  1109. wx.ALIGN_CENTER_VERTICAL,
  1110. pos=(row, 1))
  1111. row += 1
  1112. iconNote = wordwrap(_("Note: Requires GUI restart."),
  1113. self.GetSize()[0]-50, wx.ClientDC(self))
  1114. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  1115. label=iconNote),
  1116. flag=wx.ALIGN_LEFT |
  1117. wx.ALIGN_CENTER_VERTICAL,
  1118. pos=(row, 0), span=(1, 2))
  1119. row += 1
  1120. #
  1121. # digitization interface
  1122. #
  1123. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  1124. label=_("Vector digitizer interface:")),
  1125. flag=wx.ALIGN_LEFT |
  1126. wx.ALIGN_CENTER_VERTICAL,
  1127. pos=(row, 0))
  1128. digitInterface = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1129. choices=self.settings.Get(group='advanced', key='digitInterface',
  1130. subkey='choices', internal=True),
  1131. name="GetStringSelection")
  1132. digitInterface.SetStringSelection(self.settings.Get(group='advanced', key='digitInterface',
  1133. subkey='type'))
  1134. self.winId['advanced:digitInterface:type'] = digitInterface.GetId()
  1135. gridSizer.Add(item=digitInterface,
  1136. flag=wx.ALIGN_RIGHT |
  1137. wx.ALIGN_CENTER_VERTICAL,
  1138. pos=(row, 1))
  1139. row += 1
  1140. digitNote = wordwrap(_("Note: User can choose from two interfaces for digitization. "
  1141. "The simple one uses v.edit command on the background. "
  1142. "Map topology is rebuild on each operation which can "
  1143. "significantly slow-down response. The vdigit is a native "
  1144. "interface which uses v.edit functionality, but doesn't "
  1145. "call the module itself."),
  1146. self.GetSize()[0]-50, wx.ClientDC(self))
  1147. gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY,
  1148. label=digitNote),
  1149. flag=wx.ALIGN_LEFT |
  1150. wx.ALIGN_CENTER_VERTICAL,
  1151. pos=(row, 0), span=(1, 2))
  1152. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  1153. border.Add(item=sizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3)
  1154. panel.SetSizer(border)
  1155. return panel
  1156. def OnSetFont(self, event):
  1157. """'Set font' button pressed"""
  1158. dlg = SetDefaultFont(parent=self, id=wx.ID_ANY,
  1159. title=_('Select default display font'),
  1160. pos=wx.DefaultPosition, size=wx.DefaultSize,
  1161. style=wx.DEFAULT_DIALOG_STYLE,
  1162. encoding=self.parent.encoding)
  1163. if dlg.ShowModal() == wx.ID_CANCEL:
  1164. dlg.Destroy()
  1165. return
  1166. # set default font type, font, and encoding to whatever selected in dialog
  1167. if dlg.font != None:
  1168. self.font = dlg.font
  1169. if dlg.encoding != None:
  1170. self.encoding = dlg.encoding
  1171. dlg.Destroy()
  1172. # set default font and encoding environmental variables
  1173. os.environ["GRASS_FONT"] = self.font
  1174. if self.encoding != None and self.encoding != "ISO-8859-1":
  1175. os.environ["GRASS_ENCODING"] = self.encoding
  1176. event.Skip()
  1177. def OnSave(self, event):
  1178. """Button 'Save' pressed"""
  1179. self.__UpdateSettings()
  1180. file = self.settings.SaveToFile()
  1181. self.parent.goutput.WriteLog(_('Settings saved to file \'%s\'.') % file)
  1182. self.Close()
  1183. def OnApply(self, event):
  1184. """Button 'Apply' pressed"""
  1185. self.__UpdateSettings()
  1186. self.Close()
  1187. def OnCancel(self, event):
  1188. """Button 'Cancel' pressed"""
  1189. self.Close()
  1190. def OnDefault(self, event):
  1191. """Button 'Set to default' pressed"""
  1192. self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
  1193. # update widgets
  1194. for gks in self.winId.keys():
  1195. group, key, subkey = gks.split(':')
  1196. value = self.settings.Get(group, key, subkey)
  1197. win = self.FindWindowById(self.winId[gks])
  1198. if win.GetName() in ('GetValue', 'IsChecked'):
  1199. value = win.SetValue(value)
  1200. elif win.GetName() == 'GetSelection':
  1201. value = win.SetSelection(value)
  1202. elif win.GetName() == 'GetStringSelection':
  1203. value = win.SetStringSelection(value)
  1204. else:
  1205. value = win.SetValue(value)
  1206. def __UpdateSettings(self):
  1207. """Update user settings"""
  1208. for item in self.winId.keys():
  1209. group, key, subkey = item.split(':')
  1210. id = self.winId[item]
  1211. win = self.FindWindowById(id)
  1212. if win.GetName() == 'GetValue':
  1213. value = win.GetValue()
  1214. elif win.GetName() == 'GetSelection':
  1215. value = win.GetSelection()
  1216. elif win.GetName() == 'IsChecked':
  1217. value = win.IsChecked()
  1218. elif win.GetName() == 'GetStringSelection':
  1219. value = win.GetStringSelection()
  1220. else:
  1221. value = win.GetValue()
  1222. self.settings.Set(group, key, subkey, value)
  1223. #
  1224. # update default window dimension
  1225. #
  1226. if self.settings.Get(group='general', key='defWindowPos', subkey='enabled') is True:
  1227. dim = ''
  1228. # layer manager
  1229. pos = self.parent.GetPosition()
  1230. size = self.parent.GetSize()
  1231. dim = '%d,%d,%d,%d' % (pos[0], pos[1], size[0], size[1])
  1232. # opened displays
  1233. for page in range(0, self.parent.gm_cb.GetPageCount()):
  1234. pos = self.parent.gm_cb.GetPage(page).maptree.mapdisplay.GetPosition()
  1235. size = self.parent.gm_cb.GetPage(page).maptree.mapdisplay.GetSize()
  1236. dim += ',%d,%d,%d,%d' % (pos[0], pos[1], size[0], size[1])
  1237. self.settings.Set(group='general', key='defWindowPos', subkey='dim', value=dim)
  1238. else:
  1239. self.settings.Set(group='general', key='defWindowPos', subkey='dim', value='')
  1240. class SetDefaultFont(wx.Dialog):
  1241. """
  1242. Opens a file selection dialog to select default font
  1243. to use in all GRASS displays
  1244. """
  1245. def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
  1246. style=wx.DEFAULT_DIALOG_STYLE, encoding='ISO-8859-1'):
  1247. wx.Dialog.__init__(self, parent, id, title, pos, size, style)
  1248. if "GRASS_FONT" in os.environ:
  1249. self.font = os.environ["GRASS_FONT"]
  1250. else:
  1251. self.font = None
  1252. self.fontlist = self.GetFonts()
  1253. self.encoding = encoding
  1254. sizer = wx.BoxSizer(wx.VERTICAL)
  1255. box = wx.BoxSizer(wx.HORIZONTAL)
  1256. label = wx.StaticText(self, -1, "Select Font:", (15, 50))
  1257. box.Add(label, 0, wx.EXPAND|wx.GROW|wx.ALIGN_TOP|wx.RIGHT, 5)
  1258. self.fontlb = wx.ListBox(self, wx.ID_ANY, pos=wx.DefaultPosition,
  1259. size=(280,150), choices=self.fontlist,
  1260. style=wx.LB_SINGLE|wx.LB_SORT)
  1261. self.Bind(wx.EVT_LISTBOX, self.EvtListBox, self.fontlb)
  1262. self.Bind(wx.EVT_LISTBOX_DCLICK, self.EvtListBoxDClick, self.fontlb)
  1263. if self.font:
  1264. self.fontlb.SetStringSelection(self.font, True)
  1265. box.Add(self.fontlb, 0, wx.EXPAND|wx.GROW|wx.ALIGN_RIGHT)
  1266. sizer.Add(box, 0, wx.EXPAND|wx.GROW|wx.ALIGN_RIGHT|wx.ALL, 8)
  1267. box = wx.BoxSizer(wx.HORIZONTAL)
  1268. label = wx.StaticText(self, -1, "Character encoding:")
  1269. box.Add(label, 0, wx.ALIGN_RIGHT|wx.RIGHT, 5)
  1270. self.textentry = wx.TextCtrl(self, -1, "", size=(200,-1))
  1271. self.textentry.SetValue(self.encoding)
  1272. box.Add(self.textentry, 0, wx.ALIGN_LEFT)
  1273. self.textentry.Bind(wx.EVT_TEXT, self.OnEncoding)
  1274. sizer.Add(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 8)
  1275. line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL)
  1276. sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 10)
  1277. btnsizer = wx.StdDialogButtonSizer()
  1278. btn = wx.Button(self, wx.ID_OK)
  1279. btn.SetDefault()
  1280. btnsizer.AddButton(btn)
  1281. btn = wx.Button(self, wx.ID_CANCEL)
  1282. btnsizer.AddButton(btn)
  1283. btnsizer.Realize()
  1284. sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
  1285. self.SetSizer(sizer)
  1286. sizer.Fit(self)
  1287. def EvtRadioBox(self, event):
  1288. if event.GetInt() == 0:
  1289. self.fonttype = 'grassfont'
  1290. elif event.GetInt() == 1:
  1291. self.fonttype = 'truetype'
  1292. self.fontlist = self.GetFonts(self.fonttype)
  1293. self.fontlb.SetItems(self.fontlist)
  1294. def OnEncoding(self, event):
  1295. self.encoding = event.GetString()
  1296. def EvtListBox(self, event):
  1297. self.font = event.GetString()
  1298. event.Skip()
  1299. def EvtListBoxDClick(self, event):
  1300. self.font = event.GetString()
  1301. event.Skip()
  1302. def GetFonts(self):
  1303. """
  1304. parses fonts directory or fretypecap file to get a list of fonts for the listbox
  1305. """
  1306. fontlist = []
  1307. cmd = ["d.font", "-l"]
  1308. p = gcmd.Command(cmd, stderr=None)
  1309. dfonts = p.ReadStdOutput()
  1310. dfonts.sort(lambda x,y: cmp(x.lower(), y.lower()))
  1311. for item in range(len(dfonts)):
  1312. # ignore duplicate fonts and those starting with #
  1313. if not dfonts[item].startswith('#') and \
  1314. dfonts[item] != dfonts[item-1]:
  1315. fontlist.append(dfonts[item])
  1316. return fontlist
  1317. class MapsetAccess(wx.Dialog):
  1318. """
  1319. Controls setting options and displaying/hiding map overlay decorations
  1320. """
  1321. def __init__(self, parent, id, title=_('Set/unset access to mapsets in current location'),
  1322. pos=wx.DefaultPosition, size=(350, 400),
  1323. style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
  1324. wx.Dialog.__init__(self, parent, id, title, pos, size, style)
  1325. self.all_mapsets, self.accessible_mapsets = utils.ListOfMapsets()
  1326. self.curr_mapset = grassenv.GetGRASSVariable('MAPSET')
  1327. # make a checklistbox from available mapsets and check those that are active
  1328. sizer = wx.BoxSizer(wx.VERTICAL)
  1329. label = wx.StaticText(parent=self, id=wx.ID_ANY,
  1330. label=_("Check mapset to make it accessible, uncheck it to hide it.%s"
  1331. "Note: PERMANENT and current mapset are always accessible.") % os.linesep)
  1332. sizer.Add(item=label, proportion=0,
  1333. flag=wx.ALL, border=5)
  1334. self.mapsetlb = CheckListMapset(parent=self)
  1335. self.mapsetlb.LoadData(self.all_mapsets)
  1336. sizer.Add(item=self.mapsetlb, proportion=1,
  1337. flag=wx.ALL | wx.EXPAND, border=5)
  1338. # check all accessible mapsets
  1339. if globalSettings.Get(group='general', key='mapsetPath', subkey='selection') == 1:
  1340. for mset in self.all_mapsets:
  1341. self.mapsetlb.CheckItem(self.all_mapsets.index(mset), True)
  1342. else:
  1343. for mset in self.accessible_mapsets:
  1344. self.mapsetlb.CheckItem(self.all_mapsets.index(mset), True)
  1345. pass
  1346. # dialog buttons
  1347. line = wx.StaticLine(parent=self, id=wx.ID_ANY,
  1348. style=wx.LI_HORIZONTAL)
  1349. sizer.Add(item=line, proportion=0,
  1350. flag=wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border=5)
  1351. btnsizer = wx.StdDialogButtonSizer()
  1352. okbtn = wx.Button(self, wx.ID_OK)
  1353. okbtn.SetDefault()
  1354. btnsizer.AddButton(okbtn)
  1355. cancelbtn = wx.Button(self, wx.ID_CANCEL)
  1356. btnsizer.AddButton(cancelbtn)
  1357. btnsizer.Realize()
  1358. sizer.Add(item=btnsizer, proportion=0,
  1359. flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border=5)
  1360. # do layout
  1361. self.Layout()
  1362. self.SetSizer(sizer)
  1363. sizer.Fit(self)
  1364. self.SetMinSize(size)
  1365. def GetMapsets(self):
  1366. """Get list of checked mapsets"""
  1367. ms = []
  1368. i = 0
  1369. for mset in self.all_mapsets:
  1370. if self.mapsetlb.IsChecked(i):
  1371. ms.append(mset)
  1372. i += 1
  1373. return ms
  1374. class CheckListMapset(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
  1375. """List of mapset/owner/group"""
  1376. def __init__(self, parent, pos=wx.DefaultPosition,
  1377. log=None):
  1378. self.parent = parent
  1379. wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
  1380. style=wx.LC_REPORT)
  1381. listmix.CheckListCtrlMixin.__init__(self)
  1382. self.log = log
  1383. # setup mixins
  1384. listmix.ListCtrlAutoWidthMixin.__init__(self)
  1385. def LoadData(self, mapsets):
  1386. """Load data into list"""
  1387. self.InsertColumn(0, _('Mapset'))
  1388. self.InsertColumn(1, _('Owner'))
  1389. self.InsertColumn(2, _('Group'))
  1390. locationPath = os.path.join(grassenv.GetGRASSVariable('GISDBASE'),
  1391. grassenv.GetGRASSVariable('LOCATION_NAME'))
  1392. for mapset in mapsets:
  1393. index = self.InsertStringItem(sys.maxint, mapset)
  1394. mapsetPath = os.path.join(locationPath,
  1395. mapset)
  1396. stat_info = os.stat(mapsetPath)
  1397. if os.name in ('posix', 'mac'):
  1398. self.SetStringItem(index, 1, "%s" % pwd.getpwuid(stat_info.st_uid)[0])
  1399. # FIXME: get group name
  1400. self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
  1401. else:
  1402. # FIXME: no pwd under MS Windows (owner: 0, group: 0)
  1403. self.SetStringItem(index, 1, "%-8s" % stat_info.st_uid)
  1404. self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
  1405. self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE)
  1406. self.SetColumnWidth(col=1, width=wx.LIST_AUTOSIZE)
  1407. def OnCheckItem(self, index, flag):
  1408. """Mapset checked/unchecked"""
  1409. mapset = self.parent.all_mapsets[index]
  1410. if mapset == 'PERMANENT' or mapset == self.parent.curr_mapset:
  1411. self.CheckItem(index, True)