preferences.py 67 KB

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