preferences.py 64 KB

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