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