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