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