dialogs.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. """
  2. @package vdigit.dialogs
  3. @brief wxGUI vector digitizer dialogs
  4. Classes:
  5. - dialogs::VDigitCategoryDialog
  6. - dialogs::CategoryListCtrl
  7. - dialogs::VDigitZBulkDialog
  8. - dialogs::VDigitDuplicatesDialog
  9. - dialogs::CheckListFeature
  10. (C) 2007-2011 by the GRASS Development Team
  11. This program is free software under the GNU General Public License
  12. (>=v2). Read the file COPYING that comes with GRASS for details.
  13. @author Martin Landa <landa.martin gmail.com>
  14. """
  15. import sys
  16. import copy
  17. import wx
  18. import wx.lib.mixins.listctrl as listmix
  19. from core.gcmd import RunCommand, GError
  20. from core.debug import Debug
  21. from core.settings import UserSettings
  22. from core.utils import _
  23. class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
  24. def __init__(self, parent, title,
  25. vectorName, query=None, cats=None,
  26. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  27. """Dialog used to display/modify categories of vector objects
  28. :param parent:
  29. :param title: dialog title
  30. :param query: {coordinates, qdist} - used by v.edit/v.what
  31. :param cats: directory of lines (layer/categories) - used by vdigit
  32. :param style: dialog style
  33. """
  34. self.parent = parent # map window class instance
  35. self.digit = parent.digit
  36. # map name
  37. self.vectorName = vectorName
  38. # line : {layer: [categories]}
  39. self.cats = {}
  40. # do not display dialog if no line is found (-> self.cats)
  41. if cats is None:
  42. if self._getCategories(query[0], query[1]) == 0 or not self.line:
  43. Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
  44. else:
  45. self.cats = cats
  46. for line in cats.keys():
  47. for layer in cats[line].keys():
  48. self.cats[line][layer] = list(cats[line][layer])
  49. layers = []
  50. for layer in self.digit.GetLayers():
  51. layers.append(str(layer))
  52. # make copy of cats (used for 'reload')
  53. self.cats_orig = copy.deepcopy(self.cats)
  54. wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY, title=title,
  55. style=style, **kwargs)
  56. # list of categories
  57. box = wx.StaticBox(
  58. parent=self, id=wx.ID_ANY, label=" %s " %
  59. _("List of categories - right-click to delete"))
  60. listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  61. self.list = CategoryListCtrl(parent=self, id=wx.ID_ANY,
  62. style=wx.LC_REPORT |
  63. wx.BORDER_NONE |
  64. wx.LC_SORT_ASCENDING |
  65. wx.LC_HRULES |
  66. wx.LC_VRULES)
  67. # sorter
  68. self.fid = self.cats.keys()[0]
  69. self.itemDataMap = self.list.Populate(self.cats[self.fid])
  70. listmix.ColumnSorterMixin.__init__(self, 2)
  71. self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
  72. size=(150, -1))
  73. self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
  74. self.fidText = wx.StaticText(parent=self, id=wx.ID_ANY)
  75. if len(self.cats.keys()) == 1:
  76. self.fidMulti.Show(False)
  77. self.fidText.SetLabel(str(self.fid))
  78. else:
  79. self.fidText.Show(False)
  80. choices = []
  81. for fid in self.cats.keys():
  82. choices.append(str(fid))
  83. self.fidMulti.SetItems(choices)
  84. self.fidMulti.SetSelection(0)
  85. listSizer.Add(item=self.list, proportion=1, flag=wx.EXPAND)
  86. # add new category
  87. box = wx.StaticBox(parent=self, id=wx.ID_ANY,
  88. label=" %s " % _("Add new category"))
  89. addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  90. flexSizer = wx.FlexGridSizer(cols=5, hgap=5, vgap=5)
  91. flexSizer.AddGrowableCol(3)
  92. layerNewTxt = wx.StaticText(parent=self, id=wx.ID_ANY,
  93. label="%s:" % _("Layer"))
  94. self.layerNew = wx.Choice(parent=self, id=wx.ID_ANY, size=(75, -1),
  95. choices=layers)
  96. if len(layers) > 0:
  97. self.layerNew.SetSelection(0)
  98. catNewTxt = wx.StaticText(parent=self, id=wx.ID_ANY,
  99. label="%s:" % _("Category"))
  100. try:
  101. newCat = max(self.cats[self.fid][1]) + 1
  102. except KeyError:
  103. newCat = 1
  104. self.catNew = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(75, -1),
  105. initial=newCat, min=0, max=1e9)
  106. btnAddCat = wx.Button(self, wx.ID_ADD)
  107. flexSizer.Add(item=layerNewTxt, proportion=0,
  108. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  109. flexSizer.Add(item=self.layerNew, proportion=0,
  110. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  111. flexSizer.Add(item=catNewTxt, proportion=0,
  112. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
  113. border=10)
  114. flexSizer.Add(item=self.catNew, proportion=0,
  115. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  116. flexSizer.Add(item=btnAddCat, proportion=0,
  117. flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  118. addSizer.Add(
  119. item=flexSizer,
  120. proportion=1,
  121. flag=wx.ALL | wx.EXPAND,
  122. border=5)
  123. # buttons
  124. btnApply = wx.Button(self, wx.ID_APPLY)
  125. btnApply.SetToolTipString(_("Apply changes"))
  126. btnCancel = wx.Button(self, wx.ID_CANCEL)
  127. btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
  128. btnOk = wx.Button(self, wx.ID_OK)
  129. btnOk.SetToolTipString(_("Apply changes and close dialog"))
  130. btnOk.SetDefault()
  131. # sizers
  132. btnSizer = wx.StdDialogButtonSizer()
  133. btnSizer.AddButton(btnCancel)
  134. # btnSizer.AddButton(btnReload)
  135. # btnSizer.SetNegativeButton(btnReload)
  136. btnSizer.AddButton(btnApply)
  137. btnSizer.AddButton(btnOk)
  138. btnSizer.Realize()
  139. mainSizer = wx.BoxSizer(wx.VERTICAL)
  140. mainSizer.Add(item=listSizer, proportion=1,
  141. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  142. mainSizer.Add(item=addSizer, proportion=0,
  143. flag=wx.EXPAND | wx.ALIGN_CENTER |
  144. wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  145. fidSizer = wx.BoxSizer(wx.HORIZONTAL)
  146. fidSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
  147. label=_("Feature id:")),
  148. proportion=0, border=5,
  149. flag=wx.ALIGN_CENTER_VERTICAL)
  150. fidSizer.Add(item=self.fidMulti, proportion=0,
  151. flag=wx.EXPAND | wx.ALL, border=5)
  152. fidSizer.Add(item=self.fidText, proportion=0,
  153. flag=wx.EXPAND | wx.ALL, border=5)
  154. mainSizer.Add(item=fidSizer, proportion=0,
  155. flag=wx.EXPAND | wx.ALL, border=5)
  156. mainSizer.Add(item=btnSizer, proportion=0,
  157. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  158. self.SetSizer(mainSizer)
  159. mainSizer.Fit(self)
  160. self.SetAutoLayout(True)
  161. # set min size for dialog
  162. self.SetMinSize(self.GetBestSize())
  163. # bindings
  164. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  165. btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
  166. btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
  167. btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  168. self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide())
  169. # list
  170. self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) # wxMSW
  171. self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) # wxGTK
  172. self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
  173. self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
  174. self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
  175. def GetListCtrl(self):
  176. """Used by ColumnSorterMixin
  177. """
  178. return self.list
  179. def OnColClick(self, event):
  180. """Click on column header (order by)
  181. """
  182. event.Skip()
  183. def OnBeginEdit(self, event):
  184. """Editing of item started
  185. """
  186. event.Allow()
  187. def OnEndEdit(self, event):
  188. """Finish editing of item
  189. """
  190. itemIndex = event.GetIndex()
  191. layerOld = int(self.list.GetItem(itemIndex, 0).GetText())
  192. catOld = int(self.list.GetItem(itemIndex, 1).GetText())
  193. if event.GetColumn() == 0:
  194. layerNew = int(event.GetLabel())
  195. catNew = catOld
  196. else:
  197. layerNew = layerOld
  198. catNew = int(event.GetLabel())
  199. try:
  200. if layerNew not in self.cats[self.fid].keys():
  201. self.cats[self.fid][layerNew] = []
  202. self.cats[self.fid][layerNew].append(catNew)
  203. self.cats[self.fid][layerOld].remove(catOld)
  204. except:
  205. event.Veto()
  206. self.list.SetStringItem(itemIndex, 0, str(layerNew))
  207. self.list.SetStringItem(itemIndex, 1, str(catNew))
  208. dlg = wx.MessageDialog(
  209. self, _(
  210. "Unable to add new layer/category <%(layer)s/%(category)s>.\n"
  211. "Layer and category number must be integer.\n"
  212. "Layer number must be greater than zero.") %
  213. {
  214. 'layer': self.layerNew.GetStringSelection(), 'category': str(
  215. self.catNew.GetValue())}, _("Error"), wx.OK | wx.ICON_ERROR)
  216. dlg.ShowModal()
  217. dlg.Destroy()
  218. return False
  219. def OnRightDown(self, event):
  220. """Mouse right button down
  221. """
  222. x = event.GetX()
  223. y = event.GetY()
  224. item, flags = self.list.HitTest((x, y))
  225. if item != wx.NOT_FOUND and \
  226. flags & wx.LIST_HITTEST_ONITEM:
  227. self.list.Select(item)
  228. event.Skip()
  229. def OnRightUp(self, event):
  230. """Mouse right button up
  231. """
  232. if not hasattr(self, "popupID1"):
  233. self.popupID1 = wx.NewId()
  234. self.popupID2 = wx.NewId()
  235. self.popupID3 = wx.NewId()
  236. self.Bind(wx.EVT_MENU, self.OnItemDelete, id=self.popupID1)
  237. self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id=self.popupID2)
  238. self.Bind(wx.EVT_MENU, self.OnReload, id=self.popupID3)
  239. # generate popup-menu
  240. menu = wx.Menu()
  241. menu.Append(self.popupID1, _("Delete selected"))
  242. if self.list.GetFirstSelected() == -1:
  243. menu.Enable(self.popupID1, False)
  244. menu.Append(self.popupID2, _("Delete all"))
  245. menu.AppendSeparator()
  246. menu.Append(self.popupID3, _("Reload"))
  247. self.PopupMenu(menu)
  248. menu.Destroy()
  249. def OnItemSelected(self, event):
  250. """Item selected
  251. """
  252. event.Skip()
  253. def OnItemDelete(self, event):
  254. """Delete selected item(s) from the list (layer/category pair)
  255. """
  256. item = self.list.GetFirstSelected()
  257. while item != -1:
  258. layer = int(self.list.GetItem(item, 0).GetText())
  259. cat = int(self.list.GetItem(item, 1).GetText())
  260. self.list.DeleteItem(item)
  261. self.cats[self.fid][layer].remove(cat)
  262. item = self.list.GetFirstSelected()
  263. event.Skip()
  264. def OnItemDeleteAll(self, event):
  265. """Delete all items from the list
  266. """
  267. self.list.DeleteAllItems()
  268. self.cats[self.fid] = {}
  269. event.Skip()
  270. def OnFeature(self, event):
  271. """Feature id changed (on duplicates)
  272. """
  273. self.fid = int(event.GetString())
  274. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  275. update=True)
  276. try:
  277. newCat = max(self.cats[self.fid][1]) + 1
  278. except KeyError:
  279. newCat = 1
  280. self.catNew.SetValue(newCat)
  281. event.Skip()
  282. def _getCategories(self, coords, qdist):
  283. """Get layer/category pairs for all available
  284. layers
  285. :return: True line found or False if not found
  286. """
  287. ret = RunCommand('v.what',
  288. parent=self,
  289. quiet=True,
  290. map=self.vectorName,
  291. east_north='%f,%f' %
  292. (float(coords[0]), float(coords[1])),
  293. distance=qdist)
  294. if not ret:
  295. return False
  296. for item in ret.splitlines():
  297. litem = item.lower()
  298. if "id:" in litem: # get line id
  299. self.line = int(item.split(':')[1].strip())
  300. elif "layer:" in litem: # add layer
  301. layer = int(item.split(':')[1].strip())
  302. if layer not in self.cats.keys():
  303. self.cats[layer] = []
  304. elif "category:" in litem: # add category
  305. self.cats[layer].append(int(item.split(':')[1].strip()))
  306. return True
  307. def OnReload(self, event):
  308. """Reload button pressed
  309. """
  310. # restore original list
  311. self.cats = copy.deepcopy(self.cats_orig)
  312. # polulate list
  313. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  314. update=True)
  315. event.Skip()
  316. def OnCancel(self, event):
  317. """Cancel button pressed
  318. """
  319. self.parent.parent.dialogs['category'] = None
  320. if self.digit:
  321. self.digit.GetDisplay().SetSelected([])
  322. self.parent.UpdateMap(render=False)
  323. else:
  324. self.parent.parent.OnRender(None)
  325. self.Close()
  326. def OnApply(self, event):
  327. """Apply button pressed
  328. """
  329. for fid in self.cats.keys():
  330. newfid = self.ApplyChanges(fid)
  331. if fid == self.fid and newfid > 0:
  332. self.fid = newfid
  333. def ApplyChanges(self, fid):
  334. """Apply changes
  335. :param fid: feature id
  336. """
  337. cats = self.cats[fid]
  338. cats_orig = self.cats_orig[fid]
  339. # action : (catsFrom, catsTo)
  340. check = {'catadd': (cats, cats_orig),
  341. 'catdel': (cats_orig, cats)}
  342. newfid = -1
  343. # add/delete new category
  344. for action, catsCurr in check.iteritems():
  345. for layer in catsCurr[0].keys():
  346. catList = []
  347. for cat in catsCurr[0][layer]:
  348. if layer not in catsCurr[1].keys() or \
  349. cat not in catsCurr[1][layer]:
  350. catList.append(cat)
  351. if catList != []:
  352. if action == 'catadd':
  353. add = True
  354. else:
  355. add = False
  356. newfid = self.digit.SetLineCats(fid, layer,
  357. catList, add)
  358. if len(self.cats.keys()) == 1:
  359. self.fidText.SetLabel("%d" % newfid)
  360. else:
  361. choices = self.fidMulti.GetItems()
  362. choices[choices.index(str(fid))] = str(newfid)
  363. self.fidMulti.SetItems(choices)
  364. self.fidMulti.SetStringSelection(str(newfid))
  365. self.cats[newfid] = self.cats[fid]
  366. del self.cats[fid]
  367. fid = newfid
  368. if self.fid < 0:
  369. wx.MessageBox(
  370. parent=self,
  371. message=_("Unable to update vector map."),
  372. caption=_("Error"),
  373. style=wx.OK | wx.ICON_ERROR)
  374. self.cats_orig[fid] = copy.deepcopy(cats)
  375. return newfid
  376. def OnOK(self, event):
  377. """OK button pressed
  378. """
  379. self.OnApply(event)
  380. self.OnCancel(event)
  381. def OnAddCat(self, event):
  382. """Button 'Add' new category pressed
  383. """
  384. try:
  385. layer = int(self.layerNew.GetStringSelection())
  386. cat = int(self.catNew.GetValue())
  387. if layer <= 0:
  388. raise ValueError
  389. except ValueError:
  390. GError(
  391. parent=self,
  392. message=_(
  393. "Unable to add new layer/category <%(layer)s/%(category)s>.\n"
  394. "Layer and category number must be integer.\n"
  395. "Layer number must be greater than zero.") % {
  396. 'layer': str(
  397. self.layerNew.GetValue()),
  398. 'category': str(
  399. self.catNew.GetValue())})
  400. return False
  401. if layer not in self.cats[self.fid].keys():
  402. self.cats[self.fid][layer] = []
  403. self.cats[self.fid][layer].append(cat)
  404. # reload list
  405. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  406. update=True)
  407. # update category number for add
  408. self.catNew.SetValue(cat + 1)
  409. event.Skip()
  410. return True
  411. def GetLine(self):
  412. """Get id of selected line of 'None' if no line is selected
  413. """
  414. return self.cats.keys()
  415. def UpdateDialog(self, query=None, cats=None):
  416. """Update dialog
  417. :param query: {coordinates, distance} - v.what
  418. :param cats: directory layer/cats - vdigit
  419. :return: True if updated otherwise False
  420. """
  421. # line: {layer: [categories]}
  422. self.cats = {}
  423. # do not display dialog if no line is found (-> self.cats)
  424. if cats is None:
  425. ret = self._getCategories(query[0], query[1])
  426. else:
  427. self.cats = cats
  428. for line in cats.keys():
  429. for layer in cats[line].keys():
  430. self.cats[line][layer] = list(cats[line][layer])
  431. ret = 1
  432. if ret == 0 or len(self.cats.keys()) < 1:
  433. Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
  434. return False
  435. # make copy of cats (used for 'reload')
  436. self.cats_orig = copy.deepcopy(self.cats)
  437. # polulate list
  438. self.fid = self.cats.keys()[0]
  439. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  440. update=True)
  441. try:
  442. newCat = max(self.cats[self.fid][1]) + 1
  443. except KeyError:
  444. newCat = 1
  445. self.catNew.SetValue(newCat)
  446. if len(self.cats.keys()) == 1:
  447. self.fidText.Show(True)
  448. self.fidMulti.Show(False)
  449. self.fidText.SetLabel("%d" % self.fid)
  450. else:
  451. self.fidText.Show(False)
  452. self.fidMulti.Show(True)
  453. choices = []
  454. for fid in self.cats.keys():
  455. choices.append(str(fid))
  456. self.fidMulti.SetItems(choices)
  457. self.fidMulti.SetSelection(0)
  458. self.Layout()
  459. return True
  460. class CategoryListCtrl(wx.ListCtrl,
  461. listmix.ListCtrlAutoWidthMixin,
  462. listmix.TextEditMixin):
  463. def __init__(self, parent, id, pos=wx.DefaultPosition,
  464. size=wx.DefaultSize, style=0):
  465. """List of layers/categories"""
  466. self.parent = parent
  467. wx.ListCtrl.__init__(self, parent, id, pos, size, style)
  468. listmix.ListCtrlAutoWidthMixin.__init__(self)
  469. listmix.TextEditMixin.__init__(self)
  470. def Populate(self, cats, update=False):
  471. """Populate the list
  472. """
  473. itemData = {} # requested by sorter
  474. if not update:
  475. self.InsertColumn(0, _("Layer"))
  476. self.InsertColumn(1, _("Category"))
  477. else:
  478. self.DeleteAllItems()
  479. i = 1
  480. for layer in cats.keys():
  481. catsList = cats[layer]
  482. for cat in catsList:
  483. index = self.InsertStringItem(self.GetItemCount(), str(catsList[0]))
  484. self.SetStringItem(index, 0, str(layer))
  485. self.SetStringItem(index, 1, str(cat))
  486. self.SetItemData(index, i)
  487. itemData[i] = (str(layer), str(cat))
  488. i = i + 1
  489. if not update:
  490. self.SetColumnWidth(0, 100)
  491. self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
  492. self.currentItem = 0
  493. return itemData
  494. class VDigitZBulkDialog(wx.Dialog):
  495. def __init__(self, parent, title, nselected,
  496. style=wx.DEFAULT_DIALOG_STYLE):
  497. """Dialog used for Z bulk-labeling tool
  498. """
  499. wx.Dialog.__init__(
  500. self,
  501. parent=parent,
  502. id=wx.ID_ANY,
  503. title=title,
  504. style=style)
  505. self.parent = parent # map window class instance
  506. # panel = wx.Panel(parent=self, id=wx.ID_ANY)
  507. border = wx.BoxSizer(wx.VERTICAL)
  508. txt = wx.StaticText(
  509. parent=self,
  510. label=_("%d lines selected for z bulk-labeling") %
  511. nselected)
  512. border.Add(item=txt, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  513. box = wx.StaticBox(
  514. parent=self,
  515. id=wx.ID_ANY,
  516. label=" %s " %
  517. _("Set value"))
  518. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  519. flexSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
  520. flexSizer.AddGrowableCol(0)
  521. # starting value
  522. txt = wx.StaticText(parent=self,
  523. label=_("Starting value"))
  524. self.value = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(150, -1),
  525. initial=0,
  526. min=-1e6, max=1e6)
  527. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  528. flexSizer.Add(
  529. self.value,
  530. proportion=0,
  531. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  532. # step
  533. txt = wx.StaticText(parent=self,
  534. label=_("Step"))
  535. self.step = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(150, -1),
  536. initial=0,
  537. min=0, max=1e6)
  538. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  539. flexSizer.Add(
  540. self.step,
  541. proportion=0,
  542. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  543. sizer.Add(
  544. item=flexSizer,
  545. proportion=1,
  546. flag=wx.ALL | wx.EXPAND,
  547. border=1)
  548. border.Add(item=sizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=0)
  549. # buttons
  550. btnCancel = wx.Button(self, wx.ID_CANCEL)
  551. btnOk = wx.Button(self, wx.ID_OK)
  552. btnOk.SetDefault()
  553. # sizers
  554. btnSizer = wx.StdDialogButtonSizer()
  555. btnSizer.AddButton(btnCancel)
  556. btnSizer.AddButton(btnOk)
  557. btnSizer.Realize()
  558. mainSizer = wx.BoxSizer(wx.VERTICAL)
  559. mainSizer.Add(
  560. item=border,
  561. proportion=1,
  562. flag=wx.EXPAND | wx.ALL,
  563. border=5)
  564. mainSizer.Add(item=btnSizer, proportion=0,
  565. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  566. self.SetSizer(mainSizer)
  567. mainSizer.Fit(self)
  568. class VDigitDuplicatesDialog(wx.Dialog):
  569. def __init__(self, parent, data, title=_("List of duplicates"),
  570. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  571. pos=wx.DefaultPosition):
  572. """Show duplicated feature ids
  573. """
  574. wx.Dialog.__init__(
  575. self,
  576. parent=parent,
  577. id=wx.ID_ANY,
  578. title=title,
  579. style=style,
  580. pos=pos)
  581. self.parent = parent # map window instance
  582. self.data = data
  583. self.winList = []
  584. # panel = wx.Panel(parent=self, id=wx.ID_ANY)
  585. # notebook
  586. self.notebook = wx.Notebook(
  587. parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
  588. id = 1
  589. for key in self.data.keys():
  590. panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
  591. self.notebook.AddPage(page=panel, text=" %d " % (id))
  592. # notebook body
  593. border = wx.BoxSizer(wx.VERTICAL)
  594. win = CheckListFeature(parent=panel, data=list(self.data[key]))
  595. self.winList.append(win.GetId())
  596. border.Add(item=win, proportion=1,
  597. flag=wx.ALL | wx.EXPAND, border=5)
  598. panel.SetSizer(border)
  599. id += 1
  600. # buttons
  601. btnCancel = wx.Button(self, wx.ID_CANCEL)
  602. btnOk = wx.Button(self, wx.ID_OK)
  603. btnOk.SetDefault()
  604. # sizers
  605. btnSizer = wx.StdDialogButtonSizer()
  606. btnSizer.AddButton(btnCancel)
  607. btnSizer.AddButton(btnOk)
  608. btnSizer.Realize()
  609. mainSizer = wx.BoxSizer(wx.VERTICAL)
  610. mainSizer.Add(
  611. item=self.notebook,
  612. proportion=1,
  613. flag=wx.EXPAND | wx.ALL,
  614. border=5)
  615. mainSizer.Add(item=btnSizer, proportion=0,
  616. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  617. self.SetSizer(mainSizer)
  618. mainSizer.Fit(self)
  619. self.SetAutoLayout(True)
  620. # set min size for dialog
  621. self.SetMinSize((250, 180))
  622. def GetUnSelected(self):
  623. """Get unselected items (feature id)
  624. :return: list of ids
  625. """
  626. ids = []
  627. for id in self.winList:
  628. wlist = self.FindWindowById(id)
  629. for item in range(wlist.GetItemCount()):
  630. if not wlist.IsChecked(item):
  631. ids.append(int(wlist.GetItem(item, 0).GetText()))
  632. return ids
  633. class CheckListFeature(
  634. wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
  635. def __init__(self, parent, data,
  636. pos=wx.DefaultPosition, log=None):
  637. """List of mapset/owner/group
  638. """
  639. self.parent = parent
  640. self.data = data
  641. wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
  642. style=wx.LC_REPORT)
  643. listmix.CheckListCtrlMixin.__init__(self)
  644. self.log = log
  645. # setup mixins
  646. listmix.ListCtrlAutoWidthMixin.__init__(self)
  647. self.LoadData(self.data)
  648. def LoadData(self, data):
  649. """Load data into list
  650. """
  651. self.InsertColumn(0, _('Feature id'))
  652. self.InsertColumn(1, _('Layer (Categories)'))
  653. for item in data:
  654. index = self.InsertStringItem(self.GetItemCount(), str(item[0]))
  655. self.SetStringItem(index, 1, str(item[1]))
  656. # enable all items by default
  657. for item in range(self.GetItemCount()):
  658. self.CheckItem(item, True)
  659. self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE_USEHEADER)
  660. self.SetColumnWidth(col=1, width=wx.LIST_AUTOSIZE_USEHEADER)
  661. def OnCheckItem(self, index, flag):
  662. """Mapset checked/unchecked
  663. """
  664. pass