dialogs.py 26 KB

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