dialogs.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. """
  2. @package iclass.dialogs
  3. @brief wxIClass dialogs
  4. Classes:
  5. - dialogs::IClassGroupDialog
  6. - dialogs::IClassMapDialog
  7. - dialogs::IClassCategoryManagerDialog
  8. - dialogs::CategoryListCtrl
  9. - dialogs::IClassSignatureFileDialog
  10. - dialogs::IClassExportAreasDialog
  11. (C) 2006-2011 by the GRASS Development Team
  12. This program is free software under the GNU General Public
  13. License (>=v2). Read the file COPYING that comes with GRASS
  14. for details.
  15. @author Vaclav Petras <wenzeslaus gmail.com>
  16. @author Anna Kratochvilova <kratochanna gmail.com>
  17. """
  18. import os
  19. import wx
  20. import wx.lib.mixins.listctrl as listmix
  21. import wx.lib.scrolledpanel as scrolled
  22. from core import globalvar
  23. from core.utils import _
  24. from core.settings import UserSettings
  25. from core.gcmd import GError, RunCommand, GMessage
  26. from gui_core.dialogs import SimpleDialog, GroupDialog
  27. from gui_core import gselect
  28. from gui_core.widgets import SimpleValidator
  29. from iclass.statistics import Statistics, BandStatistics
  30. from gui_core.wrap import CheckBox, Button, StaticText, \
  31. StaticBox, TextCtrl, Menu
  32. import grass.script as grass
  33. class IClassGroupDialog(SimpleDialog):
  34. """Dialog for imagery group selection"""
  35. def __init__(self, parent, group=None, subgroup=None,
  36. title=_("Select imagery group"), id=wx.ID_ANY):
  37. """
  38. Does post init and layout.
  39. :param parent: gui parent
  40. :param title: dialog window title
  41. :param id: wx id
  42. """
  43. SimpleDialog.__init__(self, parent, title)
  44. self.use_subg = True
  45. self.groupSelect = gselect.Select(
  46. parent=self.panel,
  47. type='group',
  48. mapsets=[
  49. grass.gisenv()['MAPSET']],
  50. size=globalvar.DIALOG_GSELECT_SIZE,
  51. validator=SimpleValidator(
  52. callback=self.ValidatorCallback))
  53. # TODO use when subgroup will be optional
  54. # self.subg_chbox = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
  55. # label = _("Use subgroup"))
  56. self.subGroupSelect = gselect.SubGroupSelect(parent=self.panel)
  57. self.groupSelect.SetFocus()
  58. if group:
  59. self.groupSelect.SetValue(group)
  60. self.GroupSelected()
  61. if subgroup:
  62. self.subGroupSelect.SetValue(subgroup)
  63. self.editGroup = Button(parent=self.panel, id=wx.ID_ANY,
  64. label=_("Create/edit group..."))
  65. self.editGroup.Bind(wx.EVT_BUTTON, self.OnEditGroup)
  66. self.groupSelect.GetTextCtrl().Bind(
  67. wx.EVT_TEXT, lambda event: wx.CallAfter(
  68. self.GroupSelected))
  69. self.warning = _("Name of imagery group is missing.")
  70. self._layout()
  71. self.SetMinSize(self.GetSize())
  72. def _layout(self):
  73. """Do layout"""
  74. self.dataSizer.Add(StaticText(self.panel, id=wx.ID_ANY,
  75. label=_("Name of imagery group:")),
  76. proportion=0,
  77. flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
  78. border=5)
  79. self.dataSizer.Add(self.groupSelect, proportion=0,
  80. flag=wx.EXPAND | wx.ALL, border=5)
  81. # TODO use when subgroup will be optional
  82. # self.dataSizer.Add(self.subg_chbox, proportion = 0,
  83. # flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
  84. self.dataSizer.Add(
  85. StaticText(
  86. self.panel,
  87. id=wx.ID_ANY,
  88. label=_("Name of imagery subgroup:")),
  89. proportion=0,
  90. flag=wx.EXPAND | wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT,
  91. border=5)
  92. self.dataSizer.Add(self.subGroupSelect, proportion=0,
  93. flag=wx.EXPAND | wx.ALL, border=5)
  94. self.dataSizer.Add(self.editGroup, proportion=0,
  95. flag=wx.ALL, border=5)
  96. self.panel.SetSizer(self.sizer)
  97. self.sizer.Fit(self)
  98. # TODO use when subgroup will be optional
  99. # self.subg_panel.Show(False)
  100. #self.subg_chbox.Bind(wx.EVT_CHECKBOX, self.OnSubgChbox)
  101. def OnSubgChbox(self, event):
  102. self.use_subg = self.subg_chbox.GetValue()
  103. if self.use_subg:
  104. self.subg_panel.Show()
  105. # self.SubGroupSelected()
  106. else:
  107. self.subg_panel.Hide()
  108. # self.GroupSelected()
  109. self.SetMinSize(self.GetBestSize())
  110. self.Layout()
  111. def GetData(self):
  112. """Returns selected group and subgroup"""
  113. if self.use_subg:
  114. ret = (self.groupSelect.GetValue(), self.subGroupSelect.GetValue())
  115. else:
  116. ret = (self.groupSelect.GetValue(), None)
  117. return ret
  118. def OnEditGroup(self, event):
  119. """Launch edit group dialog"""
  120. g, s = self.GetData()
  121. dlg = GroupDialog(parent=self, defaultGroup=g, defaultSubgroup=s)
  122. dlg.ShowModal()
  123. gr, s = dlg.GetSelectedGroup()
  124. if gr in dlg.GetExistGroups():
  125. self.groupSelect.SetValue(gr)
  126. self.GroupSelected()
  127. wx.CallAfter(self.subGroupSelect.SetValue, s)
  128. dlg.Destroy()
  129. def GroupSelected(self):
  130. group = self.GetSelectedGroup()
  131. self.subGroupSelect.Insert(group)
  132. def GetSelectedGroup(self):
  133. """Return currently selected group (without mapset)"""
  134. return self.groupSelect.GetValue().split('@')[0]
  135. def GetGroupBandsErr(self, parent):
  136. """Get list of raster bands which are in the soubgroup of group with both having same name.
  137. If the group does not exists or it does not contain any bands in subgoup with same name,
  138. error dialog is shown.
  139. """
  140. gr, s = self.GetData()
  141. group = grass.find_file(name=gr, element='group')
  142. bands = []
  143. g = group['name']
  144. if g:
  145. if self.use_subg:
  146. if s == '':
  147. GError(_("Please choose a subgroup."), parent=parent)
  148. return bands
  149. if s not in self.GetSubgroups(g):
  150. GError(
  151. _("Subgroup <%s> not found in group <%s>") %
  152. (s, g), parent=parent)
  153. return bands
  154. bands = self.GetGroupBands(g, s)
  155. if not bands:
  156. if self.use_subg:
  157. GError(_("No data found in subgroup <%s> of group <%s>.\n"
  158. ".")
  159. % (s, g), parent=parent)
  160. else:
  161. GError(_("No data found in group <%s>.\n"
  162. ".")
  163. % g, parent=parent)
  164. else:
  165. GError(_("Group <%s> not found") % gr, parent=parent)
  166. return bands
  167. def GetGroupBands(self, group, subgroup):
  168. """Get list of raster bands which are in the soubgroup of group with both having same name."""
  169. kwargs = {}
  170. if subgroup:
  171. kwargs['subgroup'] = subgroup
  172. res = RunCommand('i.group',
  173. flags='g',
  174. group=group,
  175. read=True, **kwargs).strip()
  176. bands = None
  177. if res.split('\n')[0]:
  178. bands = res.split('\n')
  179. return bands
  180. def GetSubgroups(self, group):
  181. return RunCommand('i.group', group=group,
  182. read=True, flags='sg').splitlines()
  183. class IClassMapDialog(SimpleDialog):
  184. """Dialog for adding raster/vector map"""
  185. def __init__(self, parent, title, element):
  186. """
  187. :param parent: gui parent
  188. :param title: dialog title
  189. :param element: element type ('raster', 'vector')
  190. """
  191. SimpleDialog.__init__(self, parent, title=title)
  192. self.elementType = element
  193. self.element = gselect.Select(
  194. parent=self.panel,
  195. type=element,
  196. size=globalvar.DIALOG_GSELECT_SIZE,
  197. validator=SimpleValidator(
  198. callback=self.ValidatorCallback))
  199. self.element.SetFocus()
  200. self.warning = _("Name of map is missing.")
  201. self._layout()
  202. self.SetMinSize(self.GetSize())
  203. def _layout(self):
  204. """Do layout"""
  205. if self.elementType == 'raster':
  206. label = _("Name of raster map:")
  207. elif self.elementType == 'vector':
  208. label = _("Name of vector map:")
  209. self.dataSizer.Add(StaticText(self.panel, id=wx.ID_ANY,
  210. label=label),
  211. proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  212. self.dataSizer.Add(self.element, proportion=0,
  213. flag=wx.EXPAND | wx.ALL, border=5)
  214. self.panel.SetSizer(self.sizer)
  215. self.sizer.Fit(self)
  216. def GetMap(self):
  217. """Returns selected raster/vector map"""
  218. return self.element.GetValue()
  219. class IClassCategoryManagerDialog(wx.Dialog):
  220. """Dialog for managing categories (classes).
  221. Alows adding, deleting class and changing its name and color.
  222. """
  223. def __init__(self, parent, title=_("Class manager"), id=wx.ID_ANY):
  224. """
  225. Does post init and layout.
  226. :param parent: gui parent
  227. :param title: dialog window title
  228. :param id: wx id
  229. """
  230. wx.Dialog.__init__(self, parent=parent, title=title, id=id)
  231. self.parent = parent
  232. panel = wx.Panel(parent=self, id=wx.ID_ANY)
  233. mainSizer = wx.BoxSizer(wx.VERTICAL)
  234. box = StaticBox(panel, id=wx.ID_ANY,
  235. label=" %s " % _("Classes"))
  236. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  237. gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  238. self.catList = CategoryListCtrl(panel, mapwindow=parent,
  239. stats_data=parent.stats_data)
  240. addButton = Button(panel, id=wx.ID_ADD)
  241. deleteButton = Button(panel, id=wx.ID_DELETE)
  242. gridSizer.Add(
  243. self.catList, pos=(
  244. 0, 0), span=(
  245. 3, 1), flag=wx.EXPAND)
  246. gridSizer.Add(addButton, pos=(0, 1), flag=wx.EXPAND)
  247. gridSizer.Add(deleteButton, pos=(1, 1), flag=wx.EXPAND)
  248. gridSizer.AddGrowableCol(0)
  249. gridSizer.AddGrowableRow(2)
  250. sizer.Add(
  251. gridSizer,
  252. proportion=1,
  253. flag=wx.EXPAND | wx.ALL,
  254. border=5)
  255. mainSizer.Add(
  256. sizer,
  257. proportion=1,
  258. flag=wx.EXPAND | wx.ALL,
  259. border=5)
  260. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  261. closeButton = Button(panel, id=wx.ID_CLOSE)
  262. btnSizer.Add(wx.Size(-1, -1), proportion=1, flag=wx.EXPAND)
  263. btnSizer.Add(closeButton, proportion=0, flag=wx.ALIGN_RIGHT)
  264. mainSizer.Add(
  265. btnSizer,
  266. proportion=0,
  267. flag=wx.EXPAND | wx.ALL,
  268. border=5)
  269. addButton.Bind(wx.EVT_BUTTON, self.OnAddCategory)
  270. deleteButton.Bind(wx.EVT_BUTTON, self.OnDeleteCategory)
  271. closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
  272. self.Bind(wx.EVT_CLOSE, self.OnClose)
  273. panel.SetSizer(mainSizer)
  274. mainSizer.Fit(self)
  275. self.SetSize((400, 250))
  276. self.Layout()
  277. def OnAddCategory(self, event):
  278. if self.parent.stats_data.GetCategories():
  279. cat = max(self.parent.stats_data.GetCategories()) + 1
  280. else:
  281. cat = 1
  282. # intentionally not translatable
  283. defaultName = 'class' + '_' + str(cat)
  284. defaultColor = '0:0:0'
  285. self.catList.AddCategory(cat=cat, name=defaultName, color=defaultColor)
  286. def OnDeleteCategory(self, event):
  287. self.catList.DeleteCategory()
  288. def OnClose(self, event):
  289. self.catList.DeselectAll()
  290. self.Hide()
  291. # if not isinstance(event, wx.CloseEvent):
  292. # self.Destroy()
  293. # event.Skip()
  294. def GetListCtrl(self):
  295. """Returns list widget"""
  296. return self.catList
  297. class CategoryListCtrl(wx.ListCtrl,
  298. listmix.ListCtrlAutoWidthMixin,
  299. listmix.TextEditMixin):
  300. """Widget for controling list of classes (categories).
  301. CategoryListCtrl updates choice in mapwindow and removes raster map
  302. when deleting class (category).
  303. It uses virtual data in the terms of @c wx.ListCtrl.
  304. .. todo::
  305. delete vector features after deleting class
  306. """
  307. def __init__(self, parent, mapwindow, stats_data, id=wx.ID_ANY):
  308. """
  309. :param parent: gui parent
  310. :param mapwindow: mapwindow instance with iclass toolbar and remove raster method
  311. :param stats_data: StatisticsData instance (defined in statistics.py)
  312. :param id: wx id
  313. """
  314. wx.ListCtrl.__init__(
  315. self, parent, id, style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES |
  316. wx.LC_VRULES)
  317. self.columns = ((_('Class name'), 'name'),
  318. (_('Color'), 'color'))
  319. self.Populate(columns=self.columns)
  320. self.mapWindow = mapwindow
  321. self.stats_data = stats_data
  322. self.SetItemCount(len(self.stats_data.GetCategories()))
  323. self.rightClickedItemIdx = wx.NOT_FOUND
  324. listmix.ListCtrlAutoWidthMixin.__init__(self)
  325. listmix.TextEditMixin.__init__(self)
  326. self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
  327. self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnCategorySelected)
  328. self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnClassRightUp) # wxMSW
  329. self.Bind(wx.EVT_RIGHT_UP, self.OnClassRightUp) # wxGTK
  330. self.stats_data.statisticsAdded.connect(self.Update)
  331. self.stats_data.statisticsDeleted.connect(self.Update)
  332. self.stats_data.allStatisticsDeleted.connect(self.Update)
  333. self.stats_data.statisticsSet.connect(self.Update)
  334. def Update(self):
  335. self.SetItemCount(len(self.stats_data.GetCategories()))
  336. def SetVirtualData(self, row, column, text):
  337. attr = self.columns[column][1]
  338. if attr == 'name':
  339. try:
  340. text.encode('ascii')
  341. except UnicodeEncodeError:
  342. GMessage(parent=self, message=_(
  343. "Please use only ASCII characters."))
  344. return
  345. cat = self.stats_data.GetCategories()[row]
  346. self.stats_data.GetStatistics(cat).SetStatistics(stats={attr: text})
  347. toolbar = self.mapWindow.toolbars['iClass']
  348. toolbar.choice.SetSelection(row)
  349. self.Select(row)
  350. if attr == 'name':
  351. self.mapWindow.UpdateRasterName(
  352. text, toolbar.GetSelectedCategoryIdx())
  353. self.mapWindow.UpdateChangeState(changes=True)
  354. def Populate(self, columns):
  355. for i, col in enumerate(columns):
  356. self.InsertColumn(i, col[0]) # wx.LIST_FORMAT_RIGHT
  357. self.SetColumnWidth(0, 100)
  358. self.SetColumnWidth(1, 100)
  359. def AddCategory(self, cat, name, color):
  360. """Add category record (used when importing areas)"""
  361. self.stats_data.AddStatistics(cat, name, color)
  362. self.SetItemCount(len(self.stats_data.GetCategories()))
  363. self.mapWindow.UpdateChangeState(changes=True)
  364. def DeleteCategory(self):
  365. indexList = sorted(self.GetSelectedIndices(), reverse=True)
  366. del_cats = []
  367. cats = self.stats_data.GetCategories()
  368. for i in indexList:
  369. # remove temporary raster
  370. cat = cats[i]
  371. stat = self.stats_data.GetStatistics(cat)
  372. name = stat.rasterName
  373. self.mapWindow.RemoveTempRaster(name)
  374. del_cats.append(cat)
  375. self.stats_data.DeleteStatistics(cat)
  376. self.SetItemCount(len(self.stats_data.GetCategories()))
  377. self.mapWindow.UpdateChangeState(changes=True)
  378. self.mapWindow.DeleteAreas(cats=del_cats)
  379. def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED):
  380. indices = []
  381. lastFound = -1
  382. while True:
  383. index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
  384. if index == -1:
  385. break
  386. else:
  387. lastFound = index
  388. indices.append(index)
  389. return indices
  390. def OnEdit(self, event):
  391. currentItem = event.m_itemIndex
  392. currentCol = event.m_col
  393. if currentCol == 1:
  394. col = self.OnGetItemText(currentItem, currentCol)
  395. col = map(int, col.split(':'))
  396. col_data = wx.ColourData()
  397. col_data.SetColour(wx.Colour(*col))
  398. dlg = wx.ColourDialog(self, col_data)
  399. dlg.GetColourData().SetChooseFull(True)
  400. if dlg.ShowModal() == wx.ID_OK:
  401. color = dlg.GetColourData().GetColour().Get()
  402. color = ':'.join(map(str, color))
  403. self.SetVirtualData(currentItem, currentCol, color)
  404. dlg.Destroy()
  405. wx.CallAfter(self.SetFocus)
  406. event.Skip()
  407. def OnCategorySelected(self, event):
  408. """Highlight selected areas"""
  409. indexList = self.GetSelectedIndices()
  410. sel_cats = []
  411. cats = self.stats_data.GetCategories()
  412. for i in indexList:
  413. sel_cats.append(cats[i])
  414. self.mapWindow.HighlightCategory(sel_cats)
  415. if event:
  416. event.Skip()
  417. def OnClassRightUp(self, event):
  418. """Show context menu on right click"""
  419. item, flags = self.HitTest((event.GetX(), event.GetY()))
  420. if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
  421. self.rightClickedItemIdx = item
  422. if not hasattr(self, "popupZoomtoAreas"):
  423. self.popupZoomtoAreas = wx.NewId()
  424. self.Bind(
  425. wx.EVT_MENU,
  426. self.OnZoomToAreasByCat,
  427. id=self.popupZoomtoAreas)
  428. # generate popup-menu
  429. menu = Menu()
  430. menu.Append(
  431. self.popupZoomtoAreas,
  432. _("Zoom to training areas of selected class"))
  433. self.PopupMenu(menu)
  434. menu.Destroy()
  435. def OnZoomToAreasByCat(self, event):
  436. """Zoom to areas of given category"""
  437. cat = self.stats_data.GetCategories()[self.rightClickedItemIdx]
  438. self.mapWindow.ZoomToAreasByCat(cat)
  439. def DeselectAll(self):
  440. """Deselect all items"""
  441. indexList = self.GetSelectedIndices()
  442. for i in indexList:
  443. self.Select(i, on=0)
  444. # no highlight
  445. self.OnCategorySelected(None)
  446. def OnGetItemText(self, item, col):
  447. cat = self.stats_data.GetCategories()[item]
  448. stat = self.stats_data.GetStatistics(cat)
  449. return getattr(stat, self.columns[col][1])
  450. def OnGetItemImage(self, item):
  451. return -1
  452. def OnGetItemAttr(self, item):
  453. return None
  454. def OnGetItemAttr(self, item):
  455. """Set correct class color for a item"""
  456. back_c = wx.Colour(*map(int, self.OnGetItemText(item, 1).split(':')))
  457. text_c = wx.Colour(*ContrastColor(back_c))
  458. # if it is in scope of the method, gui falls, using self solved it
  459. self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
  460. return self.l
  461. def ContrastColor(color):
  462. """Decides which value shoud have text to be contrast with backgroud color
  463. (bright bg -> black, dark bg -> white)
  464. .. todo::
  465. could be useful by other apps, consider moving it into gui_core
  466. """
  467. # gacek,
  468. # http://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color
  469. a = 1 - (0.299 * color[0] + 0.587 * color[1] + 0.114 * color[2]) / 255
  470. if a < 0.5:
  471. d = 0
  472. else:
  473. d = 255
  474. # maybe return just bool if text shoud be dark or bright
  475. return (d, d, d)
  476. class IClassSignatureFileDialog(wx.Dialog):
  477. def __init__(self, parent, group, subgroup,
  478. file=None, title=_("Save signature file"), id=wx.ID_ANY,
  479. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  480. **kwargs):
  481. """Dialog for saving signature file
  482. :param parent: window
  483. :param group: group name
  484. :param file: signature file name
  485. :param title: window title
  486. """
  487. wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
  488. self.fileName = file
  489. env = grass.gisenv()
  490. # inconsistent group and subgroup name
  491. # path:
  492. # grassdata/nc_spm_08/landsat/group/test_group/subgroup/test_group/sig/sigFile
  493. self.baseFilePath = os.path.join(env['GISDBASE'],
  494. env['LOCATION_NAME'],
  495. env['MAPSET'],
  496. 'group', group,
  497. 'subgroup', subgroup,
  498. 'sig')
  499. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  500. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  501. self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
  502. self.btnOK.SetDefault()
  503. self.btnOK.Enable(False)
  504. self.__layout()
  505. self.fileNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
  506. self.OnTextChanged(None)
  507. def OnTextChanged(self, event):
  508. """Name for signature file given"""
  509. file = self.fileNameCtrl.GetValue()
  510. if len(file) > 0:
  511. self.btnOK.Enable(True)
  512. else:
  513. self.btnOK.Enable(False)
  514. path = os.path.join(self.baseFilePath, file)
  515. self.filePathText.SetLabel(path)
  516. bestSize = self.pathPanel.GetBestVirtualSize()
  517. self.pathPanel.SetVirtualSize(bestSize)
  518. self.pathPanel.Scroll(*bestSize)
  519. def __layout(self):
  520. """Do layout"""
  521. sizer = wx.BoxSizer(wx.VERTICAL)
  522. dataSizer = wx.BoxSizer(wx.VERTICAL)
  523. dataSizer.Add(
  524. StaticText(
  525. parent=self.panel,
  526. id=wx.ID_ANY,
  527. label=_("Enter name of signature file:")),
  528. proportion=0,
  529. flag=wx.ALL,
  530. border=3)
  531. self.fileNameCtrl = TextCtrl(
  532. parent=self.panel, id=wx.ID_ANY, size=(400, -1))
  533. if self.fileName:
  534. self.fileNameCtrl.SetValue(self.fileName)
  535. dataSizer.Add(self.fileNameCtrl,
  536. proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  537. dataSizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
  538. label=_("Signature file path:")),
  539. proportion=0, flag=wx.ALL, border=3)
  540. self.pathPanel = scrolled.ScrolledPanel(self.panel, size=(-1, 40))
  541. pathSizer = wx.BoxSizer()
  542. self.filePathText = StaticText(parent=self.pathPanel, id=wx.ID_ANY,
  543. label=self.baseFilePath)
  544. pathSizer.Add(
  545. self.filePathText,
  546. proportion=1,
  547. flag=wx.ALL | wx.EXPAND,
  548. border=1)
  549. self.pathPanel.SetupScrolling(scroll_x=True, scroll_y=False)
  550. self.pathPanel.SetSizer(pathSizer)
  551. dataSizer.Add(self.pathPanel,
  552. proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  553. # buttons
  554. btnSizer = wx.StdDialogButtonSizer()
  555. btnSizer.AddButton(self.btnCancel)
  556. btnSizer.AddButton(self.btnOK)
  557. btnSizer.Realize()
  558. sizer.Add(dataSizer, proportion=1,
  559. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  560. sizer.Add(btnSizer, proportion=0,
  561. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  562. self.panel.SetSizer(sizer)
  563. sizer.Fit(self)
  564. self.SetMinSize(self.GetSize())
  565. def GetFileName(self, fullPath=False):
  566. """Returns signature file name
  567. :param fullPath: return full path of sig. file
  568. """
  569. if fullPath:
  570. return os.path.join(self.baseFilePath,
  571. self.fileNameCtrl.GetValue())
  572. return self.fileNameCtrl.GetValue()
  573. class IClassExportAreasDialog(wx.Dialog):
  574. def __init__(self, parent, vectorName=None,
  575. title=_("Export training areas"),
  576. id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  577. **kwargs):
  578. """Dialog for export of training areas to vector layer
  579. :param parent: window
  580. :param vectorName: name of vector layer for export
  581. :param title: window title
  582. """
  583. wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
  584. self.vectorName = vectorName
  585. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  586. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  587. self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
  588. self.btnOK.SetDefault()
  589. self.btnOK.Enable(False)
  590. self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
  591. self.__layout()
  592. self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
  593. self.OnTextChanged(None)
  594. wx.CallAfter(self.vectorNameCtrl.SetFocus)
  595. def OnTextChanged(self, event):
  596. """Name of new vector map given.
  597. Enable/diable OK button.
  598. """
  599. file = self.vectorNameCtrl.GetValue()
  600. if len(file) > 0:
  601. self.btnOK.Enable(True)
  602. else:
  603. self.btnOK.Enable(False)
  604. def __layout(self):
  605. """Do layout"""
  606. sizer = wx.BoxSizer(wx.VERTICAL)
  607. dataSizer = wx.BoxSizer(wx.VERTICAL)
  608. dataSizer.Add(
  609. StaticText(
  610. parent=self.panel,
  611. id=wx.ID_ANY,
  612. label=_("Enter name of new vector map:")),
  613. proportion=0,
  614. flag=wx.ALL,
  615. border=3)
  616. self.vectorNameCtrl = gselect.Select(
  617. parent=self.panel, type='vector',
  618. mapsets=[grass.gisenv()['MAPSET']],
  619. size=globalvar.DIALOG_GSELECT_SIZE)
  620. if self.vectorName:
  621. self.vectorNameCtrl.SetValue(self.vectorName)
  622. dataSizer.Add(self.vectorNameCtrl,
  623. proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  624. self.withTableCtrl = CheckBox(parent=self.panel, id=wx.ID_ANY,
  625. label=_("Export attribute table"))
  626. self.withTableCtrl.SetValue(True)
  627. self.withTableCtrl.SetToolTip(
  628. _("Export attribute table containing" " computed statistical data"))
  629. dataSizer.Add(self.withTableCtrl,
  630. proportion=0, flag=wx.ALL, border=3)
  631. # buttons
  632. btnSizer = wx.StdDialogButtonSizer()
  633. btnSizer.AddButton(self.btnCancel)
  634. btnSizer.AddButton(self.btnOK)
  635. btnSizer.Realize()
  636. sizer.Add(dataSizer, proportion=1,
  637. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  638. sizer.Add(btnSizer, proportion=0,
  639. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  640. self.panel.SetSizer(sizer)
  641. sizer.Fit(self)
  642. self.SetMinSize(self.GetSize())
  643. def GetVectorName(self):
  644. """Returns vector name"""
  645. return self.vectorNameCtrl.GetValue()
  646. def WithTable(self):
  647. """Returns true if attribute table should be exported too"""
  648. return self.withTableCtrl.IsChecked()
  649. def OnOK(self, event):
  650. """Checks if map exists and can be overwritten."""
  651. overwrite = UserSettings.Get(
  652. group='cmd', key='overwrite', subkey='enabled')
  653. vName = self.GetVectorName()
  654. res = grass.find_file(vName, element='vector')
  655. if res['fullname'] and overwrite is False:
  656. qdlg = wx.MessageDialog(
  657. parent=self, message=_(
  658. "Vector map <%s> already exists."
  659. " Do you want to overwrite it?" %
  660. vName), caption=_(
  661. "Vector <%s> exists" %
  662. vName), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
  663. if qdlg.ShowModal() == wx.ID_YES:
  664. event.Skip()
  665. qdlg.Destroy()
  666. else:
  667. event.Skip()