dialogs.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. """
  2. @package iscatt.dialogs
  3. @brief Dialogs widgets.
  4. Classes:
  5. - dialogs::AddScattPlotDialog
  6. - dialogs::ExportCategoryRaster
  7. - dialogs::SettingsDialog
  8. - dialogs::ManageBusyCursorMixin
  9. - dialogs::RenameClassDialog
  10. (C) 2013 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 Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
  14. """
  15. import six
  16. import wx
  17. from gui_core.gselect import Select
  18. import wx.lib.colourselect as csel
  19. import grass.script as grass
  20. from core import globalvar
  21. from core.gcmd import GMessage
  22. from core.settings import UserSettings
  23. from gui_core.dialogs import SimpleDialog
  24. from gui_core.wrap import SpinCtrl, Button, StaticText, StaticBox, TextCtrl
  25. class AddScattPlotDialog(wx.Dialog):
  26. def __init__(self, parent, bands, check_bands_callback, id=wx.ID_ANY):
  27. wx.Dialog.__init__(self, parent, title=_("Add scatter plots"), id=id)
  28. self.bands = bands
  29. self.x_band = None
  30. self.y_band = None
  31. self.chb_callback = check_bands_callback
  32. self.added_bands_ids = []
  33. self.sel_bands_ids = []
  34. self._createWidgets()
  35. def _createWidgets(self):
  36. self.labels = {}
  37. self.params = {}
  38. self.band_1_label = StaticText(parent=self, id=wx.ID_ANY, label=_("x axis:"))
  39. self.band_1_ch = wx.ComboBox(
  40. parent=self,
  41. id=wx.ID_ANY,
  42. choices=self.bands,
  43. style=wx.CB_READONLY,
  44. size=(350, 30),
  45. )
  46. self.band_2_label = StaticText(parent=self, id=wx.ID_ANY, label=_("y axis:"))
  47. self.band_2_ch = wx.ComboBox(
  48. parent=self,
  49. id=wx.ID_ANY,
  50. choices=self.bands,
  51. style=wx.CB_READONLY,
  52. size=(350, 30),
  53. )
  54. self.scattsBox = wx.ListBox(
  55. parent=self,
  56. id=wx.ID_ANY,
  57. size=(-1, 150),
  58. style=wx.LB_MULTIPLE | wx.LB_NEEDED_SB,
  59. )
  60. # buttons
  61. self.btn_add = Button(parent=self, id=wx.ID_ADD)
  62. self.btn_remove = Button(parent=self, id=wx.ID_REMOVE)
  63. self.btn_close = Button(parent=self, id=wx.ID_CANCEL)
  64. self.btn_ok = Button(parent=self, id=wx.ID_OK)
  65. self._layout()
  66. def _layout(self):
  67. border = wx.BoxSizer(wx.VERTICAL)
  68. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  69. regionSizer = wx.BoxSizer(wx.HORIZONTAL)
  70. dialogSizer.Add(
  71. self._addSelectSizer(title=self.band_1_label, sel=self.band_1_ch)
  72. )
  73. dialogSizer.Add(
  74. self._addSelectSizer(title=self.band_2_label, sel=self.band_2_ch)
  75. )
  76. dialogSizer.Add(
  77. self.btn_add, proportion=0, flag=wx.TOP | wx.ALIGN_RIGHT, border=5
  78. )
  79. box = StaticBox(
  80. self,
  81. id=wx.ID_ANY,
  82. label=" %s " % _("Bands of scatter plots to be added (x y):"),
  83. )
  84. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  85. sizer.Add(self.scattsBox, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
  86. sizer.Add(self.btn_remove, proportion=0, flag=wx.TOP | wx.ALIGN_RIGHT, border=5)
  87. dialogSizer.Add(sizer, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
  88. # buttons
  89. self.btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  90. self.btnsizer.Add(
  91. self.btn_close,
  92. proportion=0,
  93. flag=wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
  94. border=10,
  95. )
  96. self.btnsizer.Add(
  97. self.btn_ok,
  98. proportion=0,
  99. flag=wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER,
  100. border=10,
  101. )
  102. dialogSizer.Add(
  103. self.btnsizer, proportion=0, flag=wx.ALIGN_CENTER | wx.TOP, border=5
  104. )
  105. border.Add(
  106. dialogSizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10
  107. )
  108. self.SetSizer(border)
  109. self.Layout()
  110. self.Fit()
  111. # bindings
  112. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  113. self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
  114. self.btn_add.Bind(wx.EVT_BUTTON, self.OnAdd)
  115. self.btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
  116. def OnOk(self, event):
  117. if not self.GetBands():
  118. GMessage(parent=self, message=_("No scatter plots selected."))
  119. return
  120. event.Skip()
  121. def _addSelectSizer(self, title, sel):
  122. """Helper layout function."""
  123. selSizer = wx.BoxSizer(orient=wx.VERTICAL)
  124. selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
  125. selTitleSizer.Add(title, proportion=1, flag=wx.TOP | wx.EXPAND, border=5)
  126. selSizer.Add(selTitleSizer, proportion=0, flag=wx.EXPAND)
  127. selSizer.Add(sel, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
  128. return selSizer
  129. def GetBands(self):
  130. """Get layers"""
  131. return self.sel_bands_ids
  132. def OnClose(self, event):
  133. """Close dialog"""
  134. if not self.IsModal():
  135. self.Destroy()
  136. event.Skip()
  137. def OnRemoveLayer(self, event):
  138. """Remove layer from listbox"""
  139. while self.scattsBox.GetSelections():
  140. sel = self.scattsBox.GetSelections()[0]
  141. self.scattsBox.Delete(sel)
  142. self.sel_bands_ids.pop(sel)
  143. def OnAdd(self, event):
  144. b_x = self.band_1_ch.GetSelection()
  145. b_y = self.band_2_ch.GetSelection()
  146. if b_x < 0 or b_y < 0:
  147. GMessage(parent=self, message=_("Select both x and y bands."))
  148. return
  149. if b_y == b_x:
  150. GMessage(parent=self, message=_("Selected bands must be different."))
  151. return
  152. if [b_x, b_y] in self.sel_bands_ids or [b_y, b_x] in self.sel_bands_ids:
  153. GMessage(
  154. parent=self,
  155. message=_(
  156. "Scatter plot with same bands combination (regardless x y order) "
  157. "has been already added into the list."
  158. ),
  159. )
  160. return
  161. if not self.chb_callback(b_x, b_y):
  162. return
  163. self.sel_bands_ids.append([b_x, b_y])
  164. b_x_str = self.band_1_ch.GetStringSelection()
  165. b_y_str = self.band_2_ch.GetStringSelection()
  166. text = b_x_str + " " + b_y_str
  167. self.scattsBox.Append(text)
  168. event.Skip()
  169. class ExportCategoryRaster(wx.Dialog):
  170. def __init__(
  171. self,
  172. parent,
  173. title,
  174. rasterName=None,
  175. id=wx.ID_ANY,
  176. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  177. **kwargs,
  178. ):
  179. """Dialog for export of category raster.
  180. :param parent: window
  181. :param str rasterName name of vector layer for export
  182. :param title: window title
  183. """
  184. wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
  185. self.rasterName = rasterName
  186. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  187. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  188. self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
  189. self.btnOK.SetDefault()
  190. self.btnOK.Enable(False)
  191. self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
  192. self.__layout()
  193. self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
  194. self.OnTextChanged(None)
  195. wx.CallAfter(self.vectorNameCtrl.SetFocus)
  196. def OnTextChanged(self, event):
  197. """Name of new vector map given.
  198. Enable/diable OK button.
  199. """
  200. file = self.vectorNameCtrl.GetValue()
  201. if len(file) > 0:
  202. self.btnOK.Enable(True)
  203. else:
  204. self.btnOK.Enable(False)
  205. def __layout(self):
  206. """Do layout"""
  207. sizer = wx.BoxSizer(wx.VERTICAL)
  208. dataSizer = wx.BoxSizer(wx.VERTICAL)
  209. dataSizer.Add(
  210. StaticText(
  211. parent=self.panel,
  212. id=wx.ID_ANY,
  213. label=_("Enter name of new vector map:"),
  214. ),
  215. proportion=0,
  216. flag=wx.ALL,
  217. border=3,
  218. )
  219. self.vectorNameCtrl = Select(
  220. parent=self.panel,
  221. type="raster",
  222. mapsets=[grass.gisenv()["MAPSET"]],
  223. size=globalvar.DIALOG_GSELECT_SIZE,
  224. )
  225. if self.rasterName:
  226. self.vectorNameCtrl.SetValue(self.rasterName)
  227. dataSizer.Add(
  228. self.vectorNameCtrl, proportion=0, flag=wx.ALL | wx.EXPAND, border=3
  229. )
  230. # buttons
  231. btnSizer = wx.StdDialogButtonSizer()
  232. btnSizer.AddButton(self.btnCancel)
  233. btnSizer.AddButton(self.btnOK)
  234. btnSizer.Realize()
  235. sizer.Add(
  236. dataSizer, proportion=1, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5
  237. )
  238. sizer.Add(
  239. btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5
  240. )
  241. self.panel.SetSizer(sizer)
  242. sizer.Fit(self)
  243. self.SetMinSize(self.GetSize())
  244. def GetRasterName(self):
  245. """Returns vector name"""
  246. return self.vectorNameCtrl.GetValue()
  247. def OnOK(self, event):
  248. """Checks if map exists and can be overwritten."""
  249. overwrite = UserSettings.Get(group="cmd", key="overwrite", subkey="enabled")
  250. rast_name = self.GetRasterName()
  251. res = grass.find_file(rast_name, element="cell")
  252. if res["fullname"] and overwrite is False:
  253. qdlg = wx.MessageDialog(
  254. parent=self,
  255. message=_(
  256. "Raster map <%s> already exists."
  257. " Do you want to overwrite it?" % rast_name
  258. ),
  259. caption=_("Raster <%s> exists" % rast_name),
  260. style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE,
  261. )
  262. if qdlg.ShowModal() == wx.ID_YES:
  263. event.Skip()
  264. qdlg.Destroy()
  265. else:
  266. event.Skip()
  267. class SettingsDialog(wx.Dialog):
  268. def __init__(
  269. self,
  270. parent,
  271. id,
  272. title,
  273. scatt_mgr,
  274. pos=wx.DefaultPosition,
  275. size=wx.DefaultSize,
  276. style=wx.DEFAULT_DIALOG_STYLE,
  277. ):
  278. """Settings dialog"""
  279. wx.Dialog.__init__(self, parent, id, title, pos, size, style)
  280. self.scatt_mgr = scatt_mgr
  281. maxValue = 1e8
  282. self.parent = parent
  283. self.settings = {}
  284. settsLabels = {}
  285. self.settings["show_ellips"] = wx.CheckBox(
  286. parent=self, id=wx.ID_ANY, label=_("Show confidence ellipses")
  287. )
  288. show_ellips = UserSettings.Get(
  289. group="scatt", key="ellipses", subkey="show_ellips"
  290. )
  291. self.settings["show_ellips"].SetValue(show_ellips)
  292. self.colorsSetts = {
  293. "sel_pol": ["selection", _("Selection polygon color:")],
  294. "sel_pol_vertex": ["selection", _("Color of selection polygon vertex:")],
  295. "sel_area": ["selection", _("Selected area color:")],
  296. }
  297. for settKey, sett in six.iteritems(self.colorsSetts):
  298. settsLabels[settKey] = StaticText(parent=self, id=wx.ID_ANY, label=sett[1])
  299. col = UserSettings.Get(group="scatt", key=sett[0], subkey=settKey)
  300. self.settings[settKey] = csel.ColourSelect(
  301. parent=self, id=wx.ID_ANY, colour=wx.Colour(col[0], col[1], col[2], 255)
  302. )
  303. self.sizeSetts = {
  304. "snap_tresh": ["selection", _("Snapping threshold in pixels:")],
  305. "sel_area_opacty": ["selection", _("Selected area opacity:")],
  306. }
  307. for settKey, sett in six.iteritems(self.sizeSetts):
  308. settsLabels[settKey] = StaticText(parent=self, id=wx.ID_ANY, label=sett[1])
  309. self.settings[settKey] = SpinCtrl(parent=self, id=wx.ID_ANY, min=0, max=100)
  310. size = int(UserSettings.Get(group="scatt", key=sett[0], subkey=settKey))
  311. self.settings[settKey].SetValue(size)
  312. # buttons
  313. self.btnSave = Button(self, wx.ID_SAVE)
  314. self.btnApply = Button(self, wx.ID_APPLY)
  315. self.btnClose = Button(self, wx.ID_CLOSE)
  316. self.btnApply.SetDefault()
  317. # bindings
  318. self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  319. self.btnApply.SetToolTip(_("Apply changes for the current session"))
  320. self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
  321. self.btnSave.SetToolTip(
  322. _(
  323. "Apply and save changes to user settings file (default for next sessions)"
  324. )
  325. )
  326. self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
  327. self.btnClose.SetToolTip(_("Close dialog"))
  328. # Layout
  329. # Analysis result style layout
  330. self.SetMinSize(self.GetBestSize())
  331. sizer = wx.BoxSizer(wx.VERTICAL)
  332. sel_pol_box = StaticBox(
  333. parent=self, id=wx.ID_ANY, label=" %s " % _("Selection style:")
  334. )
  335. selPolBoxSizer = wx.StaticBoxSizer(sel_pol_box, wx.VERTICAL)
  336. gridSizer = wx.GridBagSizer(vgap=1, hgap=1)
  337. row = 0
  338. setts = dict()
  339. setts.update(self.colorsSetts)
  340. setts.update(self.sizeSetts)
  341. settsOrder = [
  342. "sel_pol",
  343. "sel_pol_vertex",
  344. "sel_area",
  345. "sel_area_opacty",
  346. "snap_tresh",
  347. ]
  348. for settKey in settsOrder:
  349. sett = setts[settKey]
  350. gridSizer.Add(
  351. settsLabels[settKey], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)
  352. )
  353. gridSizer.Add(
  354. self.settings[settKey],
  355. flag=wx.ALIGN_RIGHT | wx.ALL,
  356. border=5,
  357. pos=(row, 1),
  358. )
  359. row += 1
  360. gridSizer.AddGrowableCol(1)
  361. selPolBoxSizer.Add(gridSizer, flag=wx.EXPAND)
  362. ell_box = StaticBox(
  363. parent=self, id=wx.ID_ANY, label=" %s " % _("Ellipses settings:")
  364. )
  365. ellPolBoxSizer = wx.StaticBoxSizer(ell_box, wx.VERTICAL)
  366. gridSizer = wx.GridBagSizer(vgap=1, hgap=1)
  367. sett = setts[settKey]
  368. row = 0
  369. gridSizer.Add(
  370. self.settings["show_ellips"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)
  371. )
  372. gridSizer.AddGrowableCol(0)
  373. ellPolBoxSizer.Add(gridSizer, flag=wx.EXPAND)
  374. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  375. btnSizer.Add(self.btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
  376. btnSizer.Add(self.btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
  377. btnSizer.Add(self.btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
  378. sizer.Add(selPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
  379. sizer.Add(ellPolBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
  380. sizer.Add(btnSizer, flag=wx.EXPAND | wx.ALL, border=5, proportion=0)
  381. self.SetSizer(sizer)
  382. sizer.Fit(self)
  383. def OnSave(self, event):
  384. """Button 'Save' pressed"""
  385. self.UpdateSettings()
  386. fileSettings = {}
  387. UserSettings.ReadSettingsFile(settings=fileSettings)
  388. fileSettings["scatt"] = UserSettings.Get(group="scatt")
  389. UserSettings.SaveToFile(fileSettings)
  390. self.Close()
  391. def UpdateSettings(self):
  392. chanaged_setts = []
  393. for settKey, sett in six.iteritems(self.colorsSetts):
  394. col = tuple(self.settings[settKey].GetColour())
  395. col_s = UserSettings.Get(group="scatt", key=sett[0], subkey=settKey)
  396. if col_s != col:
  397. UserSettings.Set(group="scatt", key=sett[0], subkey=settKey, value=col)
  398. chanaged_setts.append([settKey, sett[0]])
  399. for settKey, sett in six.iteritems(self.sizeSetts):
  400. val = self.settings[settKey].GetValue()
  401. val_s = UserSettings.Get(group="scatt", key=sett[0], subkey=settKey)
  402. if val_s != val:
  403. UserSettings.Set(group="scatt", key=sett[0], subkey=settKey, value=val)
  404. chanaged_setts.append([settKey, sett[0]])
  405. val = self.settings["show_ellips"].IsChecked()
  406. val_s = UserSettings.Get(group="scatt", key="ellipses", subkey="show_ellips")
  407. if val != val_s:
  408. UserSettings.Set(
  409. group="scatt", key="ellipses", subkey="show_ellips", value=val
  410. )
  411. chanaged_setts.append(["ellipses", "show_ellips"])
  412. if chanaged_setts:
  413. self.scatt_mgr.SettingsUpdated(chanaged_setts)
  414. def OnApply(self, event):
  415. """Button 'Apply' pressed"""
  416. self.UpdateSettings()
  417. # self.Close()
  418. def OnClose(self, event):
  419. """Button 'Cancel' pressed"""
  420. self.Close()
  421. class ManageBusyCursorMixin:
  422. def __init__(self, window):
  423. self.win = window
  424. self.is_busy = False
  425. self.cur_inside = False
  426. self.win.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
  427. self.win.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
  428. def OnLeave(self, event):
  429. self.cur_inside = False
  430. self.busy_cur = None
  431. def OnEnter(self, event):
  432. self.cur_inside = True
  433. if self.is_busy:
  434. self.busy_cur = wx.BusyCursor()
  435. def UpdateCur(self, busy):
  436. self.is_busy = busy
  437. if self.cur_inside and self.is_busy:
  438. self.busy_cur = wx.BusyCursor()
  439. return
  440. self.busy_cur = None
  441. class RenameClassDialog(SimpleDialog):
  442. def __init__(self, parent, old_name, title=("Change class name")):
  443. SimpleDialog.__init__(self, parent, title)
  444. self.name = TextCtrl(self.panel, id=wx.ID_ANY)
  445. self.name.SetValue(old_name)
  446. self.dataSizer.Add(self.name, proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
  447. self.panel.SetSizer(self.sizer)
  448. self.name.SetMinSize((200, -1))
  449. self.sizer.Fit(self)
  450. def GetNewName(self):
  451. return self.name.GetValue()