Ejemplo n.º 1
0
class TextShower(object):
    def __init__(self, parent, title):
        self._cf = wx.Frame(parent=parent, title=title)
        self._cp = wx.Panel(parent=self._cf, id=wx.ID_ANY)
        self._cs = wx.BoxSizer(wx.VERTICAL)
        self._cl = StaticText(parent=self._cp,
                              id=wx.ID_ANY,
                              label="No text set yet")
        self._cs.Add(self._cl, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        self._cp.SetSizer(self._cs)
        self._cp.Layout()
        self._cf.Show()

    def SetLabel(self, text):
        self._cl.SetLabel(text)
Ejemplo n.º 2
0
class RegionDef(BaseClass, wx.Dialog):
    """Page for setting default region extents and resolution
    """

    def __init__(self, parent, id=wx.ID_ANY, size=(800, 600), title=_(
            "Set default region extent and resolution"), location=None):
        wx.Dialog.__init__(self, parent, id, title, size=size)
        panel = wx.Panel(self, id=wx.ID_ANY)

        self.SetIcon(
            wx.Icon(
                os.path.join(
                    globalvar.ICONDIR,
                    'grass.ico'),
                wx.BITMAP_TYPE_ICO))

        self.parent = parent
        self.location = location

        #
        # default values
        #
        # 2D
        self.north = 1.0
        self.south = 0.0
        self.east = 1.0
        self.west = 0.0
        self.nsres = 1.0
        self.ewres = 1.0
        # 3D
        self.top = 1.0
        self.bottom = 0.0
        #         self.nsres3 = 1.0
        #         self.ewres3 = 1.0
        self.tbres = 1.0

        #
        # inputs
        #
        # 2D
        self.tnorth = self.MakeTextCtrl(
            text=str(
                self.north), size=(
                150, -1), parent=panel)
        self.tsouth = self.MakeTextCtrl(
            str(self.south),
            size=(150, -1),
            parent=panel)
        self.twest = self.MakeTextCtrl(
            str(self.west),
            size=(150, -1),
            parent=panel)
        self.teast = self.MakeTextCtrl(
            str(self.east),
            size=(150, -1),
            parent=panel)
        self.tnsres = self.MakeTextCtrl(
            str(self.nsres),
            size=(150, -1),
            parent=panel)
        self.tewres = self.MakeTextCtrl(
            str(self.ewres),
            size=(150, -1),
            parent=panel)

        #
        # labels
        #
        self.lrows = self.MakeLabel(parent=panel)
        self.lcols = self.MakeLabel(parent=panel)
        self.lcells = self.MakeLabel(parent=panel)

        #
        # buttons
        #
        self.bset = self.MakeButton(
            text=_("&Set region"),
            id=wx.ID_OK, parent=panel)
        self.bcancel = Button(panel, id=wx.ID_CANCEL)
        self.bset.SetDefault()

        #
        # image
        #
        self.img = wx.Image(os.path.join(globalvar.IMGDIR, "qgis_world.png"),
                            wx.BITMAP_TYPE_PNG).ConvertToBitmap()

        #
        # set current working environment to PERMANENT mapset
        # in selected location in order to set default region (WIND)
        #
        envval = {}
        ret = RunCommand('g.gisenv',
                         read=True)
        if ret:
            for line in ret.splitlines():
                key, val = line.split('=')
                envval[key] = val
            self.currlocation = envval['LOCATION_NAME'].strip("';")
            self.currmapset = envval['MAPSET'].strip("';")
            if self.currlocation != self.location or self.currmapset != 'PERMANENT':
                RunCommand('g.gisenv',
                           set='LOCATION_NAME=%s' % self.location)
                RunCommand('g.gisenv',
                           set='MAPSET=PERMANENT')
        else:
            dlg = wx.MessageBox(
                parent=self,
                message=_('Invalid location selected.'),
                caption=_("Error"),
                style=wx.ID_OK | wx.ICON_ERROR)
            return

        #
        # get current region settings
        #
        region = {}
        ret = RunCommand('g.region',
                         read=True,
                         flags='gp3')
        if ret:
            for line in ret.splitlines():
                key, val = line.split('=')
                region[key] = float(val)
        else:
            dlg = wx.MessageBox(
                parent=self,
                message=_("Invalid region"),
                caption=_("Error"),
                style=wx.ID_OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            return

        #
        # update values
        # 2D
        self.north = float(region['n'])
        self.south = float(region['s'])
        self.east = float(region['e'])
        self.west = float(region['w'])
        self.nsres = float(region['nsres'])
        self.ewres = float(region['ewres'])
        self.rows = int(region['rows'])
        self.cols = int(region['cols'])
        self.cells = int(region['cells'])
        # 3D
        self.top = float(region['t'])
        self.bottom = float(region['b'])
        #         self.nsres3 = float(region['nsres3'])
        #         self.ewres3 = float(region['ewres3'])
        self.tbres = float(region['tbres'])
        self.depth = int(region['depths'])
        self.cells3 = int(region['cells3'])

        #
        # 3D box collapsable
        #
        self.infoCollapseLabelExp = _("Click here to show 3D settings")
        self.infoCollapseLabelCol = _("Click here to hide 3D settings")
        self.settings3D = wx.CollapsiblePane(parent=panel,
                                             label=self.infoCollapseLabelExp,
                                             style=wx.CP_DEFAULT_STYLE |
                                             wx.CP_NO_TLW_RESIZE | wx.EXPAND)
        self.MakeSettings3DPaneContent(self.settings3D.GetPane())
        self.settings3D.Collapse(False)  # FIXME
        self.Bind(
            wx.EVT_COLLAPSIBLEPANE_CHANGED,
            self.OnSettings3DPaneChanged,
            self.settings3D)

        #
        # set current region settings
        #
        self.tnorth.SetValue(str(self.north))
        self.tsouth.SetValue(str(self.south))
        self.twest.SetValue(str(self.west))
        self.teast.SetValue(str(self.east))
        self.tnsres.SetValue(str(self.nsres))
        self.tewres.SetValue(str(self.ewres))
        self.ttop.SetValue(str(self.top))
        self.tbottom.SetValue(str(self.bottom))
        #         self.tnsres3.SetValue(str(self.nsres3))
        #         self.tewres3.SetValue(str(self.ewres3))
        self.ttbres.SetValue(str(self.tbres))
        self.lrows.SetLabel(_("Rows: %d") % self.rows)
        self.lcols.SetLabel(_("Cols: %d") % self.cols)
        self.lcells.SetLabel(_("Cells: %d") % self.cells)

        #
        # bindings
        #
        self.Bind(wx.EVT_BUTTON, self.OnSetButton, self.bset)
        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.bcancel)
        self.tnorth.Bind(wx.EVT_TEXT, self.OnValue)
        self.tsouth.Bind(wx.EVT_TEXT, self.OnValue)
        self.teast.Bind(wx.EVT_TEXT, self.OnValue)
        self.twest.Bind(wx.EVT_TEXT, self.OnValue)
        self.tnsres.Bind(wx.EVT_TEXT, self.OnValue)
        self.tewres.Bind(wx.EVT_TEXT, self.OnValue)
        self.ttop.Bind(wx.EVT_TEXT, self.OnValue)
        self.tbottom.Bind(wx.EVT_TEXT, self.OnValue)
        #         self.tnsres3.Bind(wx.EVT_TEXT,  self.OnValue)
        #         self.tewres3.Bind(wx.EVT_TEXT,  self.OnValue)
        self.ttbres.Bind(wx.EVT_TEXT, self.OnValue)

        self.__DoLayout(panel)
        self.SetMinSize(self.GetBestSize())
        self.minWindowSize = self.GetMinSize()
        wx.CallAfter(self.settings3D.Collapse, True)

    def MakeSettings3DPaneContent(self, pane):
        """Create 3D region settings pane"""
        border = wx.BoxSizer(wx.VERTICAL)
        gridSizer = wx.GridBagSizer(vgap=0, hgap=0)

        # inputs
        self.ttop = TextCtrl(parent=pane, id=wx.ID_ANY, value=str(self.top),
                             size=(150, -1))
        self.tbottom = TextCtrl(
            parent=pane, id=wx.ID_ANY, value=str(
                self.bottom), size=(
                150, -1))
        self.ttbres = TextCtrl(
            parent=pane, id=wx.ID_ANY, value=str(
                self.tbres), size=(
                150, -1))
        #         self.tnsres3 = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.nsres3),
        #                                    size = (150, -1))
        #         self.tewres3  =  wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.ewres3),
        #                                    size = (150, -1))

        # labels
        self.ldepth = StaticText(
            parent=pane,
            label=_("Depth: %d") %
            self.depth)
        self.lcells3 = StaticText(
            parent=pane,
            label=_("3D Cells: %d") %
            self.cells3)

        # top
        gridSizer.Add(StaticText(parent=pane, label=_("Top")),
                      flag=wx.ALIGN_CENTER |
                      wx.LEFT | wx.RIGHT | wx.TOP, border=5,
                      pos=(0, 1))
        gridSizer.Add(self.ttop,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALL, border=5, pos=(1, 1))
        # bottom
        gridSizer.Add(StaticText(parent=pane, label=_("Bottom")),
                      flag=wx.ALIGN_CENTER |
                      wx.LEFT | wx.RIGHT | wx.TOP, border=5,
                      pos=(0, 2))
        gridSizer.Add(self.tbottom,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALL, border=5, pos=(1, 2))
        # tbres
        gridSizer.Add(
            StaticText(
                parent=pane,
                label=_("T-B resolution")),
            flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.TOP,
            border=5,
            pos=(
                0,
                3))
        gridSizer.Add(self.ttbres,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALL, border=5, pos=(1, 3))

        # res
        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D N-S resolution")),
        #                       flag = wx.ALIGN_CENTER |
        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
        #                       pos = (2, 1))
        #         gridSizer.Add(item = self.tnsres3,
        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
        #                       wx.ALL, border = 5, pos = (3, 1))
        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D E-W resolution")),
        #                       flag = wx.ALIGN_CENTER |
        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
        #                       pos = (2, 3))
        #         gridSizer.Add(item = self.tewres3,
        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
        #                       wx.ALL, border = 5, pos = (3, 3))

        # rows/cols/cells
        gridSizer.Add(self.ldepth,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.ALL, border=5, pos=(2, 1))

        gridSizer.Add(self.lcells3,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.ALL, border=5, pos=(2, 2))

        border.Add(gridSizer, proportion=1,
                   flag=wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, border=5)

        pane.SetSizer(border)
        border.Fit(pane)

    def OnSettings3DPaneChanged(self, event):
        """Collapse 3D settings box"""

        if self.settings3D.IsExpanded():
            self.settings3D.SetLabel(self.infoCollapseLabelCol)
            self.Layout()
            self.SetSize(self.GetBestSize())
            self.SetMinSize(self.GetSize())
        else:
            self.settings3D.SetLabel(self.infoCollapseLabelExp)
            self.Layout()
            self.SetSize(self.minWindowSize)
            self.SetMinSize(self.minWindowSize)

        self.SendSizeEvent()

    def __DoLayout(self, panel):
        """Window layout"""
        frameSizer = wx.BoxSizer(wx.VERTICAL)
        gridSizer = wx.GridBagSizer(vgap=0, hgap=0)
        settings3DSizer = wx.BoxSizer(wx.VERTICAL)
        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)

        # north
        gridSizer.Add(self.MakeLabel(text=_("North"), parent=panel),
                      flag=wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL |
                      wx.TOP | wx.LEFT | wx.RIGHT, border=5, pos=(0, 2))
        gridSizer.Add(self.tnorth,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(1, 2))
        # west
        gridSizer.Add(self.MakeLabel(text=_("West"), parent=panel),
                      flag=wx.ALIGN_RIGHT |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.LEFT | wx.TOP | wx.BOTTOM, border=5, pos=(2, 0))
        gridSizer.Add(self.twest,
                      flag=wx.ALIGN_RIGHT |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(2, 1))

        gridSizer.Add(
            wx.StaticBitmap(
                panel, wx.ID_ANY, self.img, (-1, -1),
                (self.img.GetWidth(),
                 self.img.GetHeight())),
            flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
            pos=(2, 2))

        # east
        gridSizer.Add(self.teast,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(2, 3))
        gridSizer.Add(self.MakeLabel(text=_("East"), parent=panel),
                      flag=wx.ALIGN_LEFT |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.RIGHT | wx.TOP | wx.BOTTOM, border=5, pos=(2, 4))
        # south
        gridSizer.Add(self.tsouth,
                      flag=wx.ALIGN_CENTER_HORIZONTAL |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(3, 2))
        gridSizer.Add(self.MakeLabel(text=_("South"), parent=panel),
                      flag=wx.ALIGN_TOP | wx.ALIGN_CENTER_HORIZONTAL |
                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5, pos=(4, 2))
        # ns-res
        gridSizer.Add(self.MakeLabel(text=_("N-S resolution"), parent=panel),
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.TOP | wx.LEFT | wx.RIGHT, border=5, pos=(5, 1))
        gridSizer.Add(self.tnsres,
                      flag=wx.ALIGN_RIGHT |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(6, 1))
        # ew-res
        gridSizer.Add(self.MakeLabel(text=_("E-W resolution"), parent=panel),
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.TOP | wx.LEFT | wx.RIGHT, border=5, pos=(5, 3))
        gridSizer.Add(self.tewres,
                      flag=wx.ALIGN_RIGHT |
                      wx.ALIGN_CENTER_VERTICAL |
                      wx.ALL, border=5, pos=(6, 3))
        # rows/cols/cells
        gridSizer.Add(self.lrows,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.ALL, border=5, pos=(7, 1))

        gridSizer.Add(self.lcells,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.ALL, border=5, pos=(7, 2))

        gridSizer.Add(self.lcols,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
                      wx.ALL, border=5, pos=(7, 3))

        # 3D
        settings3DSizer.Add(self.settings3D,
                            flag=wx.ALL,
                            border=5)

        # buttons
        buttonSizer.Add(self.bcancel, proportion=1,
                        flag=wx.ALIGN_RIGHT |
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL, border=10)
        buttonSizer.Add(self.bset, proportion=1,
                        flag=wx.ALIGN_CENTER |
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL, border=10)

        frameSizer.Add(gridSizer, proportion=1,
                       flag=wx.ALL | wx.ALIGN_CENTER, border=5)
        frameSizer.Add(settings3DSizer, proportion=0,
                       flag=wx.ALL | wx.ALIGN_CENTER, border=5)
        frameSizer.Add(buttonSizer, proportion=0,
                       flag=wx.ALL | wx.ALIGN_RIGHT, border=5)

        self.SetAutoLayout(True)
        panel.SetSizer(frameSizer)
        frameSizer.Fit(panel)
        self.Layout()

    def OnValue(self, event):
        """Set given value"""
        try:
            if event.GetId() == self.tnorth.GetId():
                self.north = float(event.GetString())
            elif event.GetId() == self.tsouth.GetId():
                self.south = float(event.GetString())
            elif event.GetId() == self.teast.GetId():
                self.east = float(event.GetString())
            elif event.GetId() == self.twest.GetId():
                self.west = float(event.GetString())
            elif event.GetId() == self.tnsres.GetId():
                self.nsres = float(event.GetString())
            elif event.GetId() == self.tewres.GetId():
                self.ewres = float(event.GetString())
            elif event.GetId() == self.ttop.GetId():
                self.top = float(event.GetString())
            elif event.GetId() == self.tbottom.GetId():
                self.bottom = float(event.GetString())
            #             elif event.GetId() == self.tnsres3.GetId():
            #                 self.nsres3 = float(event.GetString())
            #             elif event.GetId() == self.tewres3.GetId():
            #                 self.ewres3 = float(event.GetString())
            elif event.GetId() == self.ttbres.GetId():
                self.tbres = float(event.GetString())

            self.__UpdateInfo()

        except ValueError as e:
            if len(event.GetString()) > 0 and event.GetString() != '-':
                dlg = wx.MessageBox(parent=self,
                                    message=_("Invalid value: %s") % e,
                                    caption=_("Error"),
                                    style=wx.OK | wx.ICON_ERROR)
                # reset values
                self.tnorth.SetValue(str(self.north))
                self.tsouth.SetValue(str(self.south))
                self.teast.SetValue(str(self.east))
                self.twest.SetValue(str(self.west))
                self.tnsres.SetValue(str(self.nsres))
                self.tewres.SetValue(str(self.ewres))
                self.ttop.SetValue(str(self.top))
                self.tbottom.SetValue(str(self.bottom))
                self.ttbres.SetValue(str(self.tbres))
                # self.tnsres3.SetValue(str(self.nsres3))
                # self.tewres3.SetValue(str(self.ewres3))

        event.Skip()

    def __UpdateInfo(self):
        """Update number of rows/cols/cells"""
        try:
            rows = int((self.north - self.south) / self.nsres)
            cols = int((self.east - self.west) / self.ewres)
        except ZeroDivisionError:
            return
        self.rows = rows
        self.cols = cols
        self.cells = self.rows * self.cols

        try:
            depth = int((self.top - self.bottom) / self.tbres)
        except ZeroDivisionError:
            return
        self.depth = depth
        self.cells3 = self.rows * self.cols * self.depth

        # 2D
        self.lrows.SetLabel(_("Rows: %d") % self.rows)
        self.lcols.SetLabel(_("Cols: %d") % self.cols)
        self.lcells.SetLabel(_("Cells: %d") % self.cells)
        # 3D
        self.ldepth.SetLabel(_("Depth: %d" % self.depth))
        self.lcells3.SetLabel(_("3D Cells: %d" % self.cells3))

    def OnSetButton(self, event=None):
        """Set default region"""
        ret = RunCommand('g.region',
                         flags='sa',
                         n=self.north,
                         s=self.south,
                         e=self.east,
                         w=self.west,
                         nsres=self.nsres,
                         ewres=self.ewres,
                         t=self.top,
                         b=self.bottom,
                         tbres=self.tbres)
        if ret == 0:
            self.Destroy()

    def OnCancel(self, event):
        self.Destroy()
Ejemplo n.º 3
0
class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
    def __init__(self,
                 parent,
                 title,
                 vectorName,
                 query=None,
                 cats=None,
                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
                 **kwargs):
        """Dialog used to display/modify categories of vector objects

        :param parent:
        :param title: dialog title
        :param query: {coordinates, qdist} - used by v.edit/v.what
        :param cats: directory of lines (layer/categories) - used by vdigit
        :param style: dialog style
        """
        self.parent = parent  # map window class instance
        self.digit = parent.digit

        # map name
        self.vectorName = vectorName

        # line : {layer: [categories]}
        self.cats = {}

        # do not display dialog if no line is found (-> self.cats)
        if cats is None:
            if self._getCategories(query[0], query[1]) == 0 or not self.line:
                Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
        else:
            self.cats = cats
            for line in cats.keys():
                for layer in cats[line].keys():
                    self.cats[line][layer] = list(cats[line][layer])

            layers = []
            for layer in self.digit.GetLayers():
                layers.append(str(layer))

        # make copy of cats (used for 'reload')
        self.cats_orig = copy.deepcopy(self.cats)

        wx.Dialog.__init__(self,
                           parent=self.parent,
                           id=wx.ID_ANY,
                           title=title,
                           style=style,
                           **kwargs)

        # list of categories
        box = StaticBox(parent=self,
                        id=wx.ID_ANY,
                        label=" %s " %
                        _("List of categories - right-click to delete"))
        listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        self.list = CategoryListCtrl(parent=self,
                                     id=wx.ID_ANY,
                                     style=wx.LC_REPORT | wx.BORDER_NONE
                                     | wx.LC_SORT_ASCENDING | wx.LC_HRULES
                                     | wx.LC_VRULES)
        # sorter
        self.fid = list(self.cats.keys())[0]
        self.itemDataMap = self.list.Populate(self.cats[self.fid])
        listmix.ColumnSorterMixin.__init__(self, 2)
        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY, size=(150, -1))
        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
        self.fidText = StaticText(parent=self, id=wx.ID_ANY)
        if len(self.cats.keys()) == 1:
            self.fidMulti.Show(False)
            self.fidText.SetLabel(str(self.fid))
        else:
            self.fidText.Show(False)
            choices = []
            for fid in self.cats.keys():
                choices.append(str(fid))
            self.fidMulti.SetItems(choices)
            self.fidMulti.SetSelection(0)

        listSizer.Add(self.list, proportion=1, flag=wx.EXPAND)

        # add new category
        box = StaticBox(parent=self,
                        id=wx.ID_ANY,
                        label=" %s " % _("Add new category"))
        addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        flexSizer = wx.FlexGridSizer(cols=5, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(3)

        layerNewTxt = StaticText(parent=self,
                                 id=wx.ID_ANY,
                                 label="%s:" % _("Layer"))
        self.layerNew = wx.Choice(parent=self,
                                  id=wx.ID_ANY,
                                  size=(75, -1),
                                  choices=layers)
        if len(layers) > 0:
            self.layerNew.SetSelection(0)

        catNewTxt = StaticText(parent=self,
                               id=wx.ID_ANY,
                               label="%s:" % _("Category"))

        try:
            newCat = max(self.cats[self.fid][1]) + 1
        except KeyError:
            newCat = 1
        self.catNew = SpinCtrl(parent=self,
                               id=wx.ID_ANY,
                               size=(75, -1),
                               initial=newCat,
                               min=0,
                               max=1e9)
        btnAddCat = Button(self, wx.ID_ADD)
        flexSizer.Add(layerNewTxt,
                      proportion=0,
                      flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.layerNew,
                      proportion=0,
                      flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(catNewTxt,
                      proportion=0,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
                      border=10)
        flexSizer.Add(self.catNew,
                      proportion=0,
                      flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(btnAddCat,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
        addSizer.Add(flexSizer,
                     proportion=1,
                     flag=wx.ALL | wx.EXPAND,
                     border=5)

        # buttons
        btnApply = Button(self, wx.ID_APPLY)
        btnApply.SetToolTip(_("Apply changes"))
        btnCancel = Button(self, wx.ID_CANCEL)
        btnCancel.SetToolTip(_("Ignore changes and close dialog"))
        btnOk = Button(self, wx.ID_OK)
        btnOk.SetToolTip(_("Apply changes and close dialog"))
        btnOk.SetDefault()

        # sizers
        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(btnCancel)
        # btnSizer.AddButton(btnReload)
        # btnSizer.SetNegativeButton(btnReload)
        btnSizer.AddButton(btnApply)
        btnSizer.AddButton(btnOk)
        btnSizer.Realize()

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(listSizer,
                      proportion=1,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
                      border=5)
        mainSizer.Add(addSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT
                      | wx.BOTTOM,
                      border=5)
        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
        fidSizer.Add(StaticText(parent=self,
                                id=wx.ID_ANY,
                                label=_("Feature id:")),
                     proportion=0,
                     border=5,
                     flag=wx.ALIGN_CENTER_VERTICAL)
        fidSizer.Add(self.fidMulti,
                     proportion=0,
                     flag=wx.EXPAND | wx.ALL,
                     border=5)
        fidSizer.Add(self.fidText,
                     proportion=0,
                     flag=wx.EXPAND | wx.ALL,
                     border=5)
        mainSizer.Add(fidSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALL,
                      border=5)
        mainSizer.Add(btnSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
                      border=5)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)
        self.SetAutoLayout(True)

        # set min size for dialog
        self.SetMinSize(self.GetBestSize())

        # bindings
        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
        btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
        btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
        self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide())

        # list
        self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp)  # wxMSW
        self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)  # wxGTK
        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)

    def GetListCtrl(self):
        """Used by ColumnSorterMixin
        """
        return self.list

    def OnColClick(self, event):
        """Click on column header (order by)
        """
        event.Skip()

    def OnBeginEdit(self, event):
        """Editing of item started
        """
        event.Allow()

    def OnEndEdit(self, event):
        """Finish editing of item
        """
        itemIndex = event.GetIndex()
        layerOld = int(self.list.GetItem(itemIndex, 0).GetText())
        catOld = int(self.list.GetItem(itemIndex, 1).GetText())

        if event.GetColumn() == 0:
            layerNew = int(event.GetLabel())
            catNew = catOld
        else:
            layerNew = layerOld
            catNew = int(event.GetLabel())

        try:
            if layerNew not in self.cats[self.fid].keys():
                self.cats[self.fid][layerNew] = []
            self.cats[self.fid][layerNew].append(catNew)
            self.cats[self.fid][layerOld].remove(catOld)
        except:
            event.Veto()
            self.list.SetStringItem(itemIndex, 0, str(layerNew))
            self.list.SetStringItem(itemIndex, 1, str(catNew))
            dlg = wx.MessageDialog(
                self,
                _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
                  "Layer and category number must be integer.\n"
                  "Layer number must be greater than zero.") % {
                      'layer': self.layerNew.GetStringSelection(),
                      'category': str(self.catNew.GetValue())
                  }, _("Error"), wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            return False

    def OnRightDown(self, event):
        """Mouse right button down
        """
        x = event.GetX()
        y = event.GetY()
        item, flags = self.list.HitTest((x, y))

        if item !=  wx.NOT_FOUND and \
                flags & wx.LIST_HITTEST_ONITEM:
            self.list.Select(item)

        event.Skip()

    def OnRightUp(self, event):
        """Mouse right button up
        """
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.popupID2 = wx.NewId()
            self.popupID3 = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnItemDelete, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id=self.popupID2)
            self.Bind(wx.EVT_MENU, self.OnReload, id=self.popupID3)

        # generate popup-menu
        menu = Menu()
        menu.Append(self.popupID1, _("Delete selected"))
        if self.list.GetFirstSelected() == -1:
            menu.Enable(self.popupID1, False)

        menu.Append(self.popupID2, _("Delete all"))
        menu.AppendSeparator()
        menu.Append(self.popupID3, _("Reload"))

        self.PopupMenu(menu)
        menu.Destroy()

    def OnItemSelected(self, event):
        """Item selected
        """
        event.Skip()

    def OnItemDelete(self, event):
        """Delete selected item(s) from the list (layer/category pair)
        """
        item = self.list.GetFirstSelected()
        while item != -1:
            layer = int(self.list.GetItem(item, 0).GetText())
            cat = int(self.list.GetItem(item, 1).GetText())
            self.list.DeleteItem(item)
            self.cats[self.fid][layer].remove(cat)

            item = self.list.GetFirstSelected()

        event.Skip()

    def OnItemDeleteAll(self, event):
        """Delete all items from the list
        """
        self.list.DeleteAllItems()
        self.cats[self.fid] = {}

        event.Skip()

    def OnFeature(self, event):
        """Feature id changed (on duplicates)
        """
        self.fid = int(event.GetString())

        self.itemDataMap = self.list.Populate(self.cats[self.fid], update=True)

        try:
            newCat = max(self.cats[self.fid][1]) + 1
        except KeyError:
            newCat = 1

        self.catNew.SetValue(newCat)

        event.Skip()

    def _getCategories(self, coords, qdist):
        """Get layer/category pairs for all available
        layers

        :return: True line found or False if not found
        """
        ret = RunCommand('v.what',
                         parent=self,
                         quiet=True,
                         map=self.vectorName,
                         east_north='%f,%f' %
                         (float(coords[0]), float(coords[1])),
                         distance=qdist)

        if not ret:
            return False

        for item in ret.splitlines():
            litem = item.lower()
            if "id:" in litem:  # get line id
                self.line = int(item.split(':')[1].strip())
            elif "layer:" in litem:  # add layer
                layer = int(item.split(':')[1].strip())
                if layer not in self.cats.keys():
                    self.cats[layer] = []
            elif "category:" in litem:  # add category
                self.cats[layer].append(int(item.split(':')[1].strip()))

        return True

    def OnReload(self, event):
        """Reload button pressed
        """
        # restore original list
        self.cats = copy.deepcopy(self.cats_orig)

        # polulate list
        self.itemDataMap = self.list.Populate(self.cats[self.fid], update=True)

        event.Skip()

    def OnCancel(self, event):
        """Cancel button pressed
        """
        self.parent.parent.dialogs['category'] = None
        if self.digit:
            self.digit.GetDisplay().SetSelected([])
            self.parent.UpdateMap(render=False)
        else:
            self.parent.parent.OnRender(None)

        self.Close()

    def OnApply(self, event):
        """Apply button pressed
        """
        for fid in self.cats.keys():
            newfid = self.ApplyChanges(fid)
            if fid == self.fid and newfid > 0:
                self.fid = newfid

    def ApplyChanges(self, fid):
        """Apply changes

        :param fid: feature id
        """
        cats = self.cats[fid]
        cats_orig = self.cats_orig[fid]

        # action : (catsFrom, catsTo)
        check = {'catadd': (cats, cats_orig), 'catdel': (cats_orig, cats)}

        newfid = -1

        # add/delete new category
        for action, catsCurr in six.iteritems(check):
            for layer in catsCurr[0].keys():
                catList = []
                for cat in catsCurr[0][layer]:
                    if layer not in catsCurr[1].keys() or \
                            cat not in catsCurr[1][layer]:
                        catList.append(cat)
                if catList != []:
                    if action == 'catadd':
                        add = True
                    else:
                        add = False

                    newfid = self.digit.SetLineCats(fid, layer, catList, add)
                    if len(self.cats.keys()) == 1:
                        self.fidText.SetLabel("%d" % newfid)
                    else:
                        choices = self.fidMulti.GetItems()
                        choices[choices.index(str(fid))] = str(newfid)
                        self.fidMulti.SetItems(choices)
                        self.fidMulti.SetStringSelection(str(newfid))

                    self.cats[newfid] = self.cats[fid]
                    del self.cats[fid]

                    fid = newfid
                    if self.fid < 0:
                        wx.MessageBox(
                            parent=self,
                            message=_("Unable to update vector map."),
                            caption=_("Error"),
                            style=wx.OK | wx.ICON_ERROR)

        self.cats_orig[fid] = copy.deepcopy(cats)

        return newfid

    def OnOK(self, event):
        """OK button pressed
        """
        self.OnApply(event)
        self.OnCancel(event)

    def OnAddCat(self, event):
        """Button 'Add' new category pressed
        """
        try:
            layer = int(self.layerNew.GetStringSelection())
            cat = int(self.catNew.GetValue())
            if layer <= 0:
                raise ValueError
        except ValueError:
            GError(
                parent=self,
                message=
                _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
                  "Layer and category number must be integer.\n"
                  "Layer number must be greater than zero.") % {
                      'layer': str(self.layerNew.GetValue()),
                      'category': str(self.catNew.GetValue())
                  })
            return False

        if layer not in self.cats[self.fid].keys():
            self.cats[self.fid][layer] = []

        self.cats[self.fid][layer].append(cat)

        # reload list
        self.itemDataMap = self.list.Populate(self.cats[self.fid], update=True)

        # update category number for add
        self.catNew.SetValue(cat + 1)

        event.Skip()

        return True

    def GetLine(self):
        """Get id of selected line of 'None' if no line is selected
        """
        return self.cats.keys()

    def UpdateDialog(self, query=None, cats=None):
        """Update dialog

        :param query: {coordinates, distance} - v.what
        :param cats:  directory layer/cats    - vdigit
        :return: True if updated otherwise False
        """
        # line: {layer: [categories]}
        self.cats = {}
        # do not display dialog if no line is found (-> self.cats)
        if cats is None:
            ret = self._getCategories(query[0], query[1])
        else:
            self.cats = cats
            for line in cats.keys():
                for layer in cats[line].keys():
                    self.cats[line][layer] = list(cats[line][layer])
            ret = 1
        if ret == 0 or len(self.cats.keys()) < 1:
            Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
            return False

        # make copy of cats (used for 'reload')
        self.cats_orig = copy.deepcopy(self.cats)

        # polulate list
        self.fid = list(self.cats.keys())[0]
        self.itemDataMap = self.list.Populate(self.cats[self.fid], update=True)

        try:
            newCat = max(self.cats[self.fid][1]) + 1
        except KeyError:
            newCat = 1
        self.catNew.SetValue(newCat)

        if len(self.cats.keys()) == 1:
            self.fidText.Show(True)
            self.fidMulti.Show(False)
            self.fidText.SetLabel("%d" % self.fid)
        else:
            self.fidText.Show(False)
            self.fidMulti.Show(True)
            choices = []
            for fid in self.cats.keys():
                choices.append(str(fid))
            self.fidMulti.SetItems(choices)
            self.fidMulti.SetSelection(0)

        self.Layout()

        return True
Ejemplo n.º 4
0
class VDigitSettingsDialog(wx.Dialog):
    def __init__(
            self,
            parent,
            giface,
            title=_("Digitization settings"),
            style=wx.DEFAULT_DIALOG_STYLE,
    ):
        """Standard settings dialog for digitization purposes"""
        wx.Dialog.__init__(self,
                           parent=parent,
                           id=wx.ID_ANY,
                           title=title,
                           style=style)

        self._giface = giface
        self.parent = parent  # MapFrame
        self.digit = self.parent.MapWindow.digit

        # notebook
        notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
        self._createGeneralPage(notebook)
        self._createSymbologyPage(notebook)
        self.digit.SetCategory()
        self._createAttributesPage(notebook)
        self._createQueryPage(notebook)

        # buttons
        btnApply = Button(self, wx.ID_APPLY)
        btnCancel = Button(self, wx.ID_CLOSE)
        btnSave = Button(self, wx.ID_SAVE)
        btnSave.SetDefault()

        # bindigs
        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
        btnApply.SetToolTip(_("Apply changes for this session"))
        btnApply.SetDefault()
        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
        btnSave.SetToolTip(
            _("Close dialog and save changes to user settings file"))
        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
        btnCancel.SetToolTip(_("Close dialog and ignore changes"))

        # sizers
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.Add(btnCancel, proportion=0, flag=wx.ALL, border=5)
        btnSizer.Add(btnApply, proportion=0, flag=wx.ALL, border=5)
        btnSizer.Add(btnSave, proportion=0, flag=wx.ALL, border=5)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(notebook,
                      proportion=1,
                      flag=wx.EXPAND | wx.ALL,
                      border=5)
        mainSizer.Add(btnSizer, proportion=0, flag=wx.ALIGN_RIGHT, border=5)

        self.Bind(wx.EVT_CLOSE, self.OnCancel)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    def _createSymbologyPage(self, notebook):
        """Create notebook page concerning symbology settings"""
        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
        notebook.AddPage(page=panel, text=_("Symbology"))

        sizer = wx.BoxSizer(wx.VERTICAL)

        flexSizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)

        self.symbology = {}
        for label, key in self._symbologyData():
            textLabel = StaticText(panel, wx.ID_ANY, label)
            color = csel.ColourSelect(
                panel,
                id=wx.ID_ANY,
                colour=UserSettings.Get(group="vdigit",
                                        key="symbol",
                                        subkey=[key, "color"]),
                size=(40, 25),
            )
            isEnabled = UserSettings.Get(group="vdigit",
                                         key="symbol",
                                         subkey=[key, "enabled"])
            if isEnabled is not None:
                enabled = CheckBox(panel, id=wx.ID_ANY, label="")
                enabled.SetValue(isEnabled)
                self.symbology[key] = (enabled, color)
            else:
                enabled = (1, 1)
                self.symbology[key] = (None, color)

            flexSizer.Add(textLabel,
                          proportion=0,
                          flag=wx.ALIGN_CENTER_VERTICAL)
            flexSizer.Add(enabled,
                          proportion=0,
                          flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
            flexSizer.Add(color,
                          proportion=0,
                          flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
            color.SetName("GetColour")

        sizer.Add(flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=10)

        panel.SetSizer(sizer)

        return panel

    def _createGeneralPage(self, notebook):
        """Create notebook page concerning general settings"""
        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
        notebook.AddPage(page=panel, text=_("General"))

        border = wx.BoxSizer(wx.VERTICAL)

        #
        # display section
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Display"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        flexSizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)
        # line width
        text = StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width"))
        self.lineWidthValue = SpinCtrl(
            parent=panel,
            id=wx.ID_ANY,
            size=(75, -1),
            initial=UserSettings.Get(group="vdigit",
                                     key="lineWidth",
                                     subkey="value"),
            min=1,
            max=1e6,
        )
        units = StaticText(
            parent=panel,
            id=wx.ID_ANY,
            size=(115, -1),
            label=UserSettings.Get(group="vdigit",
                                   key="lineWidth",
                                   subkey="units"),
            style=wx.ALIGN_LEFT,
        )
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.lineWidthValue,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(
            units,
            proportion=0,
            flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL
            | wx.LEFT,
            border=10,
        )

        sizer.Add(flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
        border.Add(sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)

        #
        # snapping section
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Snapping"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        flexSizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)

        # snapping
        text = StaticText(parent=panel,
                          id=wx.ID_ANY,
                          label=_("Snapping threshold"))
        self.snappingValue = FloatSpin(
            parent=panel,
            id=wx.ID_ANY,
            size=(75, -1),
            value=UserSettings.Get(group="vdigit",
                                   key="snapping",
                                   subkey="value"),
            min_val=-1,
            max_val=1e6,
            digits=7,
        )
        self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
        self.snappingValue.Bind(wx.EVT_TEXT, self.OnChangeSnappingValue)
        self.snappingUnit = wx.Choice(
            parent=panel,
            id=wx.ID_ANY,
            size=(125, -1),
            choices=[_("screen pixels"), _("map units")],
        )
        try:
            self.snappingUnit.SetSelection(
                UserSettings.Get(group="vdigit", key="snapping",
                                 subkey="unit"))
        except:
            self.snappingUnit.SetSelection(0)
        self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits)
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.snappingValue,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(self.snappingUnit,
                      proportion=0,
                      flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)

        vertexSizer = wx.BoxSizer(wx.VERTICAL)
        self.snapVertex = CheckBox(parent=panel,
                                   id=wx.ID_ANY,
                                   label=_("Snap also to vertex"))
        self.snapVertex.SetValue(
            UserSettings.Get(group="vdigit",
                             key="snapToVertex",
                             subkey="enabled"))
        vertexSizer.Add(self.snapVertex, proportion=0, flag=wx.EXPAND)
        self.mapUnits = self.parent.MapWindow.Map.GetProjInfo()["units"]
        self.snappingInfo = StaticText(
            parent=panel,
            id=wx.ID_ANY,
            label=_("Snapping threshold is %(value).1f %(units)s") % {
                "value": self.digit.GetDisplay().GetThreshold(),
                "units": self.mapUnits
            },
        )
        vertexSizer.Add(self.snappingInfo,
                        proportion=0,
                        flag=wx.ALL | wx.EXPAND,
                        border=1)

        sizer.Add(flexSizer, proportion=1, flag=wx.EXPAND)
        sizer.Add(vertexSizer, proportion=1, flag=wx.EXPAND)
        border.Add(
            sizer,
            proportion=0,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5,
        )

        #
        # select box
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Select vector features"))
        # feature type
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        inSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.selectFeature = {}
        for feature in ("point", "line", "centroid", "boundary"):
            chkbox = CheckBox(parent=panel, label=feature)
            self.selectFeature[feature] = chkbox.GetId()
            chkbox.SetValue(
                UserSettings.Get(group="vdigit",
                                 key="selectType",
                                 subkey=[feature, "enabled"]))
            inSizer.Add(chkbox,
                        proportion=0,
                        flag=wx.EXPAND | wx.ALL,
                        border=5)
        sizer.Add(inSizer, proportion=0, flag=wx.EXPAND)
        # threshold
        flexSizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)
        text = StaticText(parent=panel,
                          id=wx.ID_ANY,
                          label=_("Select threshold"))
        self.selectThreshValue = SpinCtrl(
            parent=panel,
            id=wx.ID_ANY,
            size=(75, -1),
            initial=UserSettings.Get(group="vdigit",
                                     key="selectThresh",
                                     subkey="value"),
            min=1,
            max=1e6,
        )
        units = StaticText(
            parent=panel,
            id=wx.ID_ANY,
            size=(115, -1),
            label=UserSettings.Get(group="vdigit",
                                   key="lineWidth",
                                   subkey="units"),
            style=wx.ALIGN_LEFT,
        )
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(
            self.selectThreshValue,
            proportion=0,
            flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE,
        )
        flexSizer.Add(
            units,
            proportion=0,
            flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL
            | wx.LEFT,
            border=10,
        )

        self.selectIn = CheckBox(
            parent=panel,
            id=wx.ID_ANY,
            label=_("Select only features inside of selection bounding box"),
        )
        self.selectIn.SetValue(
            UserSettings.Get(group="vdigit",
                             key="selectInside",
                             subkey="enabled"))
        self.selectIn.SetToolTip(
            _("By default are selected all features overlapping selection bounding box "
              ))

        self.checkForDupl = CheckBox(parent=panel,
                                     id=wx.ID_ANY,
                                     label=_("Check for duplicates"))
        self.checkForDupl.SetValue(
            UserSettings.Get(group="vdigit",
                             key="checkForDupl",
                             subkey="enabled"))

        sizer.Add(flexSizer, proportion=0, flag=wx.EXPAND)
        sizer.Add(self.selectIn,
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=1)
        sizer.Add(self.checkForDupl,
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=1)
        border.Add(
            sizer,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        #
        # digitize lines box
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Digitize lines/boundaries"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        self.intersect = CheckBox(parent=panel,
                                  label=_("Break lines at intersection"))
        self.intersect.SetValue(
            UserSettings.Get(group="vdigit",
                             key="breakLines",
                             subkey="enabled"))

        sizer.Add(self.intersect,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)

        border.Add(
            sizer,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        #
        # digitize areas box
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Digitize areas"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        self.closeBoundary = CheckBox(
            parent=panel, label=_("Close boundary (snap to the start node)"))
        self.closeBoundary.SetValue(
            UserSettings.Get(group="vdigit",
                             key="closeBoundary",
                             subkey="enabled"))

        sizer.Add(self.closeBoundary,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)

        border.Add(
            sizer,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        #
        # save-on-exit box
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Save changes"))
        # save changes on exit?
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        self.save = CheckBox(parent=panel, label=_("Save changes on exit"))
        self.save.SetValue(
            UserSettings.Get(group="vdigit",
                             key="saveOnExit",
                             subkey="enabled"))
        sizer.Add(self.save, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
        border.Add(
            sizer,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        panel.SetSizer(border)

        return panel

    def _createQueryPage(self, notebook):
        """Create notebook page for query tool"""
        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
        notebook.AddPage(page=panel, text=_("Query tool"))

        border = wx.BoxSizer(wx.VERTICAL)

        #
        # query tool box
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Choose query tool"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        LocUnits = self.parent.MapWindow.Map.GetProjInfo()["units"]

        self.queryBox = CheckBox(parent=panel,
                                 id=wx.ID_ANY,
                                 label=_("Select by box"))
        self.queryBox.SetValue(
            UserSettings.Get(group="vdigit", key="query", subkey="box"))

        sizer.Add(self.queryBox,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)
        sizer.Add((0, 5))

        #
        # length
        #
        self.queryLength = wx.RadioButton(parent=panel,
                                          id=wx.ID_ANY,
                                          label=_("length"))
        self.queryLength.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
        sizer.Add(self.queryLength,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)
        flexSizer = wx.FlexGridSizer(cols=4, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)
        txt = StaticText(parent=panel, id=wx.ID_ANY, label=_("Select lines"))
        self.queryLengthSL = wx.Choice(
            parent=panel,
            id=wx.ID_ANY,
            choices=[_("shorter than"), _("longer than")])
        self.queryLengthSL.SetSelection(
            UserSettings.Get(group="vdigit",
                             key="queryLength",
                             subkey="than-selection"))
        self.queryLengthValue = FloatSpin(
            parent=panel,
            id=wx.ID_ANY,
            size=(100, -1),
            value=1,
            min_val=0,
            max_val=1e6,
            digits=7,
        )
        self.queryLengthValue.SetValue(
            UserSettings.Get(group="vdigit",
                             key="queryLength",
                             subkey="thresh"))
        units = StaticText(parent=panel, id=wx.ID_ANY, label="%s" % LocUnits)
        flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.queryLengthSL,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(self.queryLengthValue,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(units, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        sizer.Add(flexSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)

        #
        # dangle
        #
        self.queryDangle = wx.RadioButton(parent=panel,
                                          id=wx.ID_ANY,
                                          label=_("dangle"))
        self.queryDangle.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
        sizer.Add(self.queryDangle,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)
        flexSizer = wx.FlexGridSizer(cols=4, hgap=5, vgap=5)
        flexSizer.AddGrowableCol(0)
        txt = StaticText(parent=panel, id=wx.ID_ANY, label=_("Select dangles"))
        self.queryDangleSL = wx.Choice(
            parent=panel,
            id=wx.ID_ANY,
            choices=[_("shorter than"), _("longer than")])
        self.queryDangleSL.SetSelection(
            UserSettings.Get(group="vdigit",
                             key="queryDangle",
                             subkey="than-selection"))
        self.queryDangleValue = SpinCtrl(parent=panel,
                                         id=wx.ID_ANY,
                                         size=(100, -1),
                                         initial=1,
                                         min=0,
                                         max=1e6)
        self.queryDangleValue.SetValue(
            UserSettings.Get(group="vdigit",
                             key="queryDangle",
                             subkey="thresh"))
        units = StaticText(parent=panel, id=wx.ID_ANY, label="%s" % LocUnits)
        flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.queryDangleSL,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(self.queryDangleValue,
                      proportion=0,
                      flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
        flexSizer.Add(units, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        sizer.Add(flexSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)

        if UserSettings.Get(group="vdigit", key="query",
                            subkey="selection") == 0:
            self.queryLength.SetValue(True)
        else:
            self.queryDangle.SetValue(True)

        # enable & disable items
        self.OnChangeQuery(None)

        border.Add(sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)

        panel.SetSizer(border)

        return panel

    def _createAttributesPage(self, notebook):
        """Create notebook page for attributes"""
        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
        notebook.AddPage(page=panel, text=_("Attributes"))

        border = wx.BoxSizer(wx.VERTICAL)

        #
        # add new record
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Digitize new feature"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        # checkbox
        self.addRecord = CheckBox(parent=panel,
                                  id=wx.ID_ANY,
                                  label=_("Add new record into table"))
        self.addRecord.SetValue(
            UserSettings.Get(group="vdigit", key="addRecord",
                             subkey="enabled"))
        sizer.Add(self.addRecord,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)
        # settings
        flexSizer = wx.FlexGridSizer(cols=2, hgap=3, vgap=3)
        flexSizer.AddGrowableCol(0)
        settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"),
                                                          _("Next to use")))
        # layer
        text = StaticText(parent=panel, id=wx.ID_ANY, label=_("Layer"))
        self.layer = SpinCtrl(parent=panel,
                              id=wx.ID_ANY,
                              size=(125, -1),
                              min=1,
                              max=1e3)
        self.layer.SetValue(
            int(UserSettings.Get(group="vdigit", key="layer", subkey="value")))
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(self.layer,
                      proportion=0,
                      flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
        # category number
        text = StaticText(parent=panel,
                          id=wx.ID_ANY,
                          label=_("Category number"))
        self.category = SpinCtrl(
            parent=panel,
            id=wx.ID_ANY,
            size=(125, -1),
            initial=UserSettings.Get(group="vdigit",
                                     key="category",
                                     subkey="value"),
            min=-1e9,
            max=1e9,
        )
        if (UserSettings.Get(
                group="vdigit", key="categoryMode", subkey="selection") != 1):
            self.category.Enable(False)
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(
            self.category,
            proportion=0,
            flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL,
        )
        # category mode
        text = StaticText(parent=panel, id=wx.ID_ANY, label=_("Category mode"))
        self.categoryMode = wx.Choice(
            parent=panel,
            id=wx.ID_ANY,
            size=(125, -1),
            choices=[_("Next to use"),
                     _("Manual entry"),
                     _("No category")],
        )
        self.categoryMode.SetSelection(
            UserSettings.Get(group="vdigit",
                             key="categoryMode",
                             subkey="selection"))
        flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
        flexSizer.Add(
            self.categoryMode,
            proportion=0,
            flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL,
        )

        sizer.Add(flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
        border.Add(sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)

        #
        # delete existing record
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Delete existing feature(s)"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        # checkbox
        self.deleteRecord = CheckBox(parent=panel,
                                     id=wx.ID_ANY,
                                     label=_("Delete record from table"))
        self.deleteRecord.SetValue(
            UserSettings.Get(group="vdigit", key="delRecord",
                             subkey="enabled"))
        sizer.Add(self.deleteRecord,
                  proportion=0,
                  flag=wx.ALL | wx.EXPAND,
                  border=1)
        border.Add(
            sizer,
            proportion=0,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5,
        )

        #
        # geometry attributes (currently only length and area are supported)
        #
        box = StaticBox(parent=panel,
                        id=wx.ID_ANY,
                        label=" %s " % _("Geometry attributes"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        gridSizer = wx.GridBagSizer(hgap=3, vgap=3)
        self.geomAttrb = {
            "length": {
                "label": _("length")
            },
            "area": {
                "label": _("area")
            },
            "perimeter": {
                "label": _("perimeter")
            },
        }

        digitToolbar = self.parent.toolbars["vdigit"]
        try:
            vectorName = digitToolbar.GetLayer().GetName()
        except AttributeError:
            vectorName = None  # no vector selected for editing
        layer = UserSettings.Get(group="vdigit", key="layer", subkey="value")
        mapLayer = self.parent.toolbars["vdigit"].GetLayer()
        tree = self.parent.tree
        if tree:
            item = tree.FindItemByData("maplayer", mapLayer)
        else:
            item = None
        row = 0
        for attrb in ["length", "area", "perimeter"]:
            # checkbox
            check = CheckBox(parent=panel,
                             id=wx.ID_ANY,
                             label=self.geomAttrb[attrb]["label"])
            # self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
            check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
            # column (only numeric)
            column = ColumnSelect(parent=panel, size=(200, -1))
            column.InsertColumns(
                vector=vectorName,
                layer=layer,
                excludeKey=True,
                type=["integer", "double precision"],
            )
            # units
            if attrb == "area":
                choices = Units.GetUnitsList("area")
            else:
                choices = Units.GetUnitsList("length")
            win_units = wx.Choice(parent=panel,
                                  id=wx.ID_ANY,
                                  choices=choices,
                                  size=(120, -1))

            # default values
            check.SetValue(False)
            if (item and tree.GetLayerInfo(item, key="vdigit")
                    and "geomAttr" in tree.GetLayerInfo(item, key="vdigit")
                    and attrb in tree.GetLayerInfo(item,
                                                   key="vdigit")["geomAttr"]):
                check.SetValue(True)
                column.SetStringSelection(
                    tree.GetLayerInfo(
                        item, key="vdigit")["geomAttr"][attrb]["column"])
                if attrb == "area":
                    type = "area"
                else:
                    type = "length"
                unitsIdx = Units.GetUnitsIndex(
                    type,
                    tree.GetLayerInfo(
                        item, key="vdigit")["geomAttr"][attrb]["units"],
                )
                win_units.SetSelection(unitsIdx)

            if not vectorName:
                check.Enable(False)
                column.Enable(False)

            if not check.IsChecked():
                column.Enable(False)

            self.geomAttrb[attrb]["check"] = check.GetId()
            self.geomAttrb[attrb]["column"] = column.GetId()
            self.geomAttrb[attrb]["units"] = win_units.GetId()

            gridSizer.Add(check, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
            gridSizer.Add(column, pos=(row, 1))
            gridSizer.Add(win_units, pos=(row, 2))
            row += 1

        note = "\n".join(
            textwrap.wrap(
                _("Note: These settings are stored "
                  "in the workspace not in the vector digitizer "
                  "preferences."),
                55,
            ))
        gridSizer.Add(StaticText(parent=panel, id=wx.ID_ANY, label=note),
                      pos=(3, 0),
                      span=(1, 3))

        gridSizer.AddGrowableCol(0)
        sizer.Add(gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
        border.Add(
            sizer,
            proportion=0,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5,
        )

        # bindings
        self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
        self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
        self.Bind(wx.EVT_SPINCTRL, self.OnChangeLayer, self.layer)

        panel.SetSizer(border)

        return panel

    def _symbologyData(self):
        """Data for _createSymbologyPage()

        label | checkbox | color
        """

        return (
            (_("Digitize new line segment"), "newSegment"),
            (_("Digitize new line/boundary"), "newLine"),
            (_("Highlight"), "highlight"),
            (_("Highlight (duplicates)"), "highlightDupl"),
            (_("Point"), "point"),
            (_("Line"), "line"),
            (_("Boundary (no area)"), "boundaryNo"),
            (_("Boundary (one area)"), "boundaryOne"),
            (_("Boundary (two areas)"), "boundaryTwo"),
            (_("Centroid (in area)"), "centroidIn"),
            (_("Centroid (outside area)"), "centroidOut"),
            (_("Centroid (duplicate in area)"), "centroidDup"),
            (_("Node (one line)"), "nodeOne"),
            (_("Node (two lines)"), "nodeTwo"),
            (_("Vertex"), "vertex"),
            (_("Area (closed boundary + centroid)"), "area"),
            (_("Direction"), "direction"),
        )

    def OnGeomAttrb(self, event):
        """Register geometry attributes (enable/disable)"""
        checked = event.IsChecked()
        id = event.GetId()
        key = None
        for attrb, val in six.iteritems(self.geomAttrb):
            if val["check"] == id:
                key = attrb
                break

        column = self.FindWindowById(self.geomAttrb[key]["column"])
        if checked:
            column.Enable()
        else:
            column.Enable(False)

    def OnChangeCategoryMode(self, event):
        """Change category mode"""
        mode = event.GetSelection()
        UserSettings.Set(group="vdigit",
                         key="categoryMode",
                         subkey="selection",
                         value=mode)
        if mode == 1:  # manual entry
            self.category.Enable(True)
        elif self.category.IsEnabled():  # disable
            self.category.Enable(False)

        if mode == 2 and self.addRecord.IsChecked():  # no category
            self.addRecord.SetValue(False)

        self.digit.SetCategory()
        self.category.SetValue(
            UserSettings.Get(group="vdigit", key="category", subkey="value"))

    def OnChangeLayer(self, event):
        """Layer changed"""
        layer = event.GetInt()
        if layer > 0:
            UserSettings.Set(group="vdigit",
                             key="layer",
                             subkey="value",
                             value=layer)
            self.digit.SetCategory()
            self.category.SetValue(
                UserSettings.Get(group="vdigit",
                                 key="category",
                                 subkey="value"))

        event.Skip()

    def OnChangeAddRecord(self, event):
        """Checkbox 'Add new record' status changed"""
        pass
        # self.category.SetValue(self.digit.SetCategory())

    def OnChangeSnappingValue(self, event):
        """Change snapping value - update static text"""
        value = self.snappingValue.GetValue()

        if value < 0:
            region = self.parent.MapWindow.Map.GetRegion()
            res = (region["nsres"] + region["ewres"]) / 2.0
            threshold = self.digit.GetDisplay().GetThreshold(value=res)
        else:
            if self.snappingUnit.GetSelection() == 1:  # map units
                threshold = value
            else:
                threshold = self.digit.GetDisplay().GetThreshold(value=value)

        if value == 0:
            self.snappingInfo.SetLabel(_("Snapping disabled"))
        elif value < 0:
            self.snappingInfo.SetLabel(
                _("Snapping threshold is %(value).1f %(units)s "
                  "(based on comp. resolution)") % {
                      "value": threshold,
                      "units": self.mapUnits.lower()
                  })
        else:
            self.snappingInfo.SetLabel(
                _("Snapping threshold is %(value).1f %(units)s") % {
                    "value": threshold,
                    "units": self.mapUnits.lower()
                })

        event.Skip()

    def OnChangeSnappingUnits(self, event):
        """Snapping units change -> update static text"""
        value = self.snappingValue.GetValue()
        units = self.snappingUnit.GetSelection()
        threshold = self.digit.GetDisplay().GetThreshold(value=value,
                                                         units=units)

        if units == 1:  # map units
            self.snappingInfo.SetLabel(
                _("Snapping threshold is %(value).1f %(units)s") % {
                    "value": value,
                    "units": self.mapUnits
                })
        else:
            self.snappingInfo.SetLabel(
                _("Snapping threshold is %(value).1f %(units)s") % {
                    "value": threshold,
                    "units": self.mapUnits
                })

        event.Skip()

    def OnChangeQuery(self, event):
        """Change query"""
        if self.queryLength.GetValue():
            # length
            self.queryLengthSL.Enable(True)
            self.queryLengthValue.Enable(True)
            self.queryDangleSL.Enable(False)
            self.queryDangleValue.Enable(False)
        else:
            # dangle
            self.queryLengthSL.Enable(False)
            self.queryLengthValue.Enable(False)
            self.queryDangleSL.Enable(True)
            self.queryDangleValue.Enable(True)

    def OnSave(self, event):
        """Button 'Save' pressed"""
        self.UpdateSettings()
        self.parent.toolbars["vdigit"].settingsDialog = None

        fileSettings = {}
        UserSettings.ReadSettingsFile(settings=fileSettings)
        fileSettings["vdigit"] = UserSettings.Get(group="vdigit")

        sfile = UserSettings.SaveToFile(fileSettings)
        self._giface.WriteLog(
            _("Vector digitizer settings saved to file <%s>.") % sfile)

        self.Destroy()

        event.Skip()

    def OnApply(self, event):
        """Button 'Apply' pressed"""
        self.UpdateSettings()

    def OnCancel(self, event):
        """Button 'Cancel' pressed"""
        self.parent.toolbars["vdigit"].settingsDialog = None
        self.Destroy()

        if event:
            event.Skip()

    def UpdateSettings(self):
        """Update digitizer settings

        .. todo::
            Needs refactoring
        """
        self._giface.workspaceChanged.emit()
        # symbology
        for key, (enabled, color) in six.iteritems(self.symbology):
            if enabled:
                UserSettings.Set(
                    group="vdigit",
                    key="symbol",
                    subkey=[key, "enabled"],
                    value=enabled.IsChecked(),
                )
                UserSettings.Set(
                    group="vdigit",
                    key="symbol",
                    subkey=[key, "color"],
                    value=tuple(color.GetColour()),
                )
            else:
                UserSettings.Set(
                    group="vdigit",
                    key="symbol",
                    subkey=[key, "color"],
                    value=tuple(color.GetColour()),
                )
        # display
        UserSettings.Set(
            group="vdigit",
            key="lineWidth",
            subkey="value",
            value=int(self.lineWidthValue.GetValue()),
        )

        # snapping
        UserSettings.Set(
            group="vdigit",
            key="snapping",
            subkey="value",
            value=self.snappingValue.GetValue(),
        )
        UserSettings.Set(
            group="vdigit",
            key="snapping",
            subkey="unit",
            value=self.snappingUnit.GetSelection(),
        )
        UserSettings.Set(
            group="vdigit",
            key="snapToVertex",
            subkey="enabled",
            value=self.snapVertex.IsChecked(),
        )

        # digitize new feature
        UserSettings.Set(
            group="vdigit",
            key="addRecord",
            subkey="enabled",
            value=self.addRecord.IsChecked(),
        )
        UserSettings.Set(
            group="vdigit",
            key="layer",
            subkey="value",
            value=int(self.layer.GetValue()),
        )
        UserSettings.Set(
            group="vdigit",
            key="category",
            subkey="value",
            value=int(self.category.GetValue()),
        )
        UserSettings.Set(
            group="vdigit",
            key="categoryMode",
            subkey="selection",
            value=self.categoryMode.GetSelection(),
        )

        # delete existing feature
        UserSettings.Set(
            group="vdigit",
            key="delRecord",
            subkey="enabled",
            value=self.deleteRecord.IsChecked(),
        )

        # geometry attributes (workspace)
        mapLayer = self.parent.toolbars["vdigit"].GetLayer()
        tree = self._giface.GetLayerTree()
        if tree:
            item = tree.FindItemByData("maplayer", mapLayer)
        else:
            item = None
        for key, val in six.iteritems(self.geomAttrb):
            checked = self.FindWindowById(val["check"]).IsChecked()
            column = self.FindWindowById(val["column"]).GetValue()
            unitsIdx = self.FindWindowById(val["units"]).GetSelection()
            if item and not tree.GetLayerInfo(item, key="vdigit"):
                tree.SetLayerInfo(item,
                                  key="vdigit",
                                  value={"geomAttr": dict()})

            if checked:  # enable
                if key == "area":
                    type = key
                else:
                    type = "length"
                unitsKey = Units.GetUnitsKey(type, unitsIdx)
                tree.GetLayerInfo(item, key="vdigit")["geomAttr"][key] = {
                    "column": column,
                    "units": unitsKey,
                }
            else:
                if (item and tree.GetLayerInfo(item, key="vdigit") and key
                        in tree.GetLayerInfo(item, key="vdigit")["geomAttr"]):
                    del tree.GetLayerInfo(item, key="vdigit")["geomAttr"][key]

        # query tool
        if self.queryLength.GetValue():
            UserSettings.Set(group="vdigit",
                             key="query",
                             subkey="selection",
                             value=0)
        else:
            UserSettings.Set(group="vdigit",
                             key="query",
                             subkey="type",
                             value=1)
        UserSettings.Set(group="vdigit",
                         key="query",
                         subkey="box",
                         value=self.queryBox.IsChecked())
        UserSettings.Set(
            group="vdigit",
            key="queryLength",
            subkey="than-selection",
            value=self.queryLengthSL.GetSelection(),
        )
        UserSettings.Set(
            group="vdigit",
            key="queryLength",
            subkey="thresh",
            value=self.queryLengthValue.GetValue(),
        )
        UserSettings.Set(
            group="vdigit",
            key="queryDangle",
            subkey="than-selection",
            value=self.queryDangleSL.GetSelection(),
        )
        UserSettings.Set(
            group="vdigit",
            key="queryDangle",
            subkey="thresh",
            value=int(self.queryDangleValue.GetValue()),
        )

        # select features
        for feature in ("point", "line", "centroid", "boundary"):
            UserSettings.Set(
                group="vdigit",
                key="selectType",
                subkey=[feature, "enabled"],
                value=self.FindWindowById(
                    self.selectFeature[feature]).IsChecked(),
            )
        UserSettings.Set(
            group="vdigit",
            key="selectThresh",
            subkey="value",
            value=int(self.selectThreshValue.GetValue()),
        )
        UserSettings.Set(
            group="vdigit",
            key="checkForDupl",
            subkey="enabled",
            value=self.checkForDupl.IsChecked(),
        )
        UserSettings.Set(
            group="vdigit",
            key="selectInside",
            subkey="enabled",
            value=self.selectIn.IsChecked(),
        )

        # on-exit
        UserSettings.Set(
            group="vdigit",
            key="saveOnExit",
            subkey="enabled",
            value=self.save.IsChecked(),
        )

        # break lines
        UserSettings.Set(
            group="vdigit",
            key="breakLines",
            subkey="enabled",
            value=self.intersect.IsChecked(),
        )

        # close boundary
        UserSettings.Set(
            group="vdigit",
            key="closeBoundary",
            subkey="enabled",
            value=self.closeBoundary.IsChecked(),
        )

        self.digit.UpdateSettings()

        # redraw map if auto-rendering is enabled
        if self.parent.IsAutoRendered():
            self.parent.OnRender(None)
Ejemplo n.º 5
0
class GRASSStartup(wx.Frame):
    exit_success = 0
    # 2 is file not found from python interpreter
    exit_user_requested = 5
    """GRASS start-up screen"""
    def __init__(self,
                 parent=None,
                 id=wx.ID_ANY,
                 style=wx.DEFAULT_FRAME_STYLE):

        #
        # GRASS variables
        #
        self.gisbase = os.getenv("GISBASE")
        self.grassrc = sgui.read_gisrc()
        self.gisdbase = self.GetRCValue("GISDBASE")

        #
        # list of locations/mapsets
        #
        self.listOfLocations = []
        self.listOfMapsets = []
        self.listOfMapsetsSelectable = []

        wx.Frame.__init__(self, parent=parent, id=id, style=style)

        self.locale = wx.Locale(language=wx.LANGUAGE_DEFAULT)

        # scroll panel was used here but not properly and is probably not need
        # as long as it is not high too much
        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)

        # i18N

        #
        # graphical elements
        #
        # image
        try:
            if os.getenv('ISISROOT'):
                name = os.path.join(globalvar.GUIDIR, "images",
                                    "startup_banner_isis.png")
            else:
                name = os.path.join(globalvar.GUIDIR, "images",
                                    "startup_banner.png")
            self.hbitmap = wx.StaticBitmap(
                self.panel, wx.ID_ANY,
                wx.Bitmap(name=name, type=wx.BITMAP_TYPE_PNG))
        except:
            self.hbitmap = wx.StaticBitmap(
                self.panel, wx.ID_ANY, BitmapFromImage(wx.EmptyImage(530,
                                                                     150)))

        # labels
        # crashes when LOCATION doesn't exist
        # get version & revision
        grassVersion, grassRevisionStr = sgui.GetVersion()

        self.gisdbase_box = StaticBox(
            parent=self.panel,
            id=wx.ID_ANY,
            label=" %s " % _("1. Select GRASS GIS database directory"))
        self.location_box = StaticBox(parent=self.panel,
                                      id=wx.ID_ANY,
                                      label=" %s " %
                                      _("2. Select GRASS Location"))
        self.mapset_box = StaticBox(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label=" %s " % _("3. Select GRASS Mapset"))

        self.lmessage = StaticText(parent=self.panel)
        # It is not clear if all wx versions supports color, so try-except.
        # The color itself may not be correct for all platforms/system settings
        # but in http://xoomer.virgilio.it/infinity77/wxPython/Widgets/wx.SystemSettings.html
        # there is no 'warning' color.
        try:
            self.lmessage.SetForegroundColour(wx.Colour(255, 0, 0))
        except AttributeError:
            pass

        self.gisdbase_panel = wx.Panel(parent=self.panel)
        self.location_panel = wx.Panel(parent=self.panel)
        self.mapset_panel = wx.Panel(parent=self.panel)

        self.ldbase = StaticText(
            parent=self.gisdbase_panel,
            id=wx.ID_ANY,
            label=_("GRASS GIS database directory contains Locations."))

        self.llocation = StaticWrapText(
            parent=self.location_panel,
            id=wx.ID_ANY,
            label=_("All data in one Location is in the same "
                    " coordinate reference system (projection)."
                    " One Location can be one project."
                    " Location contains Mapsets."),
            style=wx.ALIGN_LEFT)

        self.lmapset = StaticWrapText(
            parent=self.mapset_panel,
            id=wx.ID_ANY,
            label=_("Mapset contains GIS data related"
                    " to one project, task within one project,"
                    " subregion or user."),
            style=wx.ALIGN_LEFT)

        try:
            for label in [self.ldbase, self.llocation, self.lmapset]:
                label.SetForegroundColour(
                    wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
        except AttributeError:
            # for explanation of try-except see above
            pass

        # buttons
        self.bstart = Button(parent=self.panel,
                             id=wx.ID_ANY,
                             label=_("Start &GRASS session"))
        self.bstart.SetDefault()
        self.bexit = Button(parent=self.panel, id=wx.ID_EXIT)
        self.bstart.SetMinSize((180, self.bexit.GetSize()[1]))
        self.bhelp = Button(parent=self.panel, id=wx.ID_HELP)
        self.bbrowse = Button(parent=self.gisdbase_panel,
                              id=wx.ID_ANY,
                              label=_("&Browse"))
        self.bmapset = Button(
            parent=self.mapset_panel,
            id=wx.ID_ANY,
            # GTC New mapset
            label=_("&New"))
        self.bmapset.SetToolTip(_("Create a new Mapset in selected Location"))
        self.bwizard = Button(
            parent=self.location_panel,
            id=wx.ID_ANY,
            # GTC New location
            label=_("N&ew"))
        self.bwizard.SetToolTip(
            _("Create a new location using location wizard."
              " After location is created successfully,"
              " GRASS session is started."))
        self.rename_location_button = Button(
            parent=self.location_panel,
            id=wx.ID_ANY,
            # GTC Rename location
            label=_("Ren&ame"))
        self.rename_location_button.SetToolTip(_("Rename selected location"))
        self.delete_location_button = Button(
            parent=self.location_panel,
            id=wx.ID_ANY,
            # GTC Delete location
            label=_("De&lete"))
        self.delete_location_button.SetToolTip(_("Delete selected location"))
        self.download_location_button = Button(parent=self.location_panel,
                                               id=wx.ID_ANY,
                                               label=_("Do&wnload"))
        self.download_location_button.SetToolTip(_("Download sample location"))

        self.rename_mapset_button = Button(
            parent=self.mapset_panel,
            id=wx.ID_ANY,
            # GTC Rename mapset
            label=_("&Rename"))
        self.rename_mapset_button.SetToolTip(_("Rename selected mapset"))
        self.delete_mapset_button = Button(
            parent=self.mapset_panel,
            id=wx.ID_ANY,
            # GTC Delete mapset
            label=_("&Delete"))
        self.delete_mapset_button.SetToolTip(_("Delete selected mapset"))

        # textinputs
        self.tgisdbase = TextCtrl(parent=self.gisdbase_panel,
                                  id=wx.ID_ANY,
                                  value="",
                                  size=(300, -1),
                                  style=wx.TE_PROCESS_ENTER)

        # Locations
        self.lblocations = GListBox(parent=self.location_panel,
                                    id=wx.ID_ANY,
                                    size=(180, 200),
                                    choices=self.listOfLocations)
        self.lblocations.SetColumnWidth(0, 180)

        # TODO: sort; but keep PERMANENT on top of list
        # Mapsets
        self.lbmapsets = GListBox(parent=self.mapset_panel,
                                  id=wx.ID_ANY,
                                  size=(180, 200),
                                  choices=self.listOfMapsets)
        self.lbmapsets.SetColumnWidth(0, 180)

        # layout & properties, first do layout so everything is created
        self._do_layout()
        self._set_properties(grassVersion, grassRevisionStr)

        # events
        self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
        self.bstart.Bind(wx.EVT_BUTTON, self.OnStart)
        self.bexit.Bind(wx.EVT_BUTTON, self.OnExit)
        self.bhelp.Bind(wx.EVT_BUTTON, self.OnHelp)
        self.bmapset.Bind(wx.EVT_BUTTON, self.OnCreateMapset)
        self.bwizard.Bind(wx.EVT_BUTTON, self.OnWizard)

        self.rename_location_button.Bind(wx.EVT_BUTTON, self.RenameLocation)
        self.delete_location_button.Bind(wx.EVT_BUTTON, self.DeleteLocation)
        self.download_location_button.Bind(wx.EVT_BUTTON,
                                           self.DownloadLocation)
        self.rename_mapset_button.Bind(wx.EVT_BUTTON, self.RenameMapset)
        self.delete_mapset_button.Bind(wx.EVT_BUTTON, self.DeleteMapset)

        self.lblocations.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectLocation)
        self.lbmapsets.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectMapset)
        self.lbmapsets.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnStart)
        self.tgisdbase.Bind(wx.EVT_TEXT_ENTER, self.OnSetDatabase)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def _set_properties(self, version, revision):
        """Set frame properties

        :param version: Version in the form of X.Y.Z
        :param revision: Version control revision with leading space

        *revision* should be an empty string in case of release and
        otherwise it needs a leading space to be separated from the rest
        of the title.
        """
        self.SetTitle(_("GRASS GIS %s Startup%s") % (version, revision))
        self.SetIcon(
            wx.Icon(os.path.join(globalvar.ICONDIR, "grass.ico"),
                    wx.BITMAP_TYPE_ICO))

        self.bstart.SetForegroundColour(wx.Colour(35, 142, 35))
        self.bstart.SetToolTip(_("Enter GRASS session"))
        self.bstart.Enable(False)
        self.bmapset.Enable(False)
        # this all was originally a choice, perhaps just mapset needed
        self.rename_location_button.Enable(False)
        self.delete_location_button.Enable(False)
        self.rename_mapset_button.Enable(False)
        self.delete_mapset_button.Enable(False)

        # set database
        if not self.gisdbase:
            # sets an initial path for gisdbase if nothing in GISRC
            if os.path.isdir(os.getenv("HOME")):
                self.gisdbase = os.getenv("HOME")
            else:
                self.gisdbase = os.getcwd()
        try:
            self.tgisdbase.SetValue(self.gisdbase)
        except UnicodeDecodeError:
            wx.MessageBox(parent=self,
                          caption=_("Error"),
                          message=_("Unable to set GRASS database. "
                                    "Check your locale settings."),
                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)

        self.OnSetDatabase(None)
        location = self.GetRCValue("LOCATION_NAME")
        if location == "<UNKNOWN>" or location is None:
            return
        if not os.path.isdir(os.path.join(self.gisdbase, location)):
            location = None

        # list of locations
        self.UpdateLocations(self.gisdbase)
        try:
            self.lblocations.SetSelection(self.listOfLocations.index(location),
                                          force=True)
            self.lblocations.EnsureVisible(
                self.listOfLocations.index(location))
        except ValueError:
            sys.stderr.write(
                _("ERROR: Location <%s> not found\n") %
                self.GetRCValue("LOCATION_NAME"))
            if len(self.listOfLocations) > 0:
                self.lblocations.SetSelection(0, force=True)
                self.lblocations.EnsureVisible(0)
                location = self.listOfLocations[0]
            else:
                return

        # list of mapsets
        self.UpdateMapsets(os.path.join(self.gisdbase, location))
        mapset = self.GetRCValue("MAPSET")
        if mapset:
            try:
                self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset),
                                            force=True)
                self.lbmapsets.EnsureVisible(self.listOfMapsets.index(mapset))
            except ValueError:
                sys.stderr.write(_("ERROR: Mapset <%s> not found\n") % mapset)
                self.lbmapsets.SetSelection(0, force=True)
                self.lbmapsets.EnsureVisible(0)

    def _do_layout(self):
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer = sizer  # for the layout call after changing message
        dbase_sizer = wx.BoxSizer(wx.HORIZONTAL)

        location_mapset_sizer = wx.BoxSizer(wx.HORIZONTAL)

        gisdbase_panel_sizer = wx.BoxSizer(wx.VERTICAL)
        gisdbase_boxsizer = wx.StaticBoxSizer(self.gisdbase_box, wx.VERTICAL)

        btns_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.gisdbase_panel.SetSizer(gisdbase_panel_sizer)

        # gis data directory

        gisdbase_boxsizer.Add(self.gisdbase_panel,
                              proportion=1,
                              flag=wx.EXPAND | wx.ALL,
                              border=1)

        gisdbase_panel_sizer.Add(dbase_sizer,
                                 proportion=1,
                                 flag=wx.EXPAND | wx.ALL,
                                 border=1)
        gisdbase_panel_sizer.Add(self.ldbase,
                                 proportion=0,
                                 flag=wx.EXPAND | wx.ALL,
                                 border=1)

        dbase_sizer.Add(self.tgisdbase,
                        proportion=1,
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                        border=1)
        dbase_sizer.Add(self.bbrowse,
                        proportion=0,
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                        border=1)

        gisdbase_panel_sizer.Fit(self.gisdbase_panel)

        # location and mapset lists

        def layout_list_box(box, panel, list_box, buttons, description):
            panel_sizer = wx.BoxSizer(wx.VERTICAL)
            main_sizer = wx.BoxSizer(wx.HORIZONTAL)
            box_sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
            buttons_sizer = wx.BoxSizer(wx.VERTICAL)

            panel.SetSizer(panel_sizer)
            panel_sizer.Fit(panel)

            main_sizer.Add(list_box,
                           proportion=1,
                           flag=wx.EXPAND | wx.ALL,
                           border=1)
            main_sizer.Add(buttons_sizer,
                           proportion=0,
                           flag=wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                           border=1)
            for button in buttons:
                buttons_sizer.Add(button,
                                  proportion=0,
                                  flag=wx.EXPAND | wx.LEFT | wx.RIGHT
                                  | wx.BOTTOM,
                                  border=3)
            box_sizer.Add(panel,
                          proportion=1,
                          flag=wx.EXPAND | wx.ALL,
                          border=1)
            panel_sizer.Add(main_sizer,
                            proportion=1,
                            flag=wx.EXPAND | wx.ALL,
                            border=1)
            panel_sizer.Add(description,
                            proportion=0,
                            flag=wx.EXPAND | wx.ALL,
                            border=1)
            return box_sizer

        location_boxsizer = layout_list_box(box=self.location_box,
                                            panel=self.location_panel,
                                            list_box=self.lblocations,
                                            buttons=[
                                                self.bwizard,
                                                self.rename_location_button,
                                                self.delete_location_button,
                                                self.download_location_button
                                            ],
                                            description=self.llocation)
        mapset_boxsizer = layout_list_box(box=self.mapset_box,
                                          panel=self.mapset_panel,
                                          list_box=self.lbmapsets,
                                          buttons=[
                                              self.bmapset,
                                              self.rename_mapset_button,
                                              self.delete_mapset_button
                                          ],
                                          description=self.lmapset)

        # location and mapset sizer
        location_mapset_sizer.Add(location_boxsizer,
                                  proportion=1,
                                  flag=wx.LEFT | wx.RIGHT | wx.EXPAND,
                                  border=3)
        location_mapset_sizer.Add(mapset_boxsizer,
                                  proportion=1,
                                  flag=wx.RIGHT | wx.EXPAND,
                                  border=3)

        # buttons
        btns_sizer.Add(self.bstart,
                       proportion=0,
                       flag=wx.ALIGN_CENTER_HORIZONTAL
                       | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                       border=5)
        btns_sizer.Add(self.bexit,
                       proportion=0,
                       flag=wx.ALIGN_CENTER_HORIZONTAL
                       | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                       border=5)
        btns_sizer.Add(self.bhelp,
                       proportion=0,
                       flag=wx.ALIGN_CENTER_HORIZONTAL
                       | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                       border=5)

        # main sizer
        sizer.Add(self.hbitmap,
                  proportion=0,
                  flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL
                  | wx.ALL,
                  border=3)  # image
        sizer.Add(gisdbase_boxsizer,
                  proportion=0,
                  flag=wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT | wx.LEFT | wx.TOP
                  | wx.EXPAND,
                  border=3)  # GISDBASE setting

        # warning/error message
        sizer.Add(self.lmessage,
                  proportion=0,
                  flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.ALL
                  | wx.EXPAND,
                  border=5)
        sizer.Add(location_mapset_sizer,
                  proportion=1,
                  flag=wx.RIGHT | wx.LEFT | wx.EXPAND,
                  border=1)
        sizer.Add(btns_sizer,
                  proportion=0,
                  flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL
                  | wx.RIGHT | wx.LEFT,
                  border=3)

        self.panel.SetAutoLayout(True)
        self.panel.SetSizer(sizer)
        sizer.Fit(self.panel)
        sizer.SetSizeHints(self)
        self.Layout()

    def _showWarning(self, text):
        """Displays a warning, hint or info message to the user.

        This function can be used for all kinds of messages except for
        error messages.

        .. note::
            There is no cleaning procedure. You should call _hideMessage when
            you know that there is everything correct now.
        """
        self.lmessage.SetLabel(text)
        self.lmessage.Wrap(self.GetClientSize()[0])
        self.sizer.Layout()

    def _showError(self, text):
        """Displays a error message to the user.

        This function should be used only when something serious and unexpected
        happens, otherwise _showWarning should be used.

        .. note::
            There is no cleaning procedure. You should call _hideMessage when
            you know that there is everything correct now.
        """
        self.lmessage.SetLabel(_("Error: {text}").format(text=text))
        self.lmessage.Wrap(self.GetClientSize()[0])
        self.sizer.Layout()

    def _hideMessage(self):
        """Clears/hides the error message."""
        # we do no hide widget
        # because we do not want the dialog to change the size
        self.lmessage.SetLabel("")
        self.sizer.Layout()

    def GetRCValue(self, value):
        """Return GRASS variable (read from GISRC)
        """
        if value in self.grassrc:
            return self.grassrc[value]
        else:
            return None

    def SuggestDatabase(self):
        """Suggest (set) possible GRASS Database value"""
        # only if nothing is set (<UNKNOWN> comes from init script)
        if self.GetRCValue("LOCATION_NAME") != "<UNKNOWN>":
            return
        path = get_possible_database_path()
        if path:
            try:
                self.tgisdbase.SetValue(path)
            except UnicodeDecodeError:
                # restore previous state
                # wizard gives error in this case, we just ignore
                path = None
                self.tgisdbase.SetValue(self.gisdbase)
            # if we still have path
            if path:
                self.gisdbase = path
                self.OnSetDatabase(None)
        else:
            # nothing found
            # TODO: should it be warning, hint or message?
            self._showWarning(
                _('GRASS needs a directory (GRASS database) '
                  'in which to store its data. '
                  'Create one now if you have not already done so. '
                  'A popular choice is "grassdata", located in '
                  'your home directory. '
                  'Press Browse button to select the directory.'))

    def OnWizard(self, event):
        """Location wizard started"""
        from location_wizard.wizard import LocationWizard
        gWizard = LocationWizard(parent=self,
                                 grassdatabase=self.tgisdbase.GetValue())
        if gWizard.location is not None:
            self.tgisdbase.SetValue(gWizard.grassdatabase)
            self.OnSetDatabase(None)
            self.UpdateMapsets(os.path.join(self.gisdbase, gWizard.location))
            self.lblocations.SetSelection(
                self.listOfLocations.index(gWizard.location))
            self.lbmapsets.SetSelection(0)
            self.SetLocation(self.gisdbase, gWizard.location, 'PERMANENT')
            if gWizard.georeffile:
                message = _(
                    "Do you want to import <%(name)s> to the newly created location?"
                ) % {
                    'name': gWizard.georeffile
                }
                dlg = wx.MessageDialog(parent=self,
                                       message=message,
                                       caption=_("Import data?"),
                                       style=wx.YES_NO | wx.YES_DEFAULT
                                       | wx.ICON_QUESTION)
                dlg.CenterOnParent()
                if dlg.ShowModal() == wx.ID_YES:
                    self.ImportFile(gWizard.georeffile)
                dlg.Destroy()
            if gWizard.default_region:
                defineRegion = RegionDef(self, location=gWizard.location)
                defineRegion.CenterOnParent()
                defineRegion.ShowModal()
                defineRegion.Destroy()

            if gWizard.user_mapset:
                self.OnCreateMapset(event)

    def ImportFile(self, filePath):
        """Tries to import file as vector or raster.

        If successfull sets default region from imported map.
        """
        RunCommand('db.connect', flags='c')
        mapName = os.path.splitext(os.path.basename(filePath))[0]
        vectors = RunCommand('v.in.ogr', input=filePath, flags='l', read=True)

        wx.BeginBusyCursor()
        wx.GetApp().Yield()
        if vectors:
            # vector detected
            returncode, error = RunCommand('v.in.ogr',
                                           input=filePath,
                                           output=mapName,
                                           flags='e',
                                           getErrorMsg=True)
        else:
            returncode, error = RunCommand('r.in.gdal',
                                           input=filePath,
                                           output=mapName,
                                           flags='e',
                                           getErrorMsg=True)
        wx.EndBusyCursor()

        if returncode != 0:
            GError(parent=self,
                   message=_("Import of <%(name)s> failed.\n"
                             "Reason: %(msg)s") % ({
                                 'name': filePath,
                                 'msg': error
                             }))
        else:
            GMessage(message=_(
                "Data file <%(name)s> imported successfully. "
                "The location's default region was set from this imported map."
            ) % {'name': filePath},
                     parent=self)

    # the event can be refactored out by using lambda in bind
    def RenameMapset(self, event):
        """Rename selected mapset
        """
        location = self.listOfLocations[self.lblocations.GetSelection()]
        mapset = self.listOfMapsets[self.lbmapsets.GetSelection()]
        if mapset == 'PERMANENT':
            GMessage(
                parent=self,
                message=_(
                    'Mapset <PERMANENT> is required for valid GRASS location.\n\n'
                    'This mapset cannot be renamed.'))
            return

        dlg = TextEntryDialog(
            parent=self,
            message=_('Current name: %s\n\nEnter new name:') % mapset,
            caption=_('Rename selected mapset'),
            validator=GenericValidator(grass.legal_name,
                                       self._nameValidationFailed))

        if dlg.ShowModal() == wx.ID_OK:
            newmapset = dlg.GetValue()
            if newmapset == mapset:
                dlg.Destroy()
                return

            if newmapset in self.listOfMapsets:
                wx.MessageBox(
                    parent=self,
                    caption=_('Message'),
                    message=_('Unable to rename mapset.\n\n'
                              'Mapset <%s> already exists in location.') %
                    newmapset,
                    style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
            else:
                try:
                    sutils.rename_mapset(self.gisdbase, location, mapset,
                                         newmapset)
                    self.OnSelectLocation(None)
                    self.lbmapsets.SetSelection(
                        self.listOfMapsets.index(newmapset))
                except Exception as e:
                    wx.MessageBox(parent=self,
                                  caption=_('Error'),
                                  message=_('Unable to rename mapset.\n\n%s') %
                                  e,
                                  style=wx.OK | wx.ICON_ERROR | wx.CENTRE)

        dlg.Destroy()

    def RenameLocation(self, event):
        """Rename selected location
        """
        location = self.listOfLocations[self.lblocations.GetSelection()]

        dlg = TextEntryDialog(
            parent=self,
            message=_('Current name: %s\n\nEnter new name:') % location,
            caption=_('Rename selected location'),
            validator=GenericValidator(grass.legal_name,
                                       self._nameValidationFailed))

        if dlg.ShowModal() == wx.ID_OK:
            newlocation = dlg.GetValue()
            if newlocation == location:
                dlg.Destroy()
                return

            if newlocation in self.listOfLocations:
                wx.MessageBox(
                    parent=self,
                    caption=_('Message'),
                    message=_(
                        'Unable to rename location.\n\n'
                        'Location <%s> already exists in GRASS database.') %
                    newlocation,
                    style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
            else:
                try:
                    sutils.rename_location(self.gisdbase, location,
                                           newlocation)
                    self.UpdateLocations(self.gisdbase)
                    self.lblocations.SetSelection(
                        self.listOfLocations.index(newlocation))
                    self.UpdateMapsets(newlocation)
                except Exception as e:
                    wx.MessageBox(
                        parent=self,
                        caption=_('Error'),
                        message=_('Unable to rename location.\n\n%s') % e,
                        style=wx.OK | wx.ICON_ERROR | wx.CENTRE)

        dlg.Destroy()

    def DeleteMapset(self, event):
        """Delete selected mapset
        """
        location = self.listOfLocations[self.lblocations.GetSelection()]
        mapset = self.listOfMapsets[self.lbmapsets.GetSelection()]
        if mapset == 'PERMANENT':
            GMessage(
                parent=self,
                message=_(
                    'Mapset <PERMANENT> is required for valid GRASS location.\n\n'
                    'This mapset cannot be deleted.'))
            return

        dlg = wx.MessageDialog(
            parent=self,
            message=_(
                "Do you want to continue with deleting mapset <%(mapset)s> "
                "from location <%(location)s>?\n\n"
                "ALL MAPS included in this mapset will be "
                "PERMANENTLY DELETED!") % {
                    'mapset': mapset,
                    'location': location
                },
            caption=_("Delete selected mapset"),
            style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

        if dlg.ShowModal() == wx.ID_YES:
            try:
                sutils.delete_mapset(self.gisdbase, location, mapset)
                self.OnSelectLocation(None)
                self.lbmapsets.SetSelection(0)
            except:
                wx.MessageBox(message=_('Unable to delete mapset'))

        dlg.Destroy()

    def DeleteLocation(self, event):
        """
        Delete selected location
        """

        location = self.listOfLocations[self.lblocations.GetSelection()]

        dlg = wx.MessageDialog(
            parent=self,
            message=_("Do you want to continue with deleting "
                      "location <%s>?\n\n"
                      "ALL MAPS included in this location will be "
                      "PERMANENTLY DELETED!") % (location),
            caption=_("Delete selected location"),
            style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

        if dlg.ShowModal() == wx.ID_YES:
            try:
                sutils.delete_location(self.gisdbase, location)
                self.UpdateLocations(self.gisdbase)
                self.lblocations.SetSelection(0)
                self.OnSelectLocation(None)
                self.lbmapsets.SetSelection(0)
            except:
                wx.MessageBox(message=_('Unable to delete location'))

        dlg.Destroy()

    def DownloadLocation(self, event):
        """Download location online"""
        from startup.locdownload import LocationDownloadDialog

        loc_download = LocationDownloadDialog(parent=self,
                                              database=self.gisdbase)
        loc_download.ShowModal()
        location = loc_download.GetLocation()
        if location:
            # get the new location to the list
            self.UpdateLocations(self.gisdbase)
            # seems to be used in similar context
            self.UpdateMapsets(os.path.join(self.gisdbase, location))
            self.lblocations.SetSelection(self.listOfLocations.index(location))
            # wizard does this as well, not sure if needed
            self.SetLocation(self.gisdbase, location, 'PERMANENT')
            # seems to be used in similar context
            self.OnSelectLocation(None)
        loc_download.Destroy()

    def UpdateLocations(self, dbase):
        """Update list of locations"""
        try:
            self.listOfLocations = GetListOfLocations(dbase)
        except (UnicodeEncodeError, UnicodeDecodeError) as e:
            GError(parent=self,
                   message=_(
                       "Unicode error detected. "
                       "Check your locale settings. Details: {0}").format(e),
                   showTraceback=False)

        self.lblocations.Clear()
        self.lblocations.InsertItems(self.listOfLocations, 0)

        if len(self.listOfLocations) > 0:
            self._hideMessage()
            self.lblocations.SetSelection(0)
        else:
            self.lblocations.SetSelection(wx.NOT_FOUND)
            self._showWarning(
                _("No GRASS Location found in '%s'."
                  " Create a new Location or choose different"
                  " GRASS database directory.") % self.gisdbase)

        return self.listOfLocations

    def UpdateMapsets(self, location):
        """Update list of mapsets"""
        self.FormerMapsetSelection = wx.NOT_FOUND  # for non-selectable item

        self.listOfMapsetsSelectable = list()
        self.listOfMapsets = GetListOfMapsets(self.gisdbase, location)

        self.lbmapsets.Clear()

        # disable mapset with denied permission
        locationName = os.path.basename(location)

        ret = RunCommand('g.mapset',
                         read=True,
                         flags='l',
                         location=locationName,
                         gisdbase=self.gisdbase)

        if ret:
            for line in ret.splitlines():
                self.listOfMapsetsSelectable += line.split(' ')
        else:
            self.SetLocation(self.gisdbase, locationName, "PERMANENT")
            # first run only
            self.listOfMapsetsSelectable = copy.copy(self.listOfMapsets)

        disabled = []
        idx = 0
        for mapset in self.listOfMapsets:
            if mapset not in self.listOfMapsetsSelectable or \
                    get_lockfile_if_present(self.gisdbase,
                                            locationName, mapset):
                disabled.append(idx)
            idx += 1

        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled=disabled)

        return self.listOfMapsets

    def OnSelectLocation(self, event):
        """Location selected"""
        if event:
            self.lblocations.SetSelection(event.GetIndex())

        if self.lblocations.GetSelection() != wx.NOT_FOUND:
            self.UpdateMapsets(
                os.path.join(
                    self.gisdbase,
                    self.listOfLocations[self.lblocations.GetSelection()]))
        else:
            self.listOfMapsets = []

        disabled = []
        idx = 0
        try:
            locationName = self.listOfLocations[
                self.lblocations.GetSelection()]
        except IndexError:
            locationName = ''

        for mapset in self.listOfMapsets:
            if mapset not in self.listOfMapsetsSelectable or \
                    get_lockfile_if_present(self.gisdbase,
                                            locationName, mapset):
                disabled.append(idx)
            idx += 1

        self.lbmapsets.Clear()
        self.lbmapsets.InsertItems(self.listOfMapsets, 0, disabled=disabled)

        if len(self.listOfMapsets) > 0:
            self.lbmapsets.SetSelection(0)
            if locationName:
                # enable start button when location and mapset is selected
                self.bstart.Enable()
                self.bstart.SetFocus()
                self.bmapset.Enable()
                # replacing disabled choice, perhaps just mapset needed
                self.rename_location_button.Enable()
                self.delete_location_button.Enable()
                self.rename_mapset_button.Enable()
                self.delete_mapset_button.Enable()
        else:
            self.lbmapsets.SetSelection(wx.NOT_FOUND)
            self.bstart.Enable(False)
            self.bmapset.Enable(False)
            # this all was originally a choice, perhaps just mapset needed
            self.rename_location_button.Enable(False)
            self.delete_location_button.Enable(False)
            self.rename_mapset_button.Enable(False)
            self.delete_mapset_button.Enable(False)

    def OnSelectMapset(self, event):
        """Mapset selected"""
        self.lbmapsets.SetSelection(event.GetIndex())

        if event.GetText() not in self.listOfMapsetsSelectable:
            self.lbmapsets.SetSelection(self.FormerMapsetSelection)
        else:
            self.FormerMapsetSelection = event.GetIndex()
            event.Skip()

    def OnSetDatabase(self, event):
        """Database set"""
        gisdbase = self.tgisdbase.GetValue()
        self._hideMessage()
        if not os.path.exists(gisdbase):
            self._showError(_("Path '%s' doesn't exist.") % gisdbase)
            return

        self.gisdbase = self.tgisdbase.GetValue()
        self.UpdateLocations(self.gisdbase)

        self.OnSelectLocation(None)

    def OnBrowse(self, event):
        """'Browse' button clicked"""
        if not event:
            defaultPath = os.getenv('HOME')
        else:
            defaultPath = ""

        dlg = wx.DirDialog(parent=self,
                           message=_("Choose GIS Data Directory"),
                           defaultPath=defaultPath,
                           style=wx.DD_DEFAULT_STYLE)

        if dlg.ShowModal() == wx.ID_OK:
            self.gisdbase = dlg.GetPath()
            self.tgisdbase.SetValue(self.gisdbase)
            self.OnSetDatabase(event)

        dlg.Destroy()

    def OnCreateMapset(self, event):
        """Create new mapset"""
        dlg = NewMapsetDialog(
            parent=self,
            default=self._getDefaultMapsetName(),
            validation_failed_handler=self._nameValidationFailed,
            help_hanlder=self.OnHelp,
        )
        if dlg.ShowModal() == wx.ID_OK:
            mapset = dlg.GetValue()
            return self.CreateNewMapset(mapset=mapset)
        else:
            return False

    def CreateNewMapset(self, mapset):
        if mapset in self.listOfMapsets:
            GMessage(parent=self,
                     message=_("Mapset <%s> already exists.") % mapset)
            return False

        if mapset.lower() == 'ogr':
            dlg1 = wx.MessageDialog(
                parent=self,
                message=_(
                    "Mapset <%s> is reserved for direct "
                    "read access to OGR layers. Please consider to use "
                    "another name for your mapset.\n\n"
                    "Are you really sure that you want to create this mapset?")
                % mapset,
                caption=_("Reserved mapset name"),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
            ret = dlg1.ShowModal()
            dlg1.Destroy()
            if ret == wx.ID_NO:
                dlg1.Destroy()
                return False

        try:
            self.gisdbase = self.tgisdbase.GetValue()
            location = self.listOfLocations[self.lblocations.GetSelection()]
            create_mapset(self.gisdbase, location, mapset)
            self.OnSelectLocation(None)
            self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
            self.bstart.SetFocus()

            return True
        except Exception as e:
            GError(parent=self,
                   message=_("Unable to create new mapset: %s") % e,
                   showTraceback=False)
            return False

    def OnStart(self, event):
        """'Start GRASS' button clicked"""
        dbase = self.tgisdbase.GetValue()
        location = self.listOfLocations[self.lblocations.GetSelection()]
        mapset = self.listOfMapsets[self.lbmapsets.GetSelection()]

        lockfile = get_lockfile_if_present(dbase, location, mapset)
        if lockfile:
            dlg = wx.MessageDialog(
                parent=self,
                message=_(
                    "GRASS is already running in selected mapset <%(mapset)s>\n"
                    "(file %(lock)s found).\n\n"
                    "Concurrent use not allowed.\n\n"
                    "Do you want to try to remove .gislock (note that you "
                    "need permission for this operation) and continue?") % {
                        'mapset': mapset,
                        'lock': lockfile
                    },
                caption=_("Lock file found"),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)

            ret = dlg.ShowModal()
            dlg.Destroy()
            if ret == wx.ID_YES:
                dlg1 = wx.MessageDialog(
                    parent=self,
                    message=
                    _("ARE YOU REALLY SURE?\n\n"
                      "If you really are running another GRASS session doing this "
                      "could corrupt your data. Have another look in the processor "
                      "manager just to be sure..."),
                    caption=_("Lock file found"),
                    style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION
                    | wx.CENTRE)

                ret = dlg1.ShowModal()
                dlg1.Destroy()

                if ret == wx.ID_YES:
                    try:
                        os.remove(lockfile)
                    except IOError as e:
                        GError(
                            _("Unable to remove '%(lock)s'.\n\n"
                              "Details: %(reason)s") % {
                                  'lock': lockfile,
                                  'reason': e
                              })
                else:
                    return
            else:
                return
        self.SetLocation(dbase, location, mapset)
        self.ExitSuccessfully()

    def SetLocation(self, dbase, location, mapset):
        SetSessionMapset(dbase, location, mapset)

    def _getDefaultMapsetName(self):
        """Returns default name for mapset."""
        try:
            defaultName = getpass.getuser()
            # raise error if not ascii (not valid mapset name)
            defaultName.encode('ascii')
        except:  # whatever might go wrong
            defaultName = 'user'

        return defaultName

    def ExitSuccessfully(self):
        self.Destroy()
        sys.exit(self.exit_success)

    def OnExit(self, event):
        """'Exit' button clicked"""
        self.Destroy()
        sys.exit(self.exit_user_requested)

    def OnHelp(self, event):
        """'Help' button clicked"""

        # help text in lib/init/helptext.html
        RunCommand('g.manual', entry='helptext')

    def OnCloseWindow(self, event):
        """Close window event"""
        event.Skip()
        sys.exit(self.exit_user_requested)

    def _nameValidationFailed(self, ctrl):
        message = _(
            "Name <%(name)s> is not a valid name for location or mapset. "
            "Please use only ASCII characters excluding %(chars)s "
            "and space.") % {
                'name': ctrl.GetValue(),
                'chars': '/"\'@,=*~'
            }
        GError(parent=self, message=message, caption=_("Invalid name"))
Ejemplo n.º 6
0
class DisplayAttributesDialog(wx.Dialog):
    def __init__(self,
                 parent,
                 map,
                 query=None,
                 cats=None,
                 line=None,
                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
                 pos=wx.DefaultPosition,
                 action="add",
                 ignoreError=False):
        """Standard dialog used to add/update/display attributes linked
        to the vector map.

        Attribute data can be selected based on layer and category number
        or coordinates.

        :param parent:
        :param map: vector map
        :param query: query coordinates and distance (used for v.edit)
        :param cats: {layer: cats}
        :param line: feature id (requested for cats)
        :param style:
        :param pos:
        :param action: (add, update, display)
        :param ignoreError: True to ignore errors
        """
        self.parent = parent  # mapdisplay.BufferedWindow
        self.map = map
        self.action = action

        # ids/cats of selected features
        # fid : {layer : cats}
        self.cats = {}
        self.fid = -1  # feature id

        # get layer/table/column information
        self.mapDBInfo = VectorDBInfo(self.map)

        layers = self.mapDBInfo.layers.keys()  # get available layers

        # check if db connection / layer exists
        if len(layers) <= 0:
            if not ignoreError:
                dlg = wx.MessageDialog(
                    parent=self.parent,
                    message=_("No attribute table found.\n\n"
                              "Do you want to create a new attribute table "
                              "and defined a link to vector map <%s>?") %
                    self.map,
                    caption=_("Create table?"),
                    style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
                if dlg.ShowModal() == wx.ID_YES:
                    lmgr = self.parent.lmgr
                    lmgr.OnShowAttributeTable(event=None, selection='layers')

                dlg.Destroy()

            self.mapDBInfo = None

        wx.Dialog.__init__(self,
                           parent=self.parent,
                           id=wx.ID_ANY,
                           title="",
                           style=style,
                           pos=pos)

        # dialog body
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        # notebook
        self.notebook = wx.Notebook(parent=self,
                                    id=wx.ID_ANY,
                                    style=wx.BK_DEFAULT)

        self.closeDialog = wx.CheckBox(parent=self,
                                       id=wx.ID_ANY,
                                       label=_("Close dialog on submit"))
        self.closeDialog.SetValue(True)
        if self.action == 'display':
            self.closeDialog.Enable(False)

        # feature id (text/choice for duplicates)
        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY, size=(150, -1))
        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
        self.fidText = StaticText(parent=self, id=wx.ID_ANY)

        self.noFoundMsg = StaticText(parent=self,
                                     id=wx.ID_ANY,
                                     label=_("No attributes found"))

        self.UpdateDialog(query=query, cats=cats)

        # set title
        if self.action == "update":
            self.SetTitle(_("Update attributes"))
        elif self.action == "add":
            self.SetTitle(_("Define attributes"))
        else:
            self.SetTitle(_("Display attributes"))

        # buttons
        btnCancel = Button(self, wx.ID_CANCEL)
        btnReset = Button(self, wx.ID_UNDO, _("&Reload"))
        btnSubmit = Button(self, wx.ID_OK, _("&Submit"))
        if self.action == 'display':
            btnSubmit.Enable(False)

        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(btnCancel)
        btnSizer.AddButton(btnReset)
        btnSizer.SetNegativeButton(btnReset)
        btnSubmit.SetDefault()
        btnSizer.AddButton(btnSubmit)
        btnSizer.Realize()

        mainSizer.Add(self.noFoundMsg,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALL,
                      border=5)
        mainSizer.Add(self.notebook,
                      proportion=1,
                      flag=wx.EXPAND | wx.ALL,
                      border=5)
        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
        fidSizer.Add(StaticText(parent=self,
                                id=wx.ID_ANY,
                                label=_("Feature id:")),
                     proportion=0,
                     border=5,
                     flag=wx.ALIGN_CENTER_VERTICAL)
        fidSizer.Add(self.fidMulti,
                     proportion=0,
                     flag=wx.EXPAND | wx.ALL,
                     border=5)
        fidSizer.Add(self.fidText,
                     proportion=0,
                     flag=wx.EXPAND | wx.ALL,
                     border=5)
        mainSizer.Add(fidSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                      border=5)
        mainSizer.Add(self.closeDialog,
                      proportion=0,
                      flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                      border=5)
        mainSizer.Add(btnSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
                      border=5)

        # bindigs
        btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
        btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
        btnCancel.Bind(wx.EVT_BUTTON, self.OnClose)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

        # set min size for dialog
        w, h = self.GetBestSize()
        w += 50
        if h < 200:
            self.SetMinSize((w, 200))
        else:
            self.SetMinSize((w, h))

        if self.notebook.GetPageCount() == 0:
            Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
            ### self.mapDBInfo = None

    def OnSQLStatement(self, event):
        """Update SQL statement"""
        pass

    def IsFound(self):
        """Check for status

        :return: True on attributes found
        :return: False attributes not found
        """
        return bool(self.mapDBInfo and self.notebook.GetPageCount() > 0)

    def GetSQLString(self, updateValues=False):
        """Create SQL statement string based on self.sqlStatement

        Show error message when invalid values are entered.

        If updateValues is True, update dataFrame according to values
        in textfields.
        """
        sqlCommands = []
        # find updated values for each layer/category
        for layer in self.mapDBInfo.layers.keys():  # for each layer
            table = self.mapDBInfo.GetTable(layer)
            key = self.mapDBInfo.GetKeyColumn(layer)
            columns = self.mapDBInfo.GetTableDesc(table)
            for idx in range(len(columns[key]['values'])):  # for each category
                updatedColumns = []
                updatedValues = []
                for name in columns.keys():
                    if name == key:
                        cat = columns[name]['values'][idx]
                        continue
                    ctype = columns[name]['ctype']
                    value = columns[name]['values'][idx]
                    id = columns[name]['ids'][idx]
                    try:
                        newvalue = self.FindWindowById(id).GetValue()
                    except:
                        newvalue = self.FindWindowById(id).GetLabel()

                    if newvalue:
                        try:
                            if ctype == int:
                                newvalue = int(newvalue)
                            elif ctype == float:
                                newvalue = float(newvalue)
                        except ValueError:
                            GError(
                                parent=self,
                                message=
                                _("Column <%(col)s>: Value '%(value)s' needs to be entered as %(type)s."
                                  ) % {
                                      'col': name,
                                      'value': str(newvalue),
                                      'type': columns[name]['type'].lower()
                                  },
                                showTraceback=False)
                            sqlCommands.append(None)
                            continue
                    else:
                        if self.action == 'add':
                            continue

                    if newvalue != value:
                        updatedColumns.append(name)
                        if newvalue == '':
                            updatedValues.append('NULL')
                        else:
                            if ctype != str:
                                updatedValues.append(str(newvalue))
                            else:
                                updatedValues.append(
                                    "'" + newvalue.replace("'", "''") + "'")
                        columns[name]['values'][idx] = newvalue

                if self.action != "add" and len(updatedValues) == 0:
                    continue

                if self.action == "add":
                    sqlString = "INSERT INTO %s (%s," % (table, key)
                else:
                    sqlString = "UPDATE %s SET " % table

                for idx in range(len(updatedColumns)):
                    name = updatedColumns[idx]
                    if self.action == "add":
                        sqlString += name + ","
                    else:
                        sqlString += name + "=" + updatedValues[idx] + ","

                sqlString = sqlString[:-1]  # remove last comma

                if self.action == "add":
                    sqlString += ") VALUES (%s," % cat
                    for value in updatedValues:
                        sqlString += value + ","
                    sqlString = sqlString[:-1]  # remove last comma
                    sqlString += ")"
                else:
                    sqlString += " WHERE %s=%s" % (key, cat)
                sqlCommands.append(sqlString)
            # for each category
        # for each layer END

        Debug.msg(3,
                  "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)

        return sqlCommands

    def OnReset(self, event=None):
        """Reset form"""
        for layer in self.mapDBInfo.layers.keys():
            table = self.mapDBInfo.layers[layer]["table"]
            key = self.mapDBInfo.layers[layer]["key"]
            columns = self.mapDBInfo.tables[table]
            for idx in range(len(columns[key]['values'])):
                for name in columns.keys():
                    type = columns[name]['type']
                    value = columns[name]['values'][idx]
                    if value is None:
                        value = ''
                    try:
                        id = columns[name]['ids'][idx]
                    except IndexError:
                        id = wx.NOT_FOUND

                    if name != key and id != wx.NOT_FOUND:
                        self.FindWindowById(id).SetValue(str(value))

    def OnClose(self, event):
        """Closes dialog and removes query layer.
        """
        frame = self.parent.parent
        frame.dialogs['attributes'] = None
        if hasattr(self, "digit"):
            self.parent.digit.GetDisplay().SetSelected([])
            if frame.IsAutoRendered():
                self.parent.UpdateMap(render=False)
        elif frame.IsAutoRendered():
            frame.RemoveQueryLayer()
            self.parent.UpdateMap(render=True)
        if self.IsModal():
            self.EndModal(wx.ID_OK)
        else:
            self.Destroy()

    def OnSubmit(self, event):
        """Submit records"""
        layer = 1
        close = True
        enc = GetDbEncoding()

        for sql in self.GetSQLString(updateValues=True):
            if not sql:
                close = False
                continue
            sql = sql.encode(enc)

            driver, database = self.mapDBInfo.GetDbSettings(layer)
            Debug.msg(1, "SQL: %s" % sql)
            RunCommand('db.execute',
                       parent=self,
                       quiet=True,
                       input='-',
                       stdin=sql,
                       driver=driver,
                       database=database)

            layer += 1

        if close and self.closeDialog.IsChecked():
            self.OnClose(event)

    def OnFeature(self, event):
        self.fid = int(event.GetString())
        self.UpdateDialog(cats=self.cats, fid=self.fid)

    def GetCats(self):
        """Get id of selected vector object or 'None' if nothing selected

        :param id: if true return ids otherwise cats
        """
        if self.fid < 0:
            return None

        return self.cats[self.fid]

    def GetFid(self):
        """Get selected feature id"""
        return self.fid

    def UpdateDialog(self,
                     map=None,
                     query=None,
                     cats=None,
                     fid=-1,
                     action=None):
        """Update dialog

        :param map: name of vector map
        :param query:
        :param cats:
        :param fid: feature id
        :param action: add, update, display or None

        :return: True if updated
        :return: False
        """
        if action:
            self.action = action
            if action == 'display':
                enabled = False
            else:
                enabled = True
            self.closeDialog.Enable(enabled)
            self.FindWindowById(wx.ID_OK).Enable(enabled)

        if map:
            self.map = map
            # get layer/table/column information
            self.mapDBInfo = VectorDBInfo(self.map)

        if not self.mapDBInfo:
            return False

        self.mapDBInfo.Reset()

        layers = self.mapDBInfo.layers.keys()  # get available layers

        # id of selected line
        if query:  # select by position
            data = self.mapDBInfo.SelectByPoint(query[0], query[1])
            self.cats = {}
            if data and 'Layer' in data:
                idx = 0
                for layer in data['Layer']:
                    layer = int(layer)
                    if data['Id'][idx] is not None:
                        tfid = int(data['Id'][idx])
                    else:
                        tfid = 0  # Area / Volume
                    if tfid not in self.cats:
                        self.cats[tfid] = {}
                    if layer not in self.cats[tfid]:
                        self.cats[tfid][layer] = []
                    cat = int(data['Category'][idx])
                    self.cats[tfid][layer].append(cat)
                    idx += 1
        else:
            self.cats = cats

        if fid > 0:
            self.fid = fid
        elif len(self.cats.keys()) > 0:
            self.fid = list(self.cats.keys())[0]
        else:
            self.fid = -1

        if len(self.cats.keys()) == 1:
            self.fidMulti.Show(False)
            self.fidText.Show(True)
            if self.fid > 0:
                self.fidText.SetLabel("%d" % self.fid)
            else:
                self.fidText.SetLabel(_("Unknown"))
        else:
            self.fidMulti.Show(True)
            self.fidText.Show(False)
            choices = []
            for tfid in self.cats.keys():
                choices.append(str(tfid))
            self.fidMulti.SetItems(choices)
            self.fidMulti.SetStringSelection(str(self.fid))

        # reset notebook
        self.notebook.DeleteAllPages()

        for layer in layers:  # for each layer
            if not query:  # select by layer/cat
                if self.fid > 0 and layer in self.cats[self.fid]:
                    for cat in self.cats[self.fid][layer]:
                        nselected = self.mapDBInfo.SelectFromTable(
                            layer,
                            where="%s=%d" %
                            (self.mapDBInfo.layers[layer]['key'], cat))
                else:
                    nselected = 0

            # if nselected <= 0 and self.action != "add":
            #    continue # nothing selected ...

            if self.action == "add":
                if nselected <= 0:
                    if layer in self.cats[self.fid]:
                        table = self.mapDBInfo.layers[layer]["table"]
                        key = self.mapDBInfo.layers[layer]["key"]
                        columns = self.mapDBInfo.tables[table]
                        for name in columns.keys():
                            if name == key:
                                for cat in self.cats[self.fid][layer]:
                                    self.mapDBInfo.tables[table][name][
                                        'values'].append(cat)
                            else:
                                self.mapDBInfo.tables[table][name][
                                    'values'].append(None)
                else:  # change status 'add' -> 'update'
                    self.action = "update"

            table = self.mapDBInfo.layers[layer]["table"]
            key = self.mapDBInfo.layers[layer]["key"]
            columns = self.mapDBInfo.tables[table]

            for idx in range(len(columns[key]['values'])):
                for name in columns.keys():
                    if name == key:
                        cat = int(columns[name]['values'][idx])
                        break

                # use scrolled panel instead (and fix initial max height of the
                # window to 480px)
                panel = scrolled.ScrolledPanel(parent=self.notebook,
                                               id=wx.ID_ANY,
                                               size=(-1, 150))
                panel.SetupScrolling(scroll_x=False)

                self.notebook.AddPage(page=panel,
                                      text=" %s %d / %s %d" %
                                      (_("Layer"), layer, _("Category"), cat))

                # notebook body
                border = wx.BoxSizer(wx.VERTICAL)

                flexSizer = wx.FlexGridSizer(cols=3, hgap=3, vgap=3)
                flexSizer.AddGrowableCol(2)
                # columns (sorted by index)
                names = [''] * len(columns.keys())
                for name in columns.keys():
                    names[columns[name]['index']] = name

                for name in names:
                    if name == key:  # skip key column (category)
                        continue

                    vtype = columns[name]['type'].lower()
                    ctype = columns[name]['ctype']

                    if columns[name]['values'][idx] is not None:
                        if not isinstance(columns[name]['ctype'],
                                          six.string_types):
                            value = str(columns[name]['values'][idx])
                        else:
                            value = columns[name]['values'][idx]
                    else:
                        value = ''

                    colName = StaticText(parent=panel,
                                         id=wx.ID_ANY,
                                         label=name)
                    colType = StaticText(parent=panel,
                                         id=wx.ID_ANY,
                                         label="[%s]:" % vtype)
                    colValue = TextCtrl(parent=panel,
                                        id=wx.ID_ANY,
                                        value=value)
                    colValue.SetName(name)
                    if ctype == int:
                        colValue.SetValidator(IntegerValidator())
                    elif ctype == float:
                        colValue.SetValidator(FloatValidator())

                    self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
                    if self.action == 'display':
                        colValue.SetWindowStyle(wx.TE_READONLY)

                    flexSizer.Add(colName,
                                  proportion=0,
                                  flag=wx.ALIGN_CENTER_VERTICAL)
                    flexSizer.Add(colType,
                                  proportion=0,
                                  flag=wx.ALIGN_CENTER_VERTICAL
                                  | wx.ALIGN_RIGHT)
                    flexSizer.Add(colValue,
                                  proportion=1,
                                  flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
                    # add widget reference to self.columns
                    columns[name]['ids'].append(
                        colValue.GetId())  # name, type, values, id
                # for each attribute (including category) END
                border.Add(flexSizer,
                           proportion=1,
                           flag=wx.ALL | wx.EXPAND,
                           border=5)
                panel.SetSizer(border)
            # for each category END
        # for each layer END

        if self.notebook.GetPageCount() == 0:
            self.noFoundMsg.Show(True)
        else:
            self.noFoundMsg.Show(False)

        self.Layout()

        return True

    def SetColumnValue(self, layer, column, value):
        """Set attrbute value

        :param column: column name
        :param value: value
        """
        table = self.mapDBInfo.GetTable(layer)
        columns = self.mapDBInfo.GetTableDesc(table)

        for key, col in six.iteritems(columns):
            if key == column:
                col['values'] = [
                    col['ctype'](value),
                ]
                break
Ejemplo n.º 7
0
class TimeAnimationSlider(AnimationSliderBase):

    def __init__(self, parent):
        AnimationSliderBase.__init__(self, parent)
        self.timeLabels = []
        self.label2 = StaticText(self, id=wx.ID_ANY)
        self.label3 = StaticText(self, id=wx.ID_ANY)
        self.label2Length = 0
        self.temporalType = TemporalType.RELATIVE

        self._setLabel()
        self._doLayout()

    def _doLayout(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.label1, proportion=0,
                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
        hbox.AddStretchSpacer()
        hbox.Add(self.indexField, proportion=0,
                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
        hbox.Add(self.label2, proportion=0,
                 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3)
        hbox.AddStretchSpacer()
        hbox.Add(self.label3, proportion=0,
                 flag=wx.ALIGN_CENTER_VERTICAL, border=0)
        vbox.Add(hbox, proportion=0, flag=wx.EXPAND, border=0)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(
            self.slider,
            proportion=1,
            flag=wx.ALIGN_CENTER | wx.EXPAND,
            border=0)
        vbox.Add(hbox, proportion=0, flag=wx.EXPAND, border=0)

        self._setTemporalType()
        self.SetSizerAndFit(vbox)

    def _setTemporalType(self):
        sizer = self.indexField.GetContainingSizer()
        # sizer.Show(self.indexField, False) # TODO: what to do?
        sizer.Show(self.indexField, self.temporalType == TemporalType.RELATIVE)
        self.Layout()

    def SetTemporalType(self, mode):
        self.temporalType = mode
        self._setTemporalType()

    def _setFrames(self, timeLabels):
        self.timeLabels = timeLabels
        self.framesCount = len(timeLabels)
        if self.framesCount > 1:
            self.slider.SetRange(0, self.framesCount - 1)
            self.EnableSlider(True)
        else:
            self.EnableSlider(False)
        self._setLabel()
        # TODO: fix setting index values, until then:
        self.indexField.Disable()

    def _setLabel(self):
        if self.timeLabels:
            if self.temporalType == TemporalType.ABSOLUTE:
                start = self.timeLabels[0][0]
                self.label1.SetLabel(start)
                if self.timeLabels[-1][1]:
                    end = self.timeLabels[-1][1]
                else:
                    end = self.timeLabels[-1][0]
                self.label3.SetLabel(end)
            else:
                unit = self.timeLabels[0][2]
                start = self.timeLabels[0][0]
                self.label1.SetLabel(start)
                if self.timeLabels[-1][1]:
                    end = self.timeLabels[-1][1]
                else:
                    end = self.timeLabels[-1][0]
                end = "%(end)s %(unit)s" % {'end': end, 'unit': unit}
                self.label3.SetLabel(end)

            self.label2Length = len(start)
            self._updateFrameIndex(0)

        else:
            self.label1.SetLabel("")
            self.label2.SetLabel("")
            self.label3.SetLabel("")
        self.Layout()

    def _updateFrameIndex(self, index):
        start = self.timeLabels[index][0]
        if self.timeLabels[index][1]:  # interval
            if self.temporalType == TemporalType.ABSOLUTE:
                label = _("%(from)s %(dash)s %(to)s") % {
                    'from': start,
                    'dash': u"\u2013",
                    'to': self.timeLabels[index][1]}
            else:
                label = _("to %(to)s") % {'to': self.timeLabels[index][1]}
        else:
            if self.temporalType == TemporalType.ABSOLUTE:
                label = start
            else:
                label = ''
        self.label2.SetLabel(label)
        if self.temporalType == TemporalType.RELATIVE:
            self.indexField.SetValue(start)
        if len(label) != self.label2Length:
            self.label2Length = len(label)
            self.Layout()
Ejemplo n.º 8
0
class MapCalcFrame(wx.Frame):
    """Mapcalc Frame class. Calculator-style window to create and run
    r(3).mapcalc statements.
    """
    def __init__(self,
                 parent,
                 giface,
                 cmd,
                 id=wx.ID_ANY,
                 style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
                 **kwargs):
        self.parent = parent
        self._giface = giface

        if self.parent:
            self.log = self.parent.GetLogWindow()
        else:
            self.log = None

        # grass command
        self.cmd = cmd

        if self.cmd == 'r.mapcalc':
            self.rast3d = False
            title = _('GRASS GIS Raster Map Calculator')
        if self.cmd == 'r3.mapcalc':
            self.rast3d = True
            title = _('GRASS GIS 3D Raster Map Calculator')

        wx.Frame.__init__(self, parent, id=id, title=title, **kwargs)
        self.SetIcon(
            wx.Icon(os.path.join(globalvar.ICONDIR, 'grass.ico'),
                    wx.BITMAP_TYPE_ICO))

        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
        self.CreateStatusBar()

        #
        # variables
        #
        self.heading = _('mapcalc statement')
        self.funct_dict = {
            'abs(x)': 'abs()',
            'acos(x)': 'acos()',
            'asin(x)': 'asin()',
            'atan(x)': 'atan()',
            'atan(x,y)': 'atan( , )',
            'cos(x)': 'cos()',
            'double(x)': 'double()',
            'eval([x,y,...,]z)': 'eval()',
            'exp(x)': 'exp()',
            'exp(x,y)': 'exp( , )',
            'float(x)': 'float()',
            'graph(x,x1,y1[x2,y2..])': 'graph( , , )',
            'if(x)': 'if()',
            'if(x,a)': 'if( , )',
            'if(x,a,b)': 'if( , , )',
            'if(x,a,b,c)': 'if( , , , )',
            'int(x)': 'int()',
            'isnull(x)': 'isnull()',
            'log(x)': 'log(',
            'log(x,b)': 'log( , )',
            'max(x,y[,z...])': 'max( , )',
            'median(x,y[,z...])': 'median( , )',
            'min(x,y[,z...])': 'min( , )',
            'mode(x,y[,z...])': 'mode( , )',
            'nmax(x,y[,z...])': 'nmax( , )',
            'nmedian(x,y[,z...])': 'nmedian( , )',
            'nmin(x,y[,z...])': 'nmin( , )',
            'nmode(x,y[,z...])': 'nmode( , )',
            'not(x)': 'not()',
            'pow(x,y)': 'pow( , )',
            'rand(a,b)': 'rand( , )',
            'round(x)': 'round()',
            'round(x,y)': 'round( , )',
            'round(x,y,z)': 'round( , , )',
            'sin(x)': 'sin()',
            'sqrt(x)': 'sqrt()',
            'tan(x)': 'tan()',
            'xor(x,y)': 'xor( , )',
            'row()': 'row()',
            'col()': 'col()',
            'nrows()': 'nrows()',
            'ncols()': 'ncols()',
            'x()': 'x()',
            'y()': 'y()',
            'ewres()': 'ewres()',
            'nsres()': 'nsres()',
            'area()': 'area()',
            'null()': 'null()'
        }

        if self.rast3d:
            self.funct_dict['z()'] = 'z()'
            self.funct_dict['tbres()'] = 'tbres()'
            element = 'raster_3d'
        else:
            element = 'cell'

        # characters which can be in raster map name but the map name must be
        # then quoted
        self.charactersToQuote = '+-&!<>%~?^|'
        # stores last typed map name in Select widget to distinguish typing
        # from selection
        self.lastMapName = ''

        self.operatorBox = StaticBox(parent=self.panel,
                                     id=wx.ID_ANY,
                                     label=" %s " % _('Operators'))
        self.outputBox = StaticBox(parent=self.panel,
                                   id=wx.ID_ANY,
                                   label=" %s " % _('Output'))
        self.operandBox = StaticBox(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label=" %s " % _('Operands'))
        self.expressBox = StaticBox(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label=" %s " % _('Expression'))

        #
        # Buttons
        #
        self.btn_clear = ClearButton(parent=self.panel)
        self.btn_help = Button(parent=self.panel, id=wx.ID_HELP)
        self.btn_run = Button(parent=self.panel, id=wx.ID_ANY, label=_("&Run"))
        self.btn_run.SetDefault()
        self.btn_close = CloseButton(parent=self.panel)
        self.btn_save = Button(parent=self.panel, id=wx.ID_SAVE)
        self.btn_save.SetToolTip(_('Save expression to file'))
        self.btn_load = Button(parent=self.panel,
                               id=wx.ID_ANY,
                               label=_("&Load"))
        self.btn_load.SetToolTip(_('Load expression from file'))
        self.btn_copy = Button(parent=self.panel,
                               id=wx.ID_ANY,
                               label=_("Copy"))
        self.btn_copy.SetToolTip(
            _("Copy the current command string to the clipboard"))

        self.btn = dict()
        self.btn['pow'] = Button(parent=self.panel, id=wx.ID_ANY, label="^")
        self.btn['pow'].SetToolTip(_('exponent'))
        self.btn['div'] = Button(parent=self.panel, id=wx.ID_ANY, label="/")
        self.btn['div'].SetToolTip(_('divide'))
        self.btn['add'] = Button(parent=self.panel, id=wx.ID_ANY, label="+")
        self.btn['add'].SetToolTip(_('add'))
        self.btn['minus'] = Button(parent=self.panel, id=wx.ID_ANY, label="-")
        self.btn['minus'].SetToolTip(_('subtract'))
        self.btn['mod'] = Button(parent=self.panel, id=wx.ID_ANY, label="%")
        self.btn['mod'].SetToolTip(_('modulus'))
        self.btn['mult'] = Button(parent=self.panel, id=wx.ID_ANY, label="*")
        self.btn['mult'].SetToolTip(_('multiply'))

        self.btn['parenl'] = Button(parent=self.panel, id=wx.ID_ANY, label="(")
        self.btn['parenr'] = Button(parent=self.panel, id=wx.ID_ANY, label=")")
        self.btn['lshift'] = Button(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label="<<")
        self.btn['lshift'].SetToolTip(_('left shift'))
        self.btn['rshift'] = Button(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label=">>")
        self.btn['rshift'].SetToolTip(_('right shift'))
        self.btn['rshiftu'] = Button(parent=self.panel,
                                     id=wx.ID_ANY,
                                     label=">>>")
        self.btn['rshiftu'].SetToolTip(_('right shift (unsigned)'))
        self.btn['gt'] = Button(parent=self.panel, id=wx.ID_ANY, label=">")
        self.btn['gt'].SetToolTip(_('greater than'))
        self.btn['gteq'] = Button(parent=self.panel, id=wx.ID_ANY, label=">=")
        self.btn['gteq'].SetToolTip(_('greater than or equal to'))
        self.btn['lt'] = Button(parent=self.panel, id=wx.ID_ANY, label="<")
        self.btn['lt'].SetToolTip(_('less than'))
        self.btn['lteq'] = Button(parent=self.panel, id=wx.ID_ANY, label="<=")
        self.btn['lteq'].SetToolTip(_('less than or equal to'))
        self.btn['eq'] = Button(parent=self.panel, id=wx.ID_ANY, label="==")
        self.btn['eq'].SetToolTip(_('equal to'))
        self.btn['noteq'] = Button(parent=self.panel, id=wx.ID_ANY, label="!=")
        self.btn['noteq'].SetToolTip(_('not equal to'))

        self.btn['compl'] = Button(parent=self.panel, id=wx.ID_ANY, label="~")
        self.btn['compl'].SetToolTip(_('one\'s complement'))
        self.btn['not'] = Button(parent=self.panel, id=wx.ID_ANY, label="!")
        self.btn['not'].SetToolTip(_('NOT'))
        self.btn['andbit'] = Button(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label='&&')
        self.btn['andbit'].SetToolTip(_('bitwise AND'))
        self.btn['orbit'] = Button(parent=self.panel, id=wx.ID_ANY, label="|")
        self.btn['orbit'].SetToolTip(_('bitwise OR'))
        self.btn['and'] = Button(parent=self.panel, id=wx.ID_ANY, label="&&&&")
        self.btn['and'].SetToolTip(_('logical AND'))
        self.btn['andnull'] = Button(parent=self.panel,
                                     id=wx.ID_ANY,
                                     label="&&&&&&")
        self.btn['andnull'].SetToolTip(_('logical AND (ignores NULLs)'))
        self.btn['or'] = Button(parent=self.panel, id=wx.ID_ANY, label="||")
        self.btn['or'].SetToolTip(_('logical OR'))
        self.btn['ornull'] = Button(parent=self.panel,
                                    id=wx.ID_ANY,
                                    label="|||")
        self.btn['ornull'].SetToolTip(_('logical OR (ignores NULLs)'))
        self.btn['cond'] = Button(parent=self.panel,
                                  id=wx.ID_ANY,
                                  label="a ? b : c")
        self.btn['cond'].SetToolTip(_('conditional'))

        #
        # Text area
        #
        self.text_mcalc = TextCtrl(parent=self.panel,
                                   id=wx.ID_ANY,
                                   size=(-1, 100),
                                   style=wx.TE_MULTILINE)
        wx.CallAfter(self.text_mcalc.SetFocus)

        #
        # Map and function insertion text and ComboBoxes
        self.newmaplabel = StaticText(parent=self.panel, id=wx.ID_ANY)
        if self.rast3d:
            self.newmaplabel.SetLabel(
                _('Name for new 3D raster map to create'))
        else:
            self.newmaplabel.SetLabel(_('Name for new raster map to create'))
        # As we can write only to current mapset, names should not be fully qualified
        # to not confuse end user about writing in other mapset
        self.newmaptxt = Select(parent=self.panel,
                                id=wx.ID_ANY,
                                size=(250, -1),
                                type=element,
                                multiple=False,
                                fullyQualified=False)
        self.mapsellabel = StaticText(parent=self.panel, id=wx.ID_ANY)
        if self.rast3d:
            self.mapsellabel.SetLabel(_('Insert existing 3D raster map'))
        else:
            self.mapsellabel.SetLabel(_('Insert existing raster map'))
        self.mapselect = Select(parent=self.panel,
                                id=wx.ID_ANY,
                                size=(250, -1),
                                type=element,
                                multiple=False)
        self.functlabel = StaticText(parent=self.panel,
                                     id=wx.ID_ANY,
                                     label=_('Insert mapcalc function'))
        self.function = wx.ComboBox(parent=self.panel,
                                    id=wx.ID_ANY,
                                    size=(250, -1),
                                    choices=sorted(self.funct_dict.keys()),
                                    style=wx.CB_DROPDOWN | wx.CB_READONLY
                                    | wx.TE_PROCESS_ENTER)

        self.overwrite = wx.CheckBox(
            parent=self.panel,
            id=wx.ID_ANY,
            label=_("Allow output files to overwrite existing files"))
        self.overwrite.SetValue(
            UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))

        self.randomSeed = wx.CheckBox(
            parent=self.panel, label=_("Generate random seed for rand()"))
        self.randomSeedStaticText = StaticText(parent=self.panel,
                                               label=_("Seed:"))
        self.randomSeedText = TextCtrl(parent=self.panel,
                                       size=(100, -1),
                                       validator=IntegerValidator())
        self.randomSeedText.SetToolTip(_("Integer seed for rand() function"))
        self.randomSeed.SetValue(True)
        self.randomSeedStaticText.Disable()
        self.randomSeedText.Disable()

        self.addbox = wx.CheckBox(
            parent=self.panel,
            label=_('Add created raster map into layer tree'),
            style=wx.NO_BORDER)
        self.addbox.SetValue(
            UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
        if not self.parent or self.parent.GetName() != 'LayerManager':
            self.addbox.Hide()

        #
        # Bindings
        #
        for btn in self.btn.keys():
            self.btn[btn].Bind(wx.EVT_BUTTON, self.AddMark)

        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
        self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
        self.btn_run.Bind(wx.EVT_BUTTON, self.OnMCalcRun)
        self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
        self.btn_save.Bind(wx.EVT_BUTTON, self.OnSaveExpression)
        self.btn_load.Bind(wx.EVT_BUTTON, self.OnLoadExpression)
        self.btn_copy.Bind(wx.EVT_BUTTON, self.OnCopyCommand)

        self.mapselect.Bind(wx.EVT_TEXT, self.OnSelect)
        self.function.Bind(wx.EVT_COMBOBOX, self._return_funct)
        self.function.Bind(wx.EVT_TEXT_ENTER, self.OnSelect)
        self.newmaptxt.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
        self.text_mcalc.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
        self.overwrite.Bind(wx.EVT_CHECKBOX, self.OnUpdateStatusBar)
        self.randomSeed.Bind(wx.EVT_CHECKBOX, self.OnUpdateStatusBar)
        self.randomSeed.Bind(wx.EVT_CHECKBOX, self.OnSeedFlag)
        self.randomSeedText.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)

        # bind closing to ESC
        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_CANCEL)
        accelTableList = [(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CANCEL)]
        accelTable = wx.AcceleratorTable(accelTableList)
        self.SetAcceleratorTable(accelTable)

        self._layout()

        self.SetMinSize(self.panel.GetBestSize())
        # workaround for http://trac.wxwidgets.org/ticket/13628
        self.SetSize(self.panel.GetBestSize())

    def _return_funct(self, event):
        i = event.GetString()
        self._addSomething(self.funct_dict[i])

        # reset
        win = self.FindWindowById(event.GetId())
        win.SetValue('')

    def _layout(self):
        sizer = wx.BoxSizer(wx.VERTICAL)

        controlSizer = wx.BoxSizer(wx.HORIZONTAL)
        operatorSizer = wx.StaticBoxSizer(self.operatorBox, wx.HORIZONTAL)
        outOpeSizer = wx.BoxSizer(wx.VERTICAL)

        buttonSizer1 = wx.GridBagSizer(5, 1)
        buttonSizer1.Add(self.btn['add'], pos=(0, 0))
        buttonSizer1.Add(self.btn['minus'], pos=(0, 1))
        buttonSizer1.Add(self.btn['mod'], pos=(5, 0))
        buttonSizer1.Add(self.btn['mult'], pos=(1, 0))
        buttonSizer1.Add(self.btn['div'], pos=(1, 1))
        buttonSizer1.Add(self.btn['pow'], pos=(5, 1))
        buttonSizer1.Add(self.btn['gt'], pos=(2, 0))
        buttonSizer1.Add(self.btn['gteq'], pos=(2, 1))
        buttonSizer1.Add(self.btn['eq'], pos=(4, 0))
        buttonSizer1.Add(self.btn['lt'], pos=(3, 0))
        buttonSizer1.Add(self.btn['lteq'], pos=(3, 1))
        buttonSizer1.Add(self.btn['noteq'], pos=(4, 1))

        buttonSizer2 = wx.GridBagSizer(5, 1)
        buttonSizer2.Add(self.btn['and'], pos=(0, 0))
        buttonSizer2.Add(self.btn['andbit'], pos=(1, 0))
        buttonSizer2.Add(self.btn['andnull'], pos=(2, 0))
        buttonSizer2.Add(self.btn['or'], pos=(0, 1))
        buttonSizer2.Add(self.btn['orbit'], pos=(1, 1))
        buttonSizer2.Add(self.btn['ornull'], pos=(2, 1))
        buttonSizer2.Add(self.btn['lshift'], pos=(3, 0))
        buttonSizer2.Add(self.btn['rshift'], pos=(3, 1))
        buttonSizer2.Add(self.btn['rshiftu'], pos=(4, 0))
        buttonSizer2.Add(self.btn['cond'], pos=(5, 0))
        buttonSizer2.Add(self.btn['compl'], pos=(5, 1))
        buttonSizer2.Add(self.btn['not'], pos=(4, 1))

        outputSizer = wx.StaticBoxSizer(self.outputBox, wx.VERTICAL)
        outputSizer.Add(self.newmaplabel,
                        flag=wx.ALIGN_CENTER | wx.TOP,
                        border=5)
        outputSizer.Add(self.newmaptxt, flag=wx.EXPAND | wx.ALL, border=5)

        operandSizer = wx.StaticBoxSizer(self.operandBox, wx.HORIZONTAL)

        buttonSizer3 = wx.GridBagSizer(7, 1)
        buttonSizer3.Add(self.functlabel,
                         pos=(0, 0),
                         span=(1, 2),
                         flag=wx.ALIGN_CENTER | wx.EXPAND)
        buttonSizer3.Add(self.function, pos=(1, 0), span=(1, 2))
        buttonSizer3.Add(self.mapsellabel,
                         pos=(2, 0),
                         span=(1, 2),
                         flag=wx.ALIGN_CENTER)
        buttonSizer3.Add(self.mapselect, pos=(3, 0), span=(1, 2))
        threebutton = wx.GridBagSizer(1, 2)
        threebutton.Add(self.btn['parenl'],
                        pos=(0, 0),
                        span=(1, 1),
                        flag=wx.ALIGN_LEFT)
        threebutton.Add(self.btn['parenr'],
                        pos=(0, 1),
                        span=(1, 1),
                        flag=wx.ALIGN_CENTER)
        threebutton.Add(self.btn_clear,
                        pos=(0, 2),
                        span=(1, 1),
                        flag=wx.ALIGN_RIGHT)
        buttonSizer3.Add(threebutton,
                         pos=(4, 0),
                         span=(1, 1),
                         flag=wx.ALIGN_CENTER)

        buttonSizer4 = wx.BoxSizer(wx.HORIZONTAL)
        buttonSizer4.Add(self.btn_load, flag=wx.ALL, border=5)
        buttonSizer4.Add(self.btn_save, flag=wx.ALL, border=5)
        buttonSizer4.Add(self.btn_copy, flag=wx.ALL, border=5)
        buttonSizer4.AddSpacer(30)
        buttonSizer4.Add(self.btn_help, flag=wx.ALL, border=5)
        buttonSizer4.Add(self.btn_run, flag=wx.ALL, border=5)
        buttonSizer4.Add(self.btn_close, flag=wx.ALL, border=5)

        operatorSizer.Add(buttonSizer1,
                          proportion=0,
                          flag=wx.ALL | wx.EXPAND,
                          border=5)
        operatorSizer.Add(buttonSizer2,
                          proportion=0,
                          flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
                          border=5)

        operandSizer.Add(buttonSizer3, proportion=0, flag=wx.ALL, border=5)

        controlSizer.Add(operatorSizer,
                         proportion=1,
                         flag=wx.RIGHT | wx.EXPAND,
                         border=5)
        outOpeSizer.Add(outputSizer, proportion=0, flag=wx.EXPAND)
        outOpeSizer.Add(operandSizer,
                        proportion=1,
                        flag=wx.EXPAND | wx.TOP,
                        border=5)
        controlSizer.Add(outOpeSizer, proportion=0, flag=wx.EXPAND)

        expressSizer = wx.StaticBoxSizer(self.expressBox, wx.HORIZONTAL)
        expressSizer.Add(self.text_mcalc, proportion=1, flag=wx.EXPAND)

        sizer.Add(controlSizer,
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=5)
        sizer.Add(expressSizer,
                  proportion=1,
                  flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                  border=5)
        sizer.Add(buttonSizer4,
                  proportion=0,
                  flag=wx.ALIGN_RIGHT | wx.ALL,
                  border=3)

        randomSizer = wx.BoxSizer(wx.HORIZONTAL)
        randomSizer.Add(self.randomSeed,
                        proportion=0,
                        flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
                        border=20)
        randomSizer.Add(self.randomSeedStaticText,
                        proportion=0,
                        flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
                        border=5)
        randomSizer.Add(self.randomSeedText, proportion=0)
        sizer.Add(randomSizer, proportion=0, flag=wx.LEFT | wx.RIGHT, border=5)

        sizer.Add(self.overwrite,
                  proportion=0,
                  flag=wx.LEFT | wx.RIGHT,
                  border=5)
        if self.addbox.IsShown():
            sizer.Add(self.addbox,
                      proportion=0,
                      flag=wx.LEFT | wx.RIGHT,
                      border=5)

        self.panel.SetAutoLayout(True)
        self.panel.SetSizer(sizer)
        sizer.Fit(self.panel)

        self.Layout()

    def AddMark(self, event):
        """Sends operators to insertion method
        """
        if event.GetId() == self.btn['compl'].GetId():
            mark = "~"
        elif event.GetId() == self.btn['not'].GetId():
            mark = "!"
        elif event.GetId() == self.btn['pow'].GetId():
            mark = "^"
        elif event.GetId() == self.btn['div'].GetId():
            mark = "/"
        elif event.GetId() == self.btn['add'].GetId():
            mark = "+"
        elif event.GetId() == self.btn['minus'].GetId():
            mark = "-"
        elif event.GetId() == self.btn['mod'].GetId():
            mark = "%"
        elif event.GetId() == self.btn['mult'].GetId():
            mark = "*"
        elif event.GetId() == self.btn['lshift'].GetId():
            mark = "<<"
        elif event.GetId() == self.btn['rshift'].GetId():
            mark = ">>"
        elif event.GetId() == self.btn['rshiftu'].GetId():
            mark = ">>>"
        elif event.GetId() == self.btn['gt'].GetId():
            mark = ">"
        elif event.GetId() == self.btn['gteq'].GetId():
            mark = ">="
        elif event.GetId() == self.btn['lt'].GetId():
            mark = "<"
        elif event.GetId() == self.btn['lteq'].GetId():
            mark = "<="
        elif event.GetId() == self.btn['eq'].GetId():
            mark = "=="
        elif event.GetId() == self.btn['noteq'].GetId():
            mark = "!="
        elif event.GetId() == self.btn['andbit'].GetId():
            mark = "&"
        elif event.GetId() == self.btn['orbit'].GetId():
            mark = "|"
        elif event.GetId() == self.btn['or'].GetId():
            mark = "||"
        elif event.GetId() == self.btn['ornull'].GetId():
            mark = "|||"
        elif event.GetId() == self.btn['and'].GetId():
            mark = "&&"
        elif event.GetId() == self.btn['andnull'].GetId():
            mark = "&&&"
        elif event.GetId() == self.btn['cond'].GetId():
            mark = " ? : "
        elif event.GetId() == self.btn['parenl'].GetId():
            mark = "("
        elif event.GetId() == self.btn['parenr'].GetId():
            mark = ")"
        self._addSomething(mark)

    # unused
    # def OnSelectTextEvt(self, event):
    #     """Checks if user is typing or the event was emited by map selection.
    #     Prevents from changing focus.
    #     """
    #     item = self.mapselect.GetValue().strip()
    #     if not (abs(len(item) - len(self.lastMapName)) == 1 and \
    #         self.lastMapName in item or item in self.lastMapName):
    #         self.OnSelect(event)

    #     self.lastMapName = item

    def OnSelect(self, event):
        """Gets raster map or function selection and send it to
        insertion method.

        Checks for characters which can be in raster map name but
        the raster map name must be then quoted.
        """
        win = self.FindWindowById(event.GetId())
        item = win.GetValue().strip()
        if any((char in item) for char in self.charactersToQuote):
            item = '"' + item + '"'
        self._addSomething(item)

        win.ChangeValue('')  # reset
        # Map selector likes to keep focus. Set it back to expression input area
        wx.CallAfter(self.text_mcalc.SetFocus)

    def OnUpdateStatusBar(self, event):
        """Update statusbar text"""
        command = self._getCommand()
        self.SetStatusText(command)
        event.Skip()

    def OnSeedFlag(self, event):
        checked = self.randomSeed.IsChecked()
        self.randomSeedText.Enable(not checked)
        self.randomSeedStaticText.Enable(not checked)

        event.Skip()

    def _getCommand(self):
        """Returns entire command as string."""
        expr = self.text_mcalc.GetValue().strip().replace("\n", " ")
        cmd = 'r.mapcalc'
        if self.rast3d:
            cmd = 'r3.mapcalc'
        overwrite = ''
        if self.overwrite.IsChecked():
            overwrite = ' --overwrite'
        seed_flag = seed = ''
        if re.search(pattern="rand *\(.+\)", string=expr):
            if self.randomSeed.IsChecked():
                seed_flag = ' -s'
            else:
                seed = " seed={val}".format(
                    val=self.randomSeedText.GetValue().strip())

        return ('{cmd} expression="{new} = {expr}"{seed}{seed_flag}{overwrite}'
                .format(cmd=cmd,
                        expr=expr,
                        new=self.newmaptxt.GetValue(),
                        seed_flag=seed_flag,
                        seed=seed,
                        overwrite=overwrite))

    def _addSomething(self, what):
        """Inserts operators, map names, and functions into text area
        """
        mcalcstr = self.text_mcalc.GetValue()
        position = self.text_mcalc.GetInsertionPoint()

        newmcalcstr = mcalcstr[:position]

        position_offset = 0
        try:
            if newmcalcstr[-1] != ' ':
                newmcalcstr += ' '
                position_offset += 1
        except:
            pass

        newmcalcstr += what

        # Do not add extra space if there is already one
        try:
            if newmcalcstr[-1] != ' ' and mcalcstr[position] != ' ':
                newmcalcstr += ' '
        except:
            newmcalcstr += ' '

        newmcalcstr += mcalcstr[position:]

        self.text_mcalc.SetValue(newmcalcstr)
        if len(what) > 0:
            match = re.search(pattern="\(.*\)", string=what)
            if match:
                position_offset += match.start() + 1
            else:
                position_offset += len(what)
                try:
                    if newmcalcstr[position + position_offset] == ' ':
                        position_offset += 1
                except:
                    pass

        self.text_mcalc.SetInsertionPoint(position + position_offset)
        self.text_mcalc.Update()
        self.text_mcalc.SetFocus()

    def OnMCalcRun(self, event):
        """Builds and runs r.mapcalc statement
        """
        name = self.newmaptxt.GetValue().strip()
        if not name:
            GError(parent=self,
                   message=_("You must enter the name of "
                             "a new raster map to create."))
            return

        if not (name[0] == '"' and name[-1] == '"') and any(
            (char in name) for char in self.charactersToQuote):
            name = '"' + name + '"'

        expr = self.text_mcalc.GetValue().strip().replace("\n", " ")
        if not expr:
            GError(parent=self,
                   message=_("You must enter an expression "
                             "to create a new raster map."))
            return

        seed_flag = seed = None
        if re.search(pattern="rand *\(.+\)", string=expr):
            if self.randomSeed.IsChecked():
                seed_flag = '-s'
            else:
                seed = self.randomSeedText.GetValue().strip()
        if self.log:
            cmd = [self.cmd]
            if seed_flag:
                cmd.append('-s')
            if seed:
                cmd.append("seed={val}".format(val=seed))
            if self.overwrite.IsChecked():
                cmd.append('--overwrite')
            cmd.append(str('expression=%s = %s' % (name, expr)))

            self.log.RunCmd(cmd, onDone=self.OnDone)
            self.parent.Raise()
        else:
            if self.overwrite.IsChecked():
                overwrite = True
            else:
                overwrite = False
            params = dict(expression="%s=%s" % (name, expr),
                          overwrite=overwrite)
            if seed_flag:
                params['flags'] = 's'
            if seed:
                params['seed'] = seed

            RunCommand(self.cmd, **params)

    def OnDone(self, event):
        """Add create map to the layer tree

        Sends the mapCreated signal from the grass interface.
        """
        if event.returncode != 0:
            return
        name = self.newmaptxt.GetValue().strip(
            ' "') + '@' + grass.gisenv()['MAPSET']
        ltype = 'raster'
        if self.rast3d:
            ltype = 'raster_3d'
        self._giface.mapCreated.emit(name=name,
                                     ltype=ltype,
                                     add=self.addbox.IsChecked())
        gisenv = grass.gisenv()
        self._giface.grassdbChanged.emit(grassdb=gisenv['GISDBASE'],
                                         location=gisenv['LOCATION_NAME'],
                                         mapset=gisenv['MAPSET'],
                                         action='new',
                                         map=name.split('@')[0],
                                         element=ltype)

    def OnSaveExpression(self, event):
        """Saves expression to file
        """
        mctxt = self.newmaptxt.GetValue() + ' = ' + self.text_mcalc.GetValue(
        ) + os.linesep

        # dialog
        dlg = wx.FileDialog(
            parent=self,
            message=_("Choose a file name to save the expression"),
            wildcard=_("Expression file (*)|*"),
            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            if not path:
                dlg.Destroy()
                return

            try:
                fobj = open(path, 'w')
                fobj.write(mctxt)
            finally:
                fobj.close()

        dlg.Destroy()

    def OnLoadExpression(self, event):
        """Load expression from file
        """
        dlg = wx.FileDialog(
            parent=self,
            message=_("Choose a file name to load the expression"),
            wildcard=_("Expression file (*)|*"),
            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            if not path:
                dlg.Destroy()
                return

            try:
                fobj = open(path, 'r')
                mctxt = fobj.read()
            finally:
                fobj.close()

            try:
                result, exp = mctxt.split('=', 1)
            except ValueError:
                result = ''
                exp = mctxt

            self.newmaptxt.SetValue(result.strip())
            self.text_mcalc.SetValue(exp.strip())
            self.text_mcalc.SetFocus()
            self.text_mcalc.SetInsertionPointEnd()

        dlg.Destroy()

    def OnCopyCommand(self, event):
        command = self._getCommand()
        cmddata = wx.TextDataObject()
        cmddata.SetText(command)
        if wx.TheClipboard.Open():
            wx.TheClipboard.SetData(cmddata)
            wx.TheClipboard.Close()
            self.SetStatusText(
                _("'{cmd}' copied to clipboard").format(cmd=command))

    def OnClear(self, event):
        """Clears text area
        """
        self.text_mcalc.SetValue('')

    def OnHelp(self, event):
        """Launches r.mapcalc help
        """
        RunCommand('g.manual', parent=self, entry=self.cmd)

    def OnClose(self, event):
        """Close window"""
        self.Destroy()
Ejemplo n.º 9
0
class LocationDownloadPanel(wx.Panel):
    """Panel to select and initiate downloads of locations.

    Has a place to report errors to user and also any potential problems
    before the user hits the button.

    In the future, it can potentially show also some details about what
    will be downloaded. The choice widget can be also replaced.

    For the future, there can be multiple panels with different methods
    or sources, e.g. direct input of URL. These can be in separate tabs
    of one panel (perhaps sharing the common background download and
    message logic).
    """
    def __init__(self, parent, database, locations=LOCATIONS):
        """

        :param database: directory with G database to download to
        :param locations: list of dictionaries with label and url
        """
        wx.Panel.__init__(self, parent=parent)

        self.parent = parent
        self._last_downloaded_location_name = None
        self._download_in_progress = False
        self.database = database
        self.locations = locations
        self._abort_btn_label = _("Abort")
        self._abort_btn_tooltip = _("Abort download location")

        self.label = StaticText(parent=self,
                                label=_("Select sample location to download:"))

        choices = []
        for item in self.locations:
            choices.append(item["label"])
        self.choice = wx.Choice(parent=self, choices=choices)

        self.choice.Bind(wx.EVT_CHOICE, self.OnChangeChoice)
        self.parent.download_button.Bind(wx.EVT_BUTTON, self.OnDownload)
        # TODO: add button for a link to an associated website?
        # TODO: add thumbnail for each location?

        # TODO: messages copied from gis_set.py, need this as API?
        self.message = StaticText(parent=self, size=(-1, 50))
        sys.stdout = RedirectText(self.message)

        # It is not clear if all wx versions supports color, so try-except.
        # The color itself may not be correct for all platforms/system settings
        # but in http://xoomer.virgilio.it/infinity77/wxPython/Widgets/wx.SystemSettings.html
        # there is no 'warning' color.
        try:
            self.message.SetForegroundColour(wx.Colour(255, 0, 0))
        except AttributeError:
            pass

        self._layout()

        default = 0
        self.choice.SetSelection(default)
        self.CheckItem(self.locations[default])

        self.thread = gThread()

    def _layout(self):
        """Create and layout sizers"""
        vertical = wx.BoxSizer(wx.VERTICAL)
        self.sizer = vertical

        vertical.Add(
            self.label,
            proportion=0,
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=10,
        )
        vertical.Add(
            self.choice,
            proportion=0,
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=10,
        )
        vertical.AddStretchSpacer()
        vertical.Add(
            self.message,
            proportion=0,
            flag=wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
            border=10,
        )

        self.SetSizer(vertical)
        vertical.Fit(self)
        self.Layout()
        self.SetMinSize(self.GetBestSize())

    def _change_download_btn_label(self,
                                   label=_("Do&wnload"),
                                   tooltip=_("Download selected location")):
        """Change download button label/tooltip"""
        if self.parent.download_button:
            self.parent.download_button.SetLabel(label)
            self.parent.download_button.SetToolTip(tooltip)

    def OnDownload(self, event):
        """Handle user-initiated action of download"""
        button_label = self.parent.download_button.GetLabel()
        if button_label in (_("Download"), _("Do&wnload")):
            self._change_download_btn_label(
                label=self._abort_btn_label,
                tooltip=self._abort_btn_tooltip,
            )
            Debug.msg(1, "OnDownload")
            if self._download_in_progress:
                self._warning(
                    _("Download in progress, wait until it is finished"))
            index = self.choice.GetSelection()
            self.DownloadItem(self.locations[index])
        else:
            self.parent.OnCancel()

    def DownloadItem(self, item):
        """Download the selected item"""
        Debug.msg(1, "DownloadItem: %s" % item)
        # similar code as in CheckItem
        url = item["url"]
        dirname = location_name_from_url(url)
        destination = os.path.join(self.database, dirname)
        if os.path.exists(destination):
            self._error(
                _("Location named <%s> already exists,"
                  " download canceled") % dirname)
            self._change_download_btn_label()
            return

        def download_complete_callback(event):
            self._download_in_progress = False
            errors = event.ret
            if errors:
                self._error(_("Download failed: %s") % errors)
            else:
                self._last_downloaded_location_name = dirname
                self._warning(
                    _("Download completed. The downloaded sample data is listed "
                      "in the location/mapset tabs upon closing of this window"
                      ))
            self._change_download_btn_label()

        def terminate_download_callback(event):
            # Clean up after urllib urlretrieve which is used internally
            # in grass.utils.
            from urllib import request  # pylint: disable=import-outside-toplevel

            self._download_in_progress = False
            request.urlcleanup()
            sys.stdout.write("Download aborted")
            self.thread = gThread()
            self._change_download_btn_label()

        self._download_in_progress = True
        self._warning(_("Download in progress, wait until it is finished"))
        self.thread.Run(
            callable=download_location,
            url=url,
            name=dirname,
            database=self.database,
            ondone=download_complete_callback,
            onterminate=terminate_download_callback,
        )

    def OnChangeChoice(self, event):
        """React to user changing the selection"""
        index = self.choice.GetSelection()
        self.CheckItem(self.locations[index])

    def CheckItem(self, item):
        """Check what user selected and report potential issues"""
        # similar code as in DownloadItem
        url = item["url"]
        dirname = location_name_from_url(url)
        destination = os.path.join(self.database, dirname)
        if os.path.exists(destination):
            self._warning(
                _("Location named <%s> already exists,"
                  " rename it first") % dirname)
            self.parent.download_button.SetLabel(label=_("Download"))
            return
        else:
            self._clearMessage()

    def GetLocation(self):
        """Get the name of the last location downloaded by the user"""
        return self._last_downloaded_location_name

    def _warning(self, text):
        """Displays a warning, hint or info message to the user.

        This function can be used for all kinds of messages except for
        error messages.

        .. note::
            There is no cleaning procedure. You should call
            _clearMessage() when you know that there is everything
            correct.
        """
        sys.stdout.write(text)
        self.sizer.Layout()

    def _error(self, text):
        """Displays a error message to the user.

        This function should be used only when something serious and unexpected
        happens, otherwise _showWarning should be used.

        .. note::
            There is no cleaning procedure. You should call
            _clearMessage() when you know that there is everything
            correct.
        """
        sys.stdout.write(_("Error: {text}").format(text=text))
        self.sizer.Layout()

    def _clearMessage(self):
        """Clears/hides the error message."""
        # we do no hide widget
        # because we do not want the dialog to change the size
        self.message.SetLabel("")
        self.sizer.Layout()