Example #1
0
class ExportCategoryRaster(wx.Dialog):

    def __init__(self, parent, title, rasterName=None, id=wx.ID_ANY,
                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
                 **kwargs):
        """Dialog for export of category raster.

        :param parent: window
        :param str rasterName name of vector layer for export
        :param title: window title
        """
        wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)

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

        self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
        self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
        self.btnOK.SetDefault()
        self.btnOK.Enable(False)
        self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)

        self.__layout()

        self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
        self.OnTextChanged(None)
        wx.CallAfter(self.vectorNameCtrl.SetFocus)

    def OnTextChanged(self, event):
        """Name of new vector map given.

        Enable/diable OK button.
        """
        file = self.vectorNameCtrl.GetValue()
        if len(file) > 0:
            self.btnOK.Enable(True)
        else:
            self.btnOK.Enable(False)

    def __layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer.Add(
            StaticText(
                parent=self.panel,
                id=wx.ID_ANY,
                label=_("Enter name of new vector map:")),
            proportion=0,
            flag=wx.ALL,
            border=3)
        self.vectorNameCtrl = Select(parent=self.panel, type='raster',
                                     mapsets=[grass.gisenv()['MAPSET']],
                                     size=globalvar.DIALOG_GSELECT_SIZE)
        if self.rasterName:
            self.vectorNameCtrl.SetValue(self.rasterName)
        dataSizer.Add(self.vectorNameCtrl,
                      proportion=0, flag=wx.ALL | wx.EXPAND, border=3)

        # buttons
        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnOK)
        btnSizer.Realize()

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

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

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

        self.SetMinSize(self.GetSize())

    def GetRasterName(self):
        """Returns vector name"""
        return self.vectorNameCtrl.GetValue()

    def OnOK(self, event):
        """Checks if map exists and can be overwritten."""
        overwrite = UserSettings.Get(
            group='cmd', key='overwrite', subkey='enabled')
        rast_name = self.GetRasterName()
        res = grass.find_file(rast_name, element='cell')
        if res['fullname'] and overwrite is False:
            qdlg = wx.MessageDialog(
                parent=self, message=_(
                    "Raster map <%s> already exists."
                    " Do you want to overwrite it?" %
                    rast_name), caption=_(
                    "Raster <%s> exists" %
                    rast_name), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
            if qdlg.ShowModal() == wx.ID_YES:
                event.Skip()
            qdlg.Destroy()
        else:
            event.Skip()
Example #2
0
class WSPropertiesDialog(WSDialogBase):
    """Dialog for editing web service properties."""
    def __init__(
        self,
        parent,
        giface,
        layer,
        ws_cap_files,
        cmd,
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
        **kwargs,
    ):
        """
        :param giface: grass interface
        :param layer: layer tree item
        :param ws_cap_files: dict web service('WMS_1.1.1', 'WMS_1.3.0',
        'WMTS', 'OnEarth') : cap file path cap files, which will be parsed
        :param cmd: cmd to which dialog widgets will be initialized if
        it is possible (cmp parameters exists in parsed web service cap_file)
        """

        WSDialogBase.__init__(
            self,
            parent,
            id=wx.ID_ANY,
            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
            **kwargs,
        )

        self.SetTitle(_("Web service layer properties"))

        self.layer = layer
        self.giface = giface

        # after web service panels are connected, set dialog widgets
        # according to cmd in this variable (if it is not None)
        self.cmd_to_set = None

        # store data needed for reverting
        self.revert_ws_cap_files = {}
        self.revert_cmd = cmd

        ws_cap = self._getWSfromCmd(cmd)
        for ws in six.iterkeys(self.ws_panels):
            # cap file used in cmd will be deleted, thnaks to the dialogs
            # destructor
            if ws == ws_cap and "capfile" in cmd[1]:
                self.revert_ws_cap_files[ws] = cmd[1]["capfile"]
                del ws_cap_files[ws]
            else:
                self.revert_ws_cap_files[ws] = grass.tempfile()

        self._setRevertCapFiles(ws_cap_files)

        self.LoadCapFiles(ws_cap_files=self.revert_ws_cap_files, cmd=cmd)
        self.btn_ok.SetDefault()

    def __del__(self):
        for f in six.itervalues(self.revert_ws_cap_files):
            grass.try_remove(f)

    def _setRevertCapFiles(self, ws_cap_files):

        for ws, f in six.iteritems(ws_cap_files):
            if os.path.isfile(ws_cap_files[ws]):
                shutil.copyfile(f, self.revert_ws_cap_files[ws])
            else:
                # delete file content
                f_o = open(f, "w")
                f_o.close()

    def _createWidgets(self):

        WSDialogBase._createWidgets(self)

        self.btn_apply = Button(parent=self, id=wx.ID_ANY, label=_("&Apply"))
        self.btn_apply.SetToolTip(_("Apply changes"))
        self.btn_apply.Enable(False)
        self.run_btns.append(self.btn_apply)

        self.btn_ok = Button(parent=self, id=wx.ID_ANY, label=_("&OK"))
        self.btn_ok.SetToolTip(_("Apply changes and close dialog"))
        self.btn_ok.Enable(False)
        self.run_btns.append(self.btn_ok)

    def _doLayout(self):

        WSDialogBase._doLayout(self)

        self.btnsizer.Add(self.btn_apply,
                          proportion=0,
                          flag=wx.ALL | wx.ALIGN_CENTER,
                          border=10)

        self.btnsizer.Add(self.btn_ok,
                          proportion=0,
                          flag=wx.ALL | wx.ALIGN_CENTER,
                          border=10)

        # bindings
        self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
        self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave)

    def LoadCapFiles(self, ws_cap_files, cmd):
        """Parse cap files and update dialog.

        For parameters description, see the constructor.
        """
        self.ch_ws_sizer.Clear(True)

        self.cmd_to_set = cmd

        self.finished_panels_num = 0

        conn = self._getServerConnFromCmd(cmd)

        self.server.SetValue(conn["url"])
        self.password.SetValue(conn["password"])
        self.username.SetValue(conn["username"])

        self.layerName.SetValue(cmd[1]["map"])

        for ws, data in six.iteritems(self.ws_panels):
            cap_file = None

            if ws in ws_cap_files:
                cap_file = ws_cap_files[ws]

            data["panel"].ParseCapFile(
                url=conn["url"],
                username=conn["password"],
                password=conn["username"],
                cap_file=cap_file,
            )

    def _getServerConnFromCmd(self, cmd):
        """Get url/server/passwod from cmd tuple"""
        conn = {"url": "", "username": "", "password": ""}

        for k in six.iterkeys(conn):
            if k in cmd[1]:
                conn[k] = cmd[1][k]
        return conn

    def _apply(self):
        """Apply chosen values from widgets to web service layer."""
        lcmd = self.active_ws_panel.CreateCmd()
        if not lcmd:
            return

        active_ws = self.active_ws_panel.GetWebService()
        if "WMS" not in active_ws:
            lcmd.append("capfile=" + self.revert_ws_cap_files[active_ws])

        self.giface.GetLayerTree().GetOptData(dcmd=lcmd,
                                              layer=self.layer,
                                              params=True,
                                              propwin=self)

        # TODO use just list or tuple
        cmd = cmdlist_to_tuple(lcmd)
        self.revert_cmd = cmd
        self._setRevertCapFiles(self._getCapFiles())

        self.giface.updateMap.emit()

    def UpdateDialogAfterConnection(self):
        """Connect to the server"""
        WSDialogBase.UpdateDialogAfterConnection(self)
        if self._getConnectedWS():
            self.btn_ok.SetDefault()
        else:
            self.btn_connect.SetDefault()

    def OnApply(self, event):
        self._apply()

    def OnSave(self, event):
        self._apply()
        self._close()

    def OnClose(self, event):
        """Close dialog"""
        self._close()

    def _close(self):
        """Hide dialog"""
        self.Hide()
        self.LoadCapFiles(cmd=self.revert_cmd,
                          ws_cap_files=self.revert_ws_cap_files)

    def OnPanelCapParsed(self, error_msg):
        """Called when panel has downloaded and parsed capabilities file."""
        WSDialogBase.OnPanelCapParsed(self, error_msg)

        if self.finished_panels_num == len(self.ws_panels):
            if self.cmd_to_set:
                self._updateWsPanelWidgetsByCmd(self.cmd_to_set)
                self.cmd_to_set = None

    def _updateWsPanelWidgetsByCmd(self, cmd):
        """Set values of  widgets according to parameters in cmd."""

        ws = self._getWSfromCmd(cmd)
        if self.ws_panels[ws]["panel"].IsConnected():
            self.choose_ws_rb.SetStringSelection(self.ws_panels[ws]["label"])
            self._showWsPanel(ws)
            self.ws_panels[ws]["panel"].UpdateWidgetsByCmd(cmd)

    def _getWSfromCmd(self, cmd):
        driver = cmd[1]["driver"]
        ws = driver.split("_")[0]

        if ws == "WMS":
            ws += "_" + cmd[1]["wms_version"]
        return ws
Example #3
0
class ModelRelationDialog(wx.Dialog):
    """Relation properties dialog"""

    def __init__(self, parent, shape, id=wx.ID_ANY,
                 title=_("Relation properties"),
                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
        self.parent = parent
        self.shape = shape

        options = self._getOptions()
        if not options:
            self.valid = False
            return

        self.valid = True
        wx.Dialog.__init__(self, parent, id, title, style=style, **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.fromBox = StaticBox(parent=self.panel, id=wx.ID_ANY,
                                 label=" %s " % _("From"))
        self.toBox = StaticBox(parent=self.panel, id=wx.ID_ANY,
                               label=" %s " % _("To"))

        self.option = wx.ComboBox(parent=self.panel, id=wx.ID_ANY,
                                  style=wx.CB_READONLY,
                                  choices=options)
        self.option.Bind(wx.EVT_COMBOBOX, self.OnOption)

        self.btnCancel = Button(self.panel, wx.ID_CANCEL)
        self.btnOk = Button(self.panel, wx.ID_OK)
        self.btnOk.Enable(False)

        self._layout()

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

        fromSizer = wx.StaticBoxSizer(self.fromBox, wx.VERTICAL)
        self._layoutShape(shape=self.shape.GetFrom(), sizer=fromSizer)
        toSizer = wx.StaticBoxSizer(self.toBox, wx.VERTICAL)
        self._layoutShape(shape=self.shape.GetTo(), sizer=toSizer)

        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnOk)
        btnSizer.Realize()

        mainSizer.Add(fromSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL, border=5)
        mainSizer.Add(toSizer, proportion=0, flag=wx.EXPAND |
                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
        mainSizer.Add(btnSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)

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

        self.Layout()
        self.SetSize(self.GetBestSize())

    def _layoutShape(self, shape, sizer):
        if isinstance(shape, ModelData):
            sizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
                                 label=_("Data: %s") % shape.GetLog()),
                      proportion=1, flag=wx.EXPAND | wx.ALL,
                      border=5)
        elif isinstance(shape, ModelAction):
            gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
            gridSizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
                                     label=_("Command:")),
                          pos=(0, 0))
            gridSizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
                                     label=shape.GetLabel()),
                          pos=(0, 1))
            gridSizer.Add(StaticText(parent=self.panel, id=wx.ID_ANY,
                                     label=_("Option:")),
                          flag=wx.ALIGN_CENTER_VERTICAL,
                          pos=(1, 0))
            gridSizer.Add(self.option,
                          pos=(1, 1))
            sizer.Add(gridSizer,
                      proportion=1, flag=wx.EXPAND | wx.ALL,
                      border=5)

    def _getOptions(self):
        """Get relevant options"""
        items = []
        fromShape = self.shape.GetFrom()
        if not isinstance(fromShape, ModelData):
            GError(parent=self.parent,
                   message=_("Relation doesn't start with data item.\n"
                             "Unable to add relation."))
            return items

        toShape = self.shape.GetTo()
        if not isinstance(toShape, ModelAction):
            GError(parent=self.parent,
                   message=_("Relation doesn't point to GRASS command.\n"
                             "Unable to add relation."))
            return items

        prompt = fromShape.GetPrompt()
        task = toShape.GetTask()
        for p in task.get_options()['params']:
            if p.get('prompt', '') == prompt and \
                    'name' in p:
                items.append(p['name'])

        if not items:
            GError(parent=self.parent,
                   message=_("No relevant option found.\n"
                             "Unable to add relation."))
        return items

    def GetOption(self):
        """Get selected option"""
        return self.option.GetStringSelection()

    def IsValid(self):
        """Check if relation is valid"""
        return self.valid

    def OnOption(self, event):
        """Set option"""
        if event.GetString():
            self.btnOk.Enable()
        else:
            self.btnOk.Enable(False)
Example #4
0
class WSDialogBase(wx.Dialog):
    """Base class for web service dialogs."""
    def __init__(
        self,
        parent,
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
        **kwargs,
    ):

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

        self.parent = parent

        # contains panel for every web service on server
        self.ws_panels = {
            "WMS_1.1.1": {
                "panel": None,
                "label": "WMS 1.1.1"
            },
            "WMS_1.3.0": {
                "panel": None,
                "label": "WMS 1.3.0"
            },
            "WMTS": {
                "panel": None,
                "label": "WMTS"
            },
            "OnEarth": {
                "panel": None,
                "label": "OnEarth"
            },
        }

        # TODO: should be in file
        self.default_servers = {
            "OSM-WMS-EUROPE": [
                "http://watzmann-geog.urz.uni-heidelberg.de/cached/osm",
                "",
                "",
            ],
            "irs.gis-lab.info (OSM)": ["http://irs.gis-lab.info", "", ""],
            "NASA GIBS WMTS": [
                "http://gibs.earthdata.nasa.gov/wmts/epsg4326/best/wmts.cgi",
                "",
                "",
            ],
        }

        # holds reference to web service panel which is showed
        self.active_ws_panel = None

        # buttons which are disabled when the dialog is not connected
        self.run_btns = []

        # stores error messages for GError dialog showed when all web service
        # connections were unsuccessful
        self.error_msgs = ""

        self._createWidgets()
        self._doLayout()

    def _createWidgets(self):

        settingsFile = os.path.join(GetSettingsPath(), "wxWS")

        self.settsManager = WSManageSettingsWidget(
            parent=self,
            settingsFile=settingsFile,
            default_servers=self.default_servers)

        self.settingsBox = StaticBox(parent=self,
                                     id=wx.ID_ANY,
                                     label=_(" Server settings "))

        self.serverText = StaticText(parent=self,
                                     id=wx.ID_ANY,
                                     label=_("Server:"))
        self.server = TextCtrl(parent=self, id=wx.ID_ANY)

        self.btn_connect = Button(parent=self,
                                  id=wx.ID_ANY,
                                  label=_("&Connect"))
        self.btn_connect.SetToolTip(_("Connect to the server"))
        if not self.server.GetValue():
            self.btn_connect.Enable(False)

        self.infoCollapseLabelExp = _("Show advanced connection settings")
        self.infoCollapseLabelCol = _("Hide advanced connection settings")

        self.adv_conn = wx.CollapsiblePane(
            parent=self,
            label=self.infoCollapseLabelExp,
            style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND,
        )

        self.MakeAdvConnPane(pane=self.adv_conn.GetPane())
        self.adv_conn.Collapse(True)
        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnAdvConnPaneChanged,
                  self.adv_conn)

        self.reqDataPanel = wx.Panel(parent=self, id=wx.ID_ANY)

        self.layerNameBox = StaticBox(parent=self.reqDataPanel,
                                      id=wx.ID_ANY,
                                      label=_(" Layer Manager Settings "))

        self.layerNameText = StaticText(parent=self.reqDataPanel,
                                        id=wx.ID_ANY,
                                        label=_("Output layer name:"))
        self.layerName = TextCtrl(parent=self.reqDataPanel, id=wx.ID_ANY)

        for ws in six.iterkeys(self.ws_panels):
            # set class WSPanel argument layerNameTxtCtrl
            self.ws_panels[ws]["panel"] = WSPanel(parent=self.reqDataPanel,
                                                  web_service=ws)
            self.ws_panels[ws]["panel"].capParsed.connect(
                self.OnPanelCapParsed)
            self.ws_panels[ws]["panel"].layerSelected.connect(
                self.OnLayerSelected)

        # buttons
        self.btn_close = Button(parent=self, id=wx.ID_CLOSE)
        self.btn_close.SetToolTip(_("Close dialog"))

        # statusbar
        self.statusbar = wx.StatusBar(parent=self, id=wx.ID_ANY)

        # bindings
        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect)

        self.server.Bind(wx.EVT_TEXT, self.OnServer)
        self.layerName.Bind(wx.EVT_TEXT, self.OnOutputLayerName)

        self.settsManager.settingsChanged.connect(self.OnSettingsChanged)
        self.settsManager.settingsSaving.connect(self.OnSettingsSaving)

    def OnLayerSelected(self, title):
        self.layerName.SetValue(title)

    def _doLayout(self):

        dialogSizer = wx.BoxSizer(wx.VERTICAL)

        dialogSizer.Add(
            self.settsManager,
            proportion=0,
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=5,
        )

        # connectin settings
        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.VERTICAL)

        serverSizer = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)

        serverSizer.Add(self.serverText, flag=wx.ALIGN_CENTER_VERTICAL)
        serverSizer.AddGrowableCol(1)
        serverSizer.Add(self.server, flag=wx.EXPAND | wx.ALL)

        serverSizer.Add(self.btn_connect)

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

        settingsSizer.Add(self.adv_conn, flag=wx.ALL | wx.EXPAND, border=5)

        dialogSizer.Add(
            settingsSizer,
            proportion=0,
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=5,
        )

        # layer name, parsed capabilities

        reqDataSizer = wx.BoxSizer(wx.VERTICAL)

        layerNameSizer = wx.StaticBoxSizer(self.layerNameBox, wx.HORIZONTAL)

        layerNameSizer.Add(self.layerNameText,
                           flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                           border=5)

        layerNameSizer.Add(self.layerName, flag=wx.EXPAND, proportion=1)

        reqDataSizer.Add(layerNameSizer,
                         flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND,
                         border=5)

        self.ch_ws_sizer = wx.BoxSizer(wx.VERTICAL)

        reqDataSizer.Add(self.ch_ws_sizer,
                         proportion=0,
                         flag=wx.TOP | wx.EXPAND,
                         border=5)

        for ws in six.iterkeys(self.ws_panels):
            reqDataSizer.Add(
                self.ws_panels[ws]["panel"],
                proportion=1,
                flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND,
                border=5,
            )
            self.ws_panels[ws]["panel"].Hide()

        dialogSizer.Add(self.reqDataPanel, proportion=1, flag=wx.EXPAND)

        self.reqDataPanel.SetSizer(reqDataSizer)
        self.reqDataPanel.Hide()

        # buttons
        self.btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)

        self.btnsizer.Add(self.btn_close,
                          proportion=0,
                          flag=wx.ALL | wx.ALIGN_CENTER,
                          border=10)

        dialogSizer.Add(self.btnsizer, proportion=0, flag=wx.ALIGN_CENTER)

        # expand wxWidget wx.StatusBar
        statusbarSizer = wx.BoxSizer(wx.HORIZONTAL)
        statusbarSizer.Add(self.statusbar, proportion=1, flag=wx.EXPAND)
        dialogSizer.Add(statusbarSizer, proportion=0, flag=wx.EXPAND)

        self.SetSizer(dialogSizer)
        self.Layout()

        self.SetMinSize((550, -1))
        self.SetMaxSize((-1, self.GetBestSize()[1]))
        self.Fit()

    def MakeAdvConnPane(self, pane):
        """Create advanced connection settings pane"""
        self.usernameText = StaticText(parent=pane,
                                       id=wx.ID_ANY,
                                       label=_("Username:"******"Password:"******"""Check if required data are filled before setting save is performed."""
        server = self.server.GetValue().strip()
        if not server:
            GMessage(
                parent=self,
                message=_("No data source defined, settings are not saved."),
            )
            return

        self.settsManager.SetDataToSave(
            (server, self.username.GetValue(), self.password.GetValue()))
        self.settsManager.SaveSettings(name)

    def OnSettingsChanged(self, data):
        """Update widgets according to chosen settings"""
        # data list: [server, username, password]
        if len(data) < 3:
            return

        self.server.SetValue(data[0])

        self.username.SetValue(data[1])
        self.password.SetValue(data[2])

        if data[1] or data[2]:
            self.adv_conn.Expand()
        else:
            self.adv_conn.Collapse(True)

        # clear content of the wxWidget wx.TextCtrl (Output layer
        # name:), based on changing default server selection in the
        # wxWidget wx.Choice
        if len(self.layerName.GetValue()) > 0:
            self.layerName.Clear()

    def OnClose(self, event):
        """Close the dialog"""
        """Close dialog"""
        if not self.IsModal():
            self.Destroy()
        event.Skip()

    def _getCapFiles(self):
        ws_cap_files = {}
        for v in six.itervalues(self.ws_panels):
            ws_cap_files[v["panel"].GetWebService()] = v["panel"].GetCapFile()

        return ws_cap_files

    def OnServer(self, event):
        """Server settings edited"""
        value = event.GetString()
        if value:
            self.btn_connect.Enable(True)
        else:
            self.btn_connect.Enable(False)

        # clear content of the wxWidget wx.TextCtrl (Output Layer
        # name:), based on changing content of the wxWidget
        # wx.TextCtrl (Server:)
        self.layerName.Clear()

    def OnOutputLayerName(self, event):
        """Update layer name to web service panel"""
        lname = event.GetString()
        for v in six.itervalues(self.ws_panels):
            v["panel"].SetOutputLayerName(lname.strip())

    def OnConnect(self, event):
        """Connect to the server"""
        server = self.server.GetValue().strip()

        self.ch_ws_sizer.Clear(True)

        if self.active_ws_panel is not None:
            self.reqDataPanel.Hide()
            for btn in self.run_btns:
                btn.Enable(False)
            self.active_ws_panel = None

            self.Layout()
            self.Fit()

        self.statusbar.SetStatusText(
            _("Connecting to <%s>..." % self.server.GetValue().strip()))

        # number of panels already connected
        self.finished_panels_num = 0
        for ws in six.iterkeys(self.ws_panels):
            self.ws_panels[ws]["panel"].ConnectToServer(
                url=server,
                username=self.username.GetValue(),
                password=self.password.GetValue(),
            )
            self.ws_panels[ws]["panel"].Hide()

    def OnPanelCapParsed(self, error_msg):
        """Called when panel has downloaded and parsed capabilities file."""
        # how many web service panels are finished
        self.finished_panels_num += 1

        if error_msg:
            self.error_msgs += "\n" + error_msg

        # if all are finished, show panels, which succeeded in connection
        if self.finished_panels_num == len(self.ws_panels):
            self.UpdateDialogAfterConnection()

            # show error dialog only if connections to all web services were
            # unsuccessful
            if not self._getConnectedWS() and self.error_msgs:
                GError(self.error_msgs, parent=self)
            self.error_msgs = ""

            self.Layout()
            self.Fit()

    def _getConnectedWS(self):
        """
        :return: list of found web services on server (identified as keys in self.ws_panels)
        """
        conn_ws = []
        for ws, data in six.iteritems(self.ws_panels):
            if data["panel"].IsConnected():
                conn_ws.append(ws)

        return conn_ws

    def UpdateDialogAfterConnection(self):
        """Update dialog after all web service panels downloaded and parsed capabilities data."""
        avail_ws = {}
        conn_ws = self._getConnectedWS()

        for ws in conn_ws:
            avail_ws[ws] = self.ws_panels[ws]

        self.web_service_sel = []
        self.rb_choices = []

        # at least one web service found on server
        if len(avail_ws) > 0:
            self.reqDataPanel.Show()
            self.rb_order = ["WMS_1.1.1", "WMS_1.3.0", "WMTS", "OnEarth"]

            for ws in self.rb_order:

                if ws in avail_ws:
                    self.web_service_sel.append(ws)
                    self.rb_choices.append(avail_ws[ws]["label"])

            self.choose_ws_rb = wx.RadioBox(
                parent=self.reqDataPanel,
                id=wx.ID_ANY,
                label=_("Available web services"),
                pos=wx.DefaultPosition,
                choices=self.rb_choices,
                majorDimension=1,
                style=wx.RA_SPECIFY_ROWS,
            )

            self.Bind(wx.EVT_RADIOBOX, self.OnChooseWs, self.choose_ws_rb)
            self.ch_ws_sizer.Add(
                self.choose_ws_rb,
                flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND,
                border=5,
            )
            self._showWsPanel(
                self.web_service_sel[self.choose_ws_rb.GetSelection()])
            self.statusbar.SetStatusText(
                _("Connected to <%s>" % self.server.GetValue().strip()))
            for btn in self.run_btns:
                btn.Enable(True)
        # no web service found on server
        else:
            self.statusbar.SetStatusText(
                _("Unable to connect to <%s>" %
                  self.server.GetValue().strip()))
            for btn in self.run_btns:
                btn.Enable(False)
            self.reqDataPanel.Hide()
            self.active_ws_panel = None

    def OnChooseWs(self, event):
        """Show panel corresponding to selected web service."""
        choosen_r = event.GetInt()
        self._showWsPanel(self.web_service_sel[choosen_r])

    def _showWsPanel(self, ws):
        """Helper function"""
        if self.active_ws_panel is not None:
            self.active_ws_panel.Hide()

        self.active_ws_panel = self.ws_panels[ws]["panel"]
        if not self.active_ws_panel.IsShown():
            self.active_ws_panel.Show()
            self.SetMaxSize((-1, -1))
            self.active_ws_panel.GetContainingSizer().Layout()

    def OnAdvConnPaneChanged(self, event):
        """Collapse search module box"""
        if self.adv_conn.IsExpanded():
            self.adv_conn.SetLabel(self.infoCollapseLabelCol)
        else:
            self.adv_conn.SetLabel(self.infoCollapseLabelExp)

        self.Layout()
        self.SetMaxSize((-1, self.GetBestSize()[1]))
        self.SendSizeEvent()
        self.Fit()
Example #5
0
class AddWSDialog(WSDialogBase):
    """Dialog for adding web service layer."""
    def __init__(
        self,
        parent,
        giface,
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
        **kwargs,
    ):

        WSDialogBase.__init__(
            self,
            parent,
            id=wx.ID_ANY,
            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
            **kwargs,
        )

        self.SetTitle(_("Add web service layer"))

        self.parent = parent
        self.giface = giface
        self.btn_connect.SetDefault()

    def _createWidgets(self):

        WSDialogBase._createWidgets(self)

        self.btn_add = Button(parent=self, id=wx.ID_ANY, label=_("&Add layer"))
        self.btn_add.SetToolTip(
            _("Add selected web service layers as map layer into layer tree"))
        self.btn_add.Enable(False)

        self.run_btns.append(self.btn_add)

    def _doLayout(self):

        WSDialogBase._doLayout(self)

        self.btnsizer.Add(self.btn_add,
                          proportion=0,
                          flag=wx.ALL | wx.ALIGN_CENTER,
                          border=10)

        # bindings
        self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddLayer)

    def UpdateDialogAfterConnection(self):
        """Connect to the server"""
        WSDialogBase.UpdateDialogAfterConnection(self)

        if self._getConnectedWS():
            self.btn_add.SetDefault()
        else:
            self.btn_connect.SetDefault()

    def OnAddLayer(self, event):
        """Add web service layer."""
        # add layer
        if self.active_ws_panel is None:
            return

        lcmd = self.active_ws_panel.CreateCmd()
        if not lcmd:
            return None

        # TODO: It is not clear how to do GetOptData in giface
        # knowing what GetOptData is doing might help
        # (maybe Get... is not the right name)
        # please fix giface if you know
        # tree -> giface
        # GetLayerTree -> GetLayerList
        # AddLayer -> AddLayer (but tree ones returns some layer,
        # giface ones nothing)
        # GetLayerInfo -> Layer object can by used instead
        # GetOptData -> unknown
        ltree = self.giface.GetLayerTree()

        active_ws = self.active_ws_panel.GetWebService()
        if "WMS" not in active_ws:
            cap_file = self.active_ws_panel.GetCapFile()
            cmd_cap_file = grass.tempfile()
            shutil.copyfile(cap_file, cmd_cap_file)
            lcmd.append("capfile=" + cmd_cap_file)

        layer = ltree.AddLayer(
            ltype="wms",
            lname=self.active_ws_panel.GetOutputLayerName(),
            lchecked=True,
            lcmd=lcmd,
        )

        ws_cap_files = self._getCapFiles()
        # create properties dialog
        cmd_list = ltree.GetLayerInfo(layer, "cmd")
        cmd = cmdlist_to_tuple(cmd_list)

        prop_win = WSPropertiesDialog(
            parent=self.parent,
            giface=self.giface,
            id=wx.ID_ANY,
            layer=layer,
            ws_cap_files=ws_cap_files,
            cmd=cmd,
        )

        prop_win.Hide()
        ltree.GetOptData(dcmd=None, layer=layer, params=None, propwin=prop_win)
Example #6
0
class IClassSignatureFileDialog(wx.Dialog):
    def __init__(self,
                 parent,
                 group,
                 subgroup,
                 file=None,
                 title=_("Save signature file"),
                 id=wx.ID_ANY,
                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
                 **kwargs):
        """Dialog for saving signature file

        :param parent: window
        :param group: group name
        :param file: signature file name
        :param title: window title
        """
        wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)

        self.fileName = file

        env = grass.gisenv()

        # inconsistent group and subgroup name
        # path:
        # grassdata/nc_spm_08/landsat/group/test_group/subgroup/test_group/sig/sigFile
        self.baseFilePath = os.path.join(env['GISDBASE'], env['LOCATION_NAME'],
                                         env['MAPSET'], 'group', group,
                                         'subgroup', subgroup, 'sig')
        self.panel = wx.Panel(parent=self, id=wx.ID_ANY)

        self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
        self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
        self.btnOK.SetDefault()
        self.btnOK.Enable(False)

        self.__layout()

        self.fileNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
        self.OnTextChanged(None)

    def OnTextChanged(self, event):
        """Name for signature file given"""
        file = self.fileNameCtrl.GetValue()
        if len(file) > 0:
            self.btnOK.Enable(True)
        else:
            self.btnOK.Enable(False)

        path = os.path.join(self.baseFilePath, file)
        self.filePathText.SetLabel(path)
        bestSize = self.pathPanel.GetBestVirtualSize()
        self.pathPanel.SetVirtualSize(bestSize)
        self.pathPanel.Scroll(*bestSize)

    def __layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer.Add(StaticText(parent=self.panel,
                                 id=wx.ID_ANY,
                                 label=_("Enter name of signature file:")),
                      proportion=0,
                      flag=wx.ALL,
                      border=3)
        self.fileNameCtrl = TextCtrl(parent=self.panel,
                                     id=wx.ID_ANY,
                                     size=(400, -1))
        if self.fileName:
            self.fileNameCtrl.SetValue(self.fileName)
        dataSizer.Add(self.fileNameCtrl,
                      proportion=0,
                      flag=wx.ALL | wx.EXPAND,
                      border=3)

        dataSizer.Add(StaticText(parent=self.panel,
                                 id=wx.ID_ANY,
                                 label=_("Signature file path:")),
                      proportion=0,
                      flag=wx.ALL,
                      border=3)

        self.pathPanel = scrolled.ScrolledPanel(self.panel, size=(-1, 40))
        pathSizer = wx.BoxSizer()
        self.filePathText = StaticText(parent=self.pathPanel,
                                       id=wx.ID_ANY,
                                       label=self.baseFilePath)
        pathSizer.Add(self.filePathText,
                      proportion=1,
                      flag=wx.ALL | wx.EXPAND,
                      border=1)
        self.pathPanel.SetupScrolling(scroll_x=True, scroll_y=False)
        self.pathPanel.SetSizer(pathSizer)

        dataSizer.Add(self.pathPanel,
                      proportion=0,
                      flag=wx.ALL | wx.EXPAND,
                      border=3)

        # buttons
        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnOK)
        btnSizer.Realize()

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

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

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

        self.SetMinSize(self.GetSize())

    def GetFileName(self, fullPath=False):
        """Returns signature file name

        :param fullPath: return full path of sig. file
        """
        if fullPath:
            return os.path.join(self.baseFilePath,
                                self.fileNameCtrl.GetValue())

        return self.fileNameCtrl.GetValue()
Example #7
0
class AttributeManager(wx.Frame, DbMgrBase):
    def __init__(self,
                 parent,
                 id=wx.ID_ANY,
                 title=None,
                 vectorName=None,
                 item=None,
                 log=None,
                 selection=None,
                 **kwargs):
        """GRASS Attribute Table Manager window

        :param parent: parent window
        :param id: window id
        :param title: window title or None for default title
        :param vectorName: name of vector map
        :param item: item from Layer Tree
        :param log: log window
        :param selection: name of page to be selected
        :param kwagrs: other wx.Frame's arguments
        """
        self.parent = parent
        try:
            mapdisplay = self.parent.GetMapDisplay()
        except:
            mapdisplay = None

        DbMgrBase.__init__(self,
                           id=id,
                           mapdisplay=mapdisplay,
                           vectorName=vectorName,
                           item=item,
                           log=log,
                           statusbar=self,
                           **kwargs)

        wx.Frame.__init__(self, parent, id, *kwargs)

        # title
        if not title:
            title = "%s" % _("GRASS GIS Attribute Table Manager - ")
            if not self.dbMgrData['editable']:
                title += _("READONLY - ")
            title += "<%s>" % (self.dbMgrData['vectName'])

        self.SetTitle(title)

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

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

        if len(self.dbMgrData['mapDBInfo'].layers.keys()) == 0:
            GMessage(parent=self.parent,
                     message=_("Database connection for vector map <%s> "
                               "is not defined in DB file. "
                               "You can define new connection in "
                               "'Manage layers' tab.") %
                     self.dbMgrData['vectName'])

        busy = wx.BusyInfo(_("Please wait, loading attribute data..."),
                           parent=self.parent)
        wx.SafeYield()
        self.CreateStatusBar(number=1)

        self.notebook = GNotebook(self.panel, style=globalvar.FNPageDStyle)

        self.CreateDbMgrPage(parent=self, pageName='browse')

        self.notebook.AddPage(page=self.pages['browse'],
                              text=_("Browse data"),
                              name='browse')
        self.pages['browse'].SetTabAreaColour(globalvar.FNPageColor)

        self.CreateDbMgrPage(parent=self, pageName='manageTable')

        self.notebook.AddPage(page=self.pages['manageTable'],
                              text=_("Manage tables"),
                              name='table')
        self.pages['manageTable'].SetTabAreaColour(globalvar.FNPageColor)

        self.CreateDbMgrPage(parent=self, pageName='manageLayer')
        self.notebook.AddPage(page=self.pages['manageLayer'],
                              text=_("Manage layers"),
                              name='layers')
        del busy

        if selection:
            wx.CallAfter(self.notebook.SetSelectionByName, selection)
        else:
            wx.CallAfter(self.notebook.SetSelection, 0)  # select browse tab

        # buttons
        self.btnClose = CloseButton(parent=self.panel)
        self.btnClose.SetToolTip(_("Close Attribute Table Manager"))
        self.btnReload = Button(parent=self.panel, id=wx.ID_REFRESH)
        self.btnReload.SetToolTip(
            _("Reload currently selected attribute data"))
        self.btnReset = ClearButton(parent=self.panel)
        self.btnReset.SetToolTip(
            _("Reload all attribute data (drop current selection)"))

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

        # events
        self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
        self.btnReload.Bind(wx.EVT_BUTTON, self.OnReloadData)
        self.btnReset.Bind(wx.EVT_BUTTON, self.OnReloadDataAll)
        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,
                           self.OnPageChanged)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

        # do layout
        self._layout()

        # self.SetMinSize(self.GetBestSize())
        self.SetSize((700, 550))  # FIXME hard-coded size
        self.SetMinSize(self.GetSize())

    def _layout(self):
        """Do layout"""
        # frame body
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        # buttons
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.Add(self.btnReset, proportion=1, flag=wx.ALL, border=5)
        btnSizer.Add(self.btnReload, proportion=1, flag=wx.ALL, border=5)
        btnSizer.Add(self.btnClose, proportion=1, flag=wx.ALL, border=5)

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

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

    def OnCloseWindow(self, event):
        """Cancel button pressed"""
        if self.parent and self.parent.GetName() == 'LayerManager':
            # deregister ATM
            self.parent.dialogs['atm'].remove(self)

        if not isinstance(event, wx.CloseEvent):
            self.Destroy()

        event.Skip()

    def OnReloadData(self, event):
        """Reload data"""
        if self.pages['browse']:
            self.pages['browse'].OnDataReload(event)  # TODO replace by signal

    def OnReloadDataAll(self, event):
        """Reload all data"""
        if self.pages['browse']:
            self.pages['browse'].ResetPage()

    def OnPageChanged(self, event):
        """On page in ATM is changed"""
        try:
            if self.pages["browse"]:
                selPage = self.pages["browse"].selLayer
                id = self.pages["browse"].layerPage[selPage]['data']
            else:
                id = None
        except KeyError:
            id = None

        if event.GetSelection() == self.notebook.GetPageIndexByName(
                'browse') and id:
            win = self.FindWindowById(id)
            if win:
                self.log.write(
                    _("Number of loaded records: %d") % win.GetItemCount())
            else:
                self.log.write("")
            self.btnReload.Enable()
            self.btnReset.Enable()
        else:
            self.log.write("")
            self.btnReload.Enable(False)
            self.btnReset.Enable(False)

        event.Skip()

    def OnTextEnter(self, event):
        pass

    def UpdateDialog(self, layer):
        """Updates dialog layout for given layer"""
        DbMgrBase.UpdateDialog(self, layer=layer)
        # set current page selection
        self.notebook.SetSelectionByName('layers')
Example #8
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"))
Example #9
0
class InstallExtensionWindow(wx.Frame):
    def __init__(
            self,
            parent,
            giface,
            id=wx.ID_ANY,
            title=_("Fetch & install extension from GRASS Addons"),
            **kwargs,
    ):
        self.parent = parent
        self._giface = giface
        self.options = dict()  # list of options

        wx.Frame.__init__(self, parent=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.repoBox = StaticBox(
        #     parent=self.panel, id=wx.ID_ANY, label=" %s " %
        #     _("Repository (leave empty to use the official one)"))
        self.treeBox = StaticBox(
            parent=self.panel,
            id=wx.ID_ANY,
            label=" %s " % _("List of extensions - double-click to install"),
        )

        # self.repo = TextCtrl(parent=self.panel, id=wx.ID_ANY)

        # modelBuilder loads data into tree model
        self.modelBuilder = ExtensionTreeModelBuilder()
        # tree view displays model data
        self.tree = CTreeView(parent=self.panel,
                              model=self.modelBuilder.GetModel())

        self.search = SearchCtrl(self.panel)
        self.search.SetDescriptiveText(_("Search"))
        self.search.ShowCancelButton(True)
        # load data in different thread
        self.thread = gThread()

        self.optionBox = StaticBox(parent=self.panel,
                                   id=wx.ID_ANY,
                                   label=" %s " % _("Options"))
        task = gtask.parse_interface("g.extension")
        ignoreFlags = ["l", "c", "g", "a", "f", "t", "help", "quiet"]
        if sys.platform == "win32":
            ignoreFlags.append("d")
            ignoreFlags.append("i")

        for f in task.get_options()["flags"]:
            name = f.get("name", "")
            desc = f.get("label", "")
            if not desc:
                desc = f.get("description", "")
            if not name and not desc:
                continue
            if name in ignoreFlags:
                continue
            self.options[name] = wx.CheckBox(parent=self.panel,
                                             id=wx.ID_ANY,
                                             label=desc)
        # defaultUrl = ''  # default/official one will be used when option empty
        # self.repo.SetValue(
        #     task.get_param(
        #         value='url').get(
        #         'default',
        #         defaultUrl))

        self.statusbar = self.CreateStatusBar(number=1)

        # self.btnFetch = Button(parent=self.panel, id=wx.ID_ANY,
        #                        label=_("&Fetch"))
        # self.btnFetch.SetToolTip(_("Fetch list of available modules "
        #                            "from GRASS Addons repository"))
        self.btnClose = Button(parent=self.panel, id=wx.ID_CLOSE)
        self.btnInstall = Button(parent=self.panel,
                                 id=wx.ID_ANY,
                                 label=_("&Install"))
        self.btnInstall.SetToolTip(_("Install selected add-ons GRASS module"))
        self.btnInstall.Enable(False)
        self.btnHelp = Button(parent=self.panel, id=wx.ID_HELP)
        self.btnHelp.SetToolTip(_("Show g.extension manual page"))

        self.btnClose.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
        # self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
        self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
        self.btnHelp.Bind(wx.EVT_BUTTON, self.OnHelp)
        self.search.Bind(wx.EVT_TEXT, lambda evt: self.Filter(evt.GetString()))
        self.search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
                         lambda evt: self.Filter(""))
        self.tree.selectionChanged.connect(self.OnItemSelected)
        self.tree.itemActivated.connect(self.OnItemActivated)
        self.tree.contextMenu.connect(self.OnContextMenu)

        wx.CallAfter(self._fetch)

        self._layout()

    def _layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)
        # repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
        # repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
        # repo1Sizer.Add(self.repo, proportion=1,
        #                flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
        # repo1Sizer.Add(self.btnFetch, proportion=0,
        #                flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
        # repoSizer.Add(repo1Sizer,
        #               flag=wx.EXPAND)

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

        treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
        treeSizer.Add(self.tree,
                      proportion=1,
                      flag=wx.ALL | wx.EXPAND,
                      border=1)

        # options
        optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
        for key in self.options.keys():
            optionSizer.Add(self.options[key], proportion=0)

        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.Add(self.btnHelp, proportion=0)
        btnSizer.AddStretchSpacer()
        btnSizer.Add(self.btnClose, proportion=0, flag=wx.RIGHT, border=5)
        btnSizer.Add(self.btnInstall, proportion=0)

        # sizer.Add(repoSizer, proportion=0,
        #           flag=wx.ALL | wx.EXPAND, border=3)
        sizer.Add(
            treeSizer,
            proportion=1,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=3,
        )
        sizer.Add(
            optionSizer,
            proportion=0,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=3,
        )
        sizer.Add(btnSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)

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

        self.Layout()

    def _getCmd(self):
        item = self.tree.GetSelected()
        if not item or "command" not in item[0].data:
            GError(_("Extension not defined"), parent=self)
            return

        name = item[0].data["command"]

        flags = list()
        for key in self.options.keys():
            if self.options[key].IsChecked():
                if len(key) == 1:
                    flags.append("-%s" % key)
                else:
                    flags.append("--%s" % key)

        # 'url=' + self.repo.GetValue().strip()]
        return ["g.extension"] + flags + ["extension={}".format(name)]

    def OnFetch(self, event):
        """Fetch list of available extensions"""
        self._fetch()

    def _fetch(self):
        """Fetch list of available extensions"""
        wx.BeginBusyCursor()
        self.SetStatusText(
            _("Fetching list of modules from GRASS-Addons (be patient)..."), 0)
        try:
            self.thread.Run(
                callable=self.modelBuilder.Load,
                url="",  # self.repo.GetValue().strip(),
                ondone=lambda event: self._fetchDone(),
            )
        except GException as error:
            self._fetchDone()
            GError(str(error), parent=self, showTraceback=False)

    def _fetchDone(self):
        self.tree.RefreshItems()
        nitems = len(self.modelBuilder.GetModel().SearchNodes(key="command",
                                                              value="*"))
        self.SetStatusText(_("%d extensions loaded") % nitems, 0)
        wx.EndBusyCursor()

    def Filter(self, text):
        model = self.modelBuilder.GetModel()
        if text:
            model = model.Filtered(key=["command", "keywords", "description"],
                                   value=text)
            self.tree.SetModel(model)
            self.tree.ExpandAll()
        else:
            self.tree.SetModel(model)

    def OnContextMenu(self, node):
        if not hasattr(self, "popupID"):
            self.popupID = dict()
            for key in ("install", "help"):
                self.popupID[key] = NewId()

        data = node.data
        if data and "command" in data:
            self.popupMenu = Menu()
            self.popupMenu.Append(self.popupID["install"], _("Install"))
            self.Bind(wx.EVT_MENU, self.OnInstall, id=self.popupID["install"])
            self.popupMenu.AppendSeparator()
            self.popupMenu.Append(self.popupID["help"], _("Show manual page"))
            self.Bind(wx.EVT_MENU, self.OnItemHelp, id=self.popupID["help"])

            self.PopupMenu(self.popupMenu)
            self.popupMenu.Destroy()

    def OnItemActivated(self, node):
        data = node.data
        if data and "command" in data:
            self.OnInstall(event=None)

    def OnInstall(self, event):
        """Install selected extension"""
        log = self.parent.GetLogWindow()
        cmd = self._getCmd()
        if cmd:
            log.RunCmd(cmd, onDone=self.OnDone)

    def OnDone(self, event):
        if event.returncode == 0:
            if not os.getenv("GRASS_ADDON_BASE"):
                SetAddOnPath(key="BASE")

            globalvar.UpdateGRASSAddOnCommands()
            toolboxesOutdated()

    def OnItemHelp(self, event):
        item = self.tree.GetSelected()
        if not item or "command" not in item[0].data:
            return

        self._giface.Help(entry=item[0].data["command"], online=True)

    def OnHelp(self, event):
        self._giface.Help(entry="g.extension")

    def OnItemSelected(self, node):
        """Item selected"""
        data = node.data
        if data is None:
            self.SetStatusText("", 0)
            self.btnInstall.Enable(False)
        else:
            self.SetStatusText(data.get("description", ""), 0)
            self.btnInstall.Enable(True)
Example #10
0
class HelpPanel(wx.Panel):
    def __init__(self,
                 parent,
                 command="index",
                 text=None,
                 skipDescription=False,
                 **kwargs):
        self.command = command
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)

        self.content = HelpWindow(self, command, text, skipDescription)

        self.btnNext = Button(parent=self, id=wx.ID_ANY, label=_("&Next"))
        self.btnNext.Enable(False)
        self.btnPrev = Button(parent=self, id=wx.ID_ANY, label=_("&Previous"))
        self.btnPrev.Enable(False)

        self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
        self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)

        self._layout()

    def _layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)

        btnSizer.Add(self.btnPrev, proportion=0, flag=wx.ALL, border=5)
        btnSizer.Add(wx.Size(1, 1), proportion=1)
        btnSizer.Add(self.btnNext, proportion=0, flag=wx.ALL, border=5)

        sizer.Add(self.content, proportion=1, flag=wx.EXPAND)
        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND)

        self.SetSizer(sizer)
        sizer.Fit(self)

    def LoadPage(self, path=None):
        """Load page"""
        if not path:
            path = self.GetFile()
        self.content.history.append(path)
        self.content.LoadPage(path)

    def GetFile(self):
        """Get HTML file"""
        fMan = os.path.join(self.content.fspath, self.command + ".html")
        if os.path.isfile(fMan):
            return fMan

        # check also addons
        faMan = os.path.join(os.getenv('GRASS_ADDON_BASE'), "docs", "html",
                             self.command + ".html")
        if os.getenv('GRASS_ADDON_BASE') and \
                os.path.isfile(faMan):
            return faMan

        return None

    def IsLoaded(self):
        return self.content.loaded

    def OnHistory(self):
        """Update buttons"""
        nH = len(self.content.history)
        iH = self.content.historyIdx
        if iH == nH - 1:
            self.btnNext.Enable(False)
        elif iH > -1:
            self.btnNext.Enable(True)
        if iH < 1:
            self.btnPrev.Enable(False)
        else:
            self.btnPrev.Enable(True)

    def OnNext(self, event):
        """Load next page"""
        self.content.historyIdx += 1
        idx = self.content.historyIdx
        path = self.content.history[idx]
        self.content.LoadPage(path)
        self.OnHistory()

        event.Skip()

    def OnPrev(self, event):
        """Load previous page"""
        self.content.historyIdx -= 1
        idx = self.content.historyIdx
        path = self.content.history[idx]
        self.content.LoadPage(path)
        self.OnHistory()

        event.Skip()
Example #11
0
    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!")
Example #12
0
class SQLBuilderSelect(SQLBuilder):
    """Class for building SELECT SQL statement"""

    def __init__(self, parent, vectmap, id=wx.ID_ANY,
                 layer=1, evtHandler=None):

        self.evtHandler = evtHandler

        # set dialog title
        title = _("GRASS SQL Builder (%(type)s) - <%(map)s>") % \
            {'type': "SELECT", 'map': vectmap}

        modeChoices = [_("Column to show (SELECT clause)"),
                       _("Constraint for query (WHERE clause)")]

        SQLBuilder.__init__(self, parent, title, vectmap, id=wx.ID_ANY,
                            modeChoices=modeChoices, layer=layer)

    def _doLayout(self, modeChoices):
        """Do dialog layout"""

        SQLBuilder._doLayout(self, modeChoices)

        self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
        self.text_sql.SetToolTip(
            _("Example: %s") %
            "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")

        self.btn_verify = Button(parent=self.panel, id=wx.ID_ANY,
                                    label=_("Verify"))
        self.btn_verify.SetToolTip(_("Verify SQL statement"))

        self.buttonsizer.Insert(1, self.btn_verify)

        self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
        self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)

        self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition())
        self.statusbar.SetStatusText(_("SQL statement not verified"), 0)

    def OnApply(self, event):
        """Apply button pressed"""
        if self.evtHandler:
            self.evtHandler(event='apply')

        if self.close_onapply.IsChecked():
            self.Destroy()

        event.Skip()

    def OnClear(self, event):
        """Clear button pressed"""
        self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)

    def OnMode(self, event):
        """Adjusts builder for chosen mode"""
        if self.mode.GetSelection() == 0:
            self.valuespanel.Hide()
            self.btn_logicpanel.Hide()
        elif self.mode.GetSelection() == 1:
            self.valuespanel.Show()
            self.btn_logicpanel.Show()
        self.pagesizer.Layout()

    def OnText(self, event):
        """Query string changed"""
        if len(self.text_sql.GetValue()) > 0:
            self.btn_verify.Enable(True)
        else:
            self.btn_verify.Enable(False)

    def OnVerify(self, event):
        """Verify button pressed"""
        ret, msg = RunCommand('db.select',
                              getErrorMsg=True,
                              table=self.tablename,
                              sql=self.text_sql.GetValue(),
                              flags='t',
                              driver=self.driver,
                              database=self.database)

        if ret != 0 and msg:
            self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
            GError(parent=self,
                   message=_("SQL statement is not valid.\n\n%s") % msg)
        else:
            self.statusbar.SetStatusText(_("SQL statement is valid"), 0)

    def _add(self, element, value):
        """Add element to the query

        :param element: element to add (column, value)
        """
        sqlstr = self.text_sql.GetValue()
        curspos = self.text_sql.GetInsertionPoint()
        newsqlstr = ''
        if element == 'column':
            if self.mode.GetSelection() == 0:  # -> column
                idx1 = len('select')
                idx2 = sqlstr.lower().find('from')
                colstr = sqlstr[idx1:idx2].strip()
                if colstr == '*':
                    cols = []
                else:
                    cols = colstr.split(',')
                if value in cols:
                    cols.remove(value)
                else:
                    cols.append(value)

                if len(cols) < 1:
                    cols = ['*', ]
                newsqlstr = 'SELECT ' + ','.join(cols) + ' '
                curspos = len(newsqlstr)
                newsqlstr += sqlstr[idx2:]
            else:  # -> where
                newsqlstr = ''
                if sqlstr.lower().find('where') < 0:
                    newsqlstr += ' WHERE'
                newsqlstr += ' ' + value
                curspos = self.text_sql.GetLastPosition() + len(newsqlstr)
                newsqlstr = sqlstr + newsqlstr

        elif element in ['value', 'mark']:
            addstr = ' ' + value + ' '
            newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:]
            curspos += len(addstr)

        if newsqlstr:
            self.text_sql.SetValue(newsqlstr)

        wx.CallAfter(self.text_sql.SetFocus)
        self.text_sql.SetInsertionPoint(curspos)

    def CloseOnApply(self):
        """Return True if the dialog will be close on apply"""
        return self.close_onapply.IsChecked()

    def OnClose(self, event):
        """Close button pressed"""
        if self.evtHandler:
            self.evtHandler(event='close')

        SQLBuilder.OnClose(self, event)
Example #13
0
class SQLBuilder(wx.Frame):
    """SQLBuider class
    Base class for classes, which builds SQL statements.
    """

    def __init__(self, parent, title, vectmap, modeChoices=[], id=wx.ID_ANY,
                 layer=1):
        wx.Frame.__init__(self, parent, id, title)

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

        self.parent = parent

        # variables
        self.vectmap = vectmap  # fullname
        if not "@" in self.vectmap:
            self.vectmap = grass.find_file(
                self.vectmap, element='vector')['fullname']
            if not self.vectmap:
                grass.fatal(_("Vector map <%s> not found") % vectmap)
        self.mapname, self.mapset = self.vectmap.split("@", 1)

        # db info
        self.layer = layer
        self.dbInfo = VectorDBInfo(self.vectmap)
        self.tablename = self.dbInfo.GetTable(self.layer)
                
        self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)

        self.colvalues = []     # array with unique values in selected column

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

        # statusbar
        self.statusbar = self.CreateStatusBar(number=1)

        self._doLayout(modeChoices)

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

        self.SetMinSize((400, 600))
        self.SetClientSize(self.panel.GetSize())
        self.CenterOnParent()

    def _doLayout(self, modeChoices, showDbInfo=False):
        """Do dialog layout"""

        self.pagesizer = wx.BoxSizer(wx.VERTICAL)

        # dbInfo
        if showDbInfo:
            databasebox = StaticBox(parent=self.panel, id=wx.ID_ANY,
                                    label=" %s " % _("Database connection"))
            databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
            databaseboxsizer.Add(
                CreateDbInfoDesc(
                    self.panel,
                    self.dbInfo,
                    layer=self.layer),
                proportion=1,
                flag=wx.EXPAND | wx.ALL,
                border=3)
        
        #
        # text areas
        #
        # sql box
        sqlbox = StaticBox(parent=self.panel, id=wx.ID_ANY,
                           label=" %s " % _("Query"))
        sqlboxsizer = wx.StaticBoxSizer(sqlbox, wx.VERTICAL)

        self.text_sql = TextCtrl(parent=self.panel, id=wx.ID_ANY,
                                    value='', size=(-1, 50),
                                    style=wx.TE_MULTILINE)

        self.text_sql.SetInsertionPointEnd()
        wx.CallAfter(self.text_sql.SetFocus)

        sqlboxsizer.Add(self.text_sql, flag=wx.EXPAND)

        #
        # buttons
        #
        self.btn_clear = Button(parent=self.panel, id=wx.ID_CLEAR)
        self.btn_clear.SetToolTip(_("Set SQL statement to default"))
        self.btn_apply = Button(parent=self.panel, id=wx.ID_APPLY)
        self.btn_apply.SetToolTip(_("Apply SQL statement"))
        self.btn_close = Button(parent=self.panel, id=wx.ID_CLOSE)
        self.btn_close.SetToolTip(_("Close the dialog"))

        self.btn_logic = {'is': ['=', ],
                          'isnot': ['!=', ],
                          'like': ['LIKE', ],
                          'gt': ['>', ],
                          'ge': ['>=', ],
                          'lt': ['<', ],
                          'le': ['<=', ],
                          'or': ['OR', ],
                          'not': ['NOT', ],
                          'and': ['AND', ],
                          'brac': ['()', ],
                          'prc': ['%', ]}

        self.btn_logicpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY)
        for key, value in six.iteritems(self.btn_logic):
            btn = Button(parent=self.btn_logicpanel, id=wx.ID_ANY,
                            label=value[0])
            self.btn_logic[key].append(btn.GetId())

        self.buttonsizer = wx.FlexGridSizer(cols=4, hgap=5, vgap=5)
        self.buttonsizer.Add(self.btn_clear)
        self.buttonsizer.Add(self.btn_apply)
        self.buttonsizer.Add(self.btn_close)

        btn_logicsizer = wx.GridBagSizer(5, 5)
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['is'][1]), pos=(
                0, 0))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['isnot'][1]), pos=(
                1, 0))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['like'][1]), pos=(
                2, 0))

        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['gt'][1]), pos=(
                0, 1))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['ge'][1]), pos=(
                1, 1))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['or'][1]), pos=(
                2, 1))

        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['lt'][1]), pos=(
                0, 2))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['le'][1]), pos=(
                1, 2))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['not'][1]), pos=(
                2, 2))

        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['brac'][1]), pos=(
                0, 3))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['prc'][1]), pos=(
                1, 3))
        btn_logicsizer.Add(
            self.FindWindowById(
                self.btn_logic['and'][1]), pos=(
                2, 3))

        self.btn_logicpanel.SetSizer(btn_logicsizer)

        #
        # list boxes (columns, values)
        #
        self.hsizer = wx.BoxSizer(wx.HORIZONTAL)

        columnsbox = StaticBox(parent=self.panel, id=wx.ID_ANY,
                               label=" %s " % _("Columns"))
        columnsizer = wx.StaticBoxSizer(columnsbox, wx.VERTICAL)
        self.list_columns = wx.ListBox(
            parent=self.panel,
            id=wx.ID_ANY,
            choices=self.dbInfo.GetColumns(
                self.tablename),
            style=wx.LB_MULTIPLE)
        columnsizer.Add(self.list_columns, proportion=1,
                        flag=wx.EXPAND)

        if modeChoices:
            modesizer = wx.BoxSizer(wx.VERTICAL)
            
            self.mode = wx.RadioBox(parent=self.panel, id=wx.ID_ANY,
                                    label=" %s " % _("Interactive insertion"),
                                    choices=modeChoices,
                                    style=wx.RA_SPECIFY_COLS,
                                    majorDimension=1)
            
            self.mode.SetSelection(1)  # default 'values'
            modesizer.Add(self.mode, proportion=1,
                          flag=wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND, border=5)

        # self.list_columns.SetMinSize((-1,130))
        # self.list_values.SetMinSize((-1,100))

        self.valuespanel = wx.Panel(parent=self.panel, id=wx.ID_ANY)
        valuesbox = StaticBox(parent=self.valuespanel, id=wx.ID_ANY,
                              label=" %s " % _("Values"))
        valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
        self.list_values = wx.ListBox(parent=self.valuespanel, id=wx.ID_ANY,
                                      choices=self.colvalues,
                                      style=wx.LB_MULTIPLE)
        valuesizer.Add(self.list_values, proportion=1,
                       flag=wx.EXPAND)
        self.valuespanel.SetSizer(valuesizer)

        self.btn_unique = Button(parent=self.valuespanel, id=wx.ID_ANY,
                                    label=_("Get all values"))
        self.btn_unique.Enable(False)
        self.btn_uniquesample = Button(
            parent=self.valuespanel,
            id=wx.ID_ANY,
            label=_("Get sample"))
        self.btn_uniquesample.SetToolTip(
            _("Get first 256 unique values as sample"))
        self.btn_uniquesample.Enable(False)

        buttonsizer3 = wx.BoxSizer(wx.HORIZONTAL)
        buttonsizer3.Add(self.btn_uniquesample, proportion=0,
                         flag=wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border=5)
        buttonsizer3.Add(self.btn_unique, proportion=0,
                         flag=wx.ALIGN_CENTER_HORIZONTAL)

        valuesizer.Add(buttonsizer3, proportion=0,
                       flag=wx.TOP, border=5)

        # go to
        gotosizer = wx.BoxSizer(wx.HORIZONTAL)
        self.goto = TextCtrl(
            parent=self.valuespanel,
            id=wx.ID_ANY,
            style=wx.TE_PROCESS_ENTER)
        gotosizer.Add(StaticText(parent=self.valuespanel, id=wx.ID_ANY,
                                 label=_("Go to:")), proportion=0,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5)
        gotosizer.Add(self.goto, proportion=1,
                      flag=wx.EXPAND)
        valuesizer.Add(gotosizer, proportion=0,
                       flag=wx.ALL | wx.EXPAND, border=5)

        self.hsizer.Add(columnsizer, proportion=1,
                        flag=wx.EXPAND)
        self.hsizer.Add(self.valuespanel, proportion=1,
                        flag=wx.EXPAND)

        self.close_onapply = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
                                         label=_("Close dialog on apply"))
        self.close_onapply.SetValue(True)

        if showDbInfo:
            self.pagesizer.Add(databaseboxsizer,
                               flag=wx.ALL | wx.EXPAND, border=5)
        if modeChoices:
            self.pagesizer.Add(
                modesizer,
                proportion=0,
                flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
                border=5)
        self.pagesizer.Add(
            self.hsizer,
            proportion=1,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5)
        # self.pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
        # self.pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
        self.pagesizer.Add(self.btn_logicpanel, proportion=0,
                           flag=wx.ALIGN_CENTER_HORIZONTAL)
        self.pagesizer.Add(sqlboxsizer, proportion=0,
                           flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
        self.pagesizer.Add(self.buttonsizer, proportion=0,
                           flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
        self.pagesizer.Add(
            self.close_onapply,
            proportion=0,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5)

        #
        # bindings
        #
        if modeChoices:
            self.mode.Bind(wx.EVT_RADIOBOX, self.OnMode)
        # self.text_sql.Bind(wx.EVT_ACTIVATE, self.OnTextSqlActivate)TODO

        self.btn_unique.Bind(wx.EVT_BUTTON, self.OnUniqueValues)
        self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.OnSampleValues)

        for key, value in six.iteritems(self.btn_logic):
            self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)

        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
        self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
        self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)

        self.list_columns.Bind(wx.EVT_LISTBOX, self.OnAddColumn)
        self.list_values.Bind(wx.EVT_LISTBOX, self.OnAddValue)
        self.goto.Bind(wx.EVT_TEXT, self.OnGoTo)
        self.goto.Bind(wx.EVT_TEXT_ENTER, self.OnAddValue)

    def OnUniqueValues(self, event, justsample=False):
        """Get unique values"""
        vals = []
        try:
            idx = self.list_columns.GetSelections()[0]
            column = self.list_columns.GetString(idx)
        except:
            self.list_values.Clear()
            return

        self.list_values.Clear()

        sql = "SELECT DISTINCT {column} FROM {table} ORDER BY {column}".format(
            column=column, table=self.tablename)
        if justsample:
            sql += " LIMIT {}".format(255)
        data = grass.db_select(
            sql=sql,
            database=self.database,
            driver=self.driver,
            sep='{_sep_}')
        if not data:
            return

        desc = self.dbInfo.GetTableDesc(
            self.dbInfo.GetTable(self.layer))[column]

        i = 0
        items = []
        for item in data: #sorted(set(map(lambda x: desc['ctype'](x[0]), data))):
            if desc['type'] not in ('character', 'text'):
                items.append(str(item[0]))
            else:
                items.append(u"'{}'".format(GetUnicodeValue(item[0])))
            i += 1

        self.list_values.AppendItems(items)
        
    def OnSampleValues(self, event):
        """Get sample values"""
        self.OnUniqueValues(None, True)

    def OnAddColumn(self, event):
        """Add column name to the query"""
        idx = self.list_columns.GetSelections()
        for i in idx:
            column = self.list_columns.GetString(i)
            self._add(element='column', value=column)

        if not self.btn_uniquesample.IsEnabled():
            self.btn_uniquesample.Enable(True)
            self.btn_unique.Enable(True)

    def OnAddValue(self, event):
        """Add value"""
        selection = self.list_values.GetSelections()
        if not selection:
            event.Skip()
            return

        idx = selection[0]
        value = self.list_values.GetString(idx)
        idx = self.list_columns.GetSelections()[0]
        column = self.list_columns.GetString(idx)

        ctype = self.dbInfo.GetTableDesc(
            self.dbInfo.GetTable(
                self.layer))[column]['type']

        self._add(element='value', value=value)

    def OnGoTo(self, event):
        # clear all previous selections
        for item in self.list_values.GetSelections():
            self.list_values.Deselect(item)

        gotoText = event.GetString()
        lenLimit = len(gotoText)
        found = idx = 0
        string = False
        for item in self.list_values.GetItems():
            if idx == 0 and item.startswith("'"):
                string = True
            if string:
                item = item[1:-1] # strip "'"
            if item[:lenLimit] == gotoText:
                found = idx
                break
            idx += 1

        if found > 0:
            self.list_values.SetSelection(found)

    def OnAddMark(self, event):
        """Add mark"""
        mark = None
        if self.btn_logicpanel and \
           self.btn_logicpanel.IsShown():
            btns = self.btn_logic
        elif self.btn_arithmeticpanel and \
                self.btn_arithmeticpanel.IsShown():
            btns = self.btn_arithmetic

        for key, value in six.iteritems(btns):
            if event.GetId() == value[1]:
                mark = value[0]
                break

        self._add(element='mark', value=mark)

    def GetSQLStatement(self):
        """Return SQL statement"""
        return self.text_sql.GetValue().strip().replace("\n", " ")

    def OnClose(self, event):
        self.Destroy()
        event.Skip()
Example #14
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._last_downloaded_location_name = None
        self._download_in_progress = False
        self.database = database
        self.locations = locations

        self.label = wx.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.download_button = Button(parent=self, id=wx.ID_ANY,
                                      label=_("Do&wnload"))
        self.download_button.SetToolTip(_("Download selected location"))
        self.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 = wx.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)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.AddStretchSpacer()
        button_sizer.Add(self.download_button, proportion=0)

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

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

    def OnDownload(self, event):
        """Handle user-initiated action of download"""
        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])
        self.download_button.Enable(False)

    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)
            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._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)

    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)
            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.
        """
        self.message.SetLabel(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.
        """
        self.message.SetLabel(_("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()
Example #15
0
class InstallExtensionWindow(wx.Frame):
    def __init__(self,
                 parent,
                 giface,
                 id=wx.ID_ANY,
                 title=_("Fetch & install extension from GRASS Addons"),
                 **kwargs):
        self.parent = parent
        self._giface = giface
        self.options = dict()  # list of options

        wx.Frame.__init__(self, parent=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.repoBox = StaticBox(
        #     parent=self.panel, id=wx.ID_ANY, label=" %s " %
        #     _("Repository (leave empty to use the official one)"))
        self.treeBox = StaticBox(
            parent=self.panel,
            id=wx.ID_ANY,
            label=" %s " % _("List of extensions - double-click to install"))

        # self.repo = TextCtrl(parent=self.panel, id=wx.ID_ANY)

        # modelBuilder loads data into tree model
        self.modelBuilder = ExtensionTreeModelBuilder()
        # tree view displays model data
        self.tree = CTreeView(parent=self.panel,
                              model=self.modelBuilder.GetModel())

        self.search = SearchModuleWidget(parent=self.panel,
                                         model=self.modelBuilder.GetModel(),
                                         showChoice=False)
        self.search.showSearchResult.connect(
            lambda result: self.tree.Select(result))
        # show text in statusbar when notification appears
        self.search.showNotification.connect(
            lambda message: self.SetStatusText(message))
        # load data in different thread
        self.thread = gThread()

        self.optionBox = StaticBox(parent=self.panel,
                                   id=wx.ID_ANY,
                                   label=" %s " % _("Options"))
        task = gtask.parse_interface('g.extension')
        ignoreFlags = ['l', 'c', 'g', 'a', 'f', 't', 'help', 'quiet']
        if sys.platform == 'win32':
            ignoreFlags.append('d')
            ignoreFlags.append('i')

        for f in task.get_options()['flags']:
            name = f.get('name', '')
            desc = f.get('label', '')
            if not desc:
                desc = f.get('description', '')
            if not name and not desc:
                continue
            if name in ignoreFlags:
                continue
            self.options[name] = wx.CheckBox(parent=self.panel,
                                             id=wx.ID_ANY,
                                             label=desc)
        # defaultUrl = ''  # default/official one will be used when option empty
        # self.repo.SetValue(
        #     task.get_param(
        #         value='url').get(
        #         'default',
        #         defaultUrl))

        self.statusbar = self.CreateStatusBar(number=1)

        # self.btnFetch = Button(parent=self.panel, id=wx.ID_ANY,
        #                        label=_("&Fetch"))
        # self.btnFetch.SetToolTip(_("Fetch list of available modules "
        #                            "from GRASS Addons repository"))
        self.btnClose = Button(parent=self.panel, id=wx.ID_CLOSE)
        self.btnInstall = Button(parent=self.panel,
                                 id=wx.ID_ANY,
                                 label=_("&Install"))
        self.btnInstall.SetToolTip(_("Install selected add-ons GRASS module"))
        self.btnInstall.Enable(False)
        self.btnHelp = Button(parent=self.panel, id=wx.ID_HELP)
        self.btnHelp.SetToolTip(_("Show g.extension manual page"))

        self.btnClose.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
        # self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
        self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
        self.btnHelp.Bind(wx.EVT_BUTTON, self.OnHelp)
        self.tree.selectionChanged.connect(self.OnItemSelected)
        self.tree.itemActivated.connect(self.OnItemActivated)
        self.tree.contextMenu.connect(self.OnContextMenu)

        wx.CallAfter(self._fetch)

        self._layout()

    def _layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)
        # repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
        # repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
        # repo1Sizer.Add(self.repo, proportion=1,
        #                flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
        # repo1Sizer.Add(self.btnFetch, proportion=0,
        #                flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=1)
        # repoSizer.Add(repo1Sizer,
        #               flag=wx.EXPAND)

        findSizer = wx.BoxSizer(wx.HORIZONTAL)
        findSizer.Add(self.search, proportion=1)

        treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
        treeSizer.Add(self.tree,
                      proportion=1,
                      flag=wx.ALL | wx.EXPAND,
                      border=1)

        # options
        optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
        for key in self.options.keys():
            optionSizer.Add(self.options[key], proportion=0)

        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.Add(self.btnHelp, proportion=0)
        btnSizer.AddStretchSpacer()
        btnSizer.Add(self.btnClose, proportion=0, flag=wx.RIGHT, border=5)
        btnSizer.Add(self.btnInstall, proportion=0)

        # sizer.Add(repoSizer, proportion=0,
        #           flag=wx.ALL | wx.EXPAND, border=3)
        sizer.Add(findSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
        sizer.Add(treeSizer,
                  proportion=1,
                  flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
                  border=3)
        sizer.Add(optionSizer,
                  proportion=0,
                  flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
                  border=3)
        sizer.Add(btnSizer,
                  proportion=0,
                  flag=wx.ALIGN_RIGHT | wx.ALL | wx.EXPAND,
                  border=5)

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

        self.Layout()

    def _getCmd(self):
        item = self.tree.GetSelected()
        if not item or 'command' not in item[0].data:
            GError(_("Extension not defined"), parent=self)
            return

        name = item[0].data['command']

        flags = list()
        for key in self.options.keys():
            if self.options[key].IsChecked():
                if len(key) == 1:
                    flags.append('-%s' % key)
                else:
                    flags.append('--%s' % key)

        # 'url=' + self.repo.GetValue().strip()]
        return ['g.extension'] + flags + ['extension={}'.format(name)]

    def OnFetch(self, event):
        """Fetch list of available extensions"""
        self._fetch()

    def _fetch(self):
        """Fetch list of available extensions"""
        wx.BeginBusyCursor()
        self.SetStatusText(
            _("Fetching list of modules from GRASS-Addons (be patient)..."), 0)
        try:
            self.thread.Run(
                callable=self.modelBuilder.Load,
                url='',  # self.repo.GetValue().strip(),
                ondone=lambda event: self._fetchDone())
        except GException as e:
            self._fetchDone()
            GError(unicode(e), parent=self, showTraceback=False)

    def _fetchDone(self):
        self.tree.RefreshItems()
        nitems = len(self.modelBuilder.GetModel().SearchNodes(key='command',
                                                              value='*'))
        self.SetStatusText(_("%d extensions loaded") % nitems, 0)
        wx.EndBusyCursor()

    def OnContextMenu(self, node):
        if not hasattr(self, "popupID"):
            self.popupID = dict()
            for key in ('install', 'help'):
                self.popupID[key] = NewId()

        data = node.data
        if data and 'command' in data:
            self.popupMenu = Menu()
            self.popupMenu.Append(self.popupID['install'], text=_("Install"))
            self.Bind(wx.EVT_MENU, self.OnInstall, id=self.popupID['install'])
            self.popupMenu.AppendSeparator()
            self.popupMenu.Append(self.popupID['help'],
                                  text=_("Show manual page"))
            self.Bind(wx.EVT_MENU, self.OnItemHelp, id=self.popupID['help'])

            self.PopupMenu(self.popupMenu)
            self.popupMenu.Destroy()

    def OnItemActivated(self, node):
        data = node.data
        if data and 'command' in data:
            self.OnInstall(event=None)

    def OnInstall(self, event):
        """Install selected extension"""
        log = self.parent.GetLogWindow()
        cmd = self._getCmd()
        if cmd:
            log.RunCmd(cmd, onDone=self.OnDone)

    def OnDone(self, event):
        if event.returncode == 0:
            if not os.getenv('GRASS_ADDON_BASE'):
                SetAddOnPath(key='BASE')

            globalvar.UpdateGRASSAddOnCommands()
            toolboxesOutdated()

    def OnItemHelp(self, event):
        item = self.tree.GetSelected()
        if not item or 'command' not in item[0].data:
            return

        self._giface.Help(entry=item[0].data['command'], online=True)

    def OnHelp(self, event):
        self._giface.Help(entry='g.extension')

    def OnItemSelected(self, node):
        """Item selected"""
        data = node.data
        if data is None:
            self.SetStatusText('', 0)
            self.btnInstall.Enable(False)
        else:
            self.SetStatusText(data.get('description', ''), 0)
            self.btnInstall.Enable(True)
Example #16
0
class IClassExportAreasDialog(wx.Dialog):
    def __init__(
        self,
        parent,
        vectorName=None,
        title=_("Export training areas"),
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
        **kwargs,
    ):
        """Dialog for export of training areas to vector layer

        :param parent: window
        :param vectorName: name of vector layer for export
        :param title: window title
        """
        wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)

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

        self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
        self.btnOK = Button(parent=self.panel, id=wx.ID_OK)
        self.btnOK.SetDefault()
        self.btnOK.Enable(False)
        self.btnOK.Bind(wx.EVT_BUTTON, self.OnOK)

        self.__layout()

        self.vectorNameCtrl.Bind(wx.EVT_TEXT, self.OnTextChanged)
        self.OnTextChanged(None)
        wx.CallAfter(self.vectorNameCtrl.SetFocus)

    def OnTextChanged(self, event):
        """Name of new vector map given.

        Enable/diable OK button.
        """
        file = self.vectorNameCtrl.GetValue()
        if len(file) > 0:
            self.btnOK.Enable(True)
        else:
            self.btnOK.Enable(False)

    def __layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer = wx.BoxSizer(wx.VERTICAL)

        dataSizer.Add(
            StaticText(
                parent=self.panel,
                id=wx.ID_ANY,
                label=_("Enter name of new vector map:"),
            ),
            proportion=0,
            flag=wx.ALL,
            border=3,
        )
        self.vectorNameCtrl = gselect.Select(
            parent=self.panel,
            type="vector",
            mapsets=[grass.gisenv()["MAPSET"]],
            size=globalvar.DIALOG_GSELECT_SIZE,
        )
        if self.vectorName:
            self.vectorNameCtrl.SetValue(self.vectorName)
        dataSizer.Add(self.vectorNameCtrl,
                      proportion=0,
                      flag=wx.ALL | wx.EXPAND,
                      border=3)
        self.withTableCtrl = CheckBox(parent=self.panel,
                                      id=wx.ID_ANY,
                                      label=_("Export attribute table"))
        self.withTableCtrl.SetValue(True)
        self.withTableCtrl.SetToolTip(
            _("Export attribute table containing"
              " computed statistical data"))

        dataSizer.Add(self.withTableCtrl, proportion=0, flag=wx.ALL, border=3)

        # buttons
        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnOK)
        btnSizer.Realize()

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

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

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

        self.SetMinSize(self.GetSize())

    def GetVectorName(self):
        """Returns vector name"""
        return self.vectorNameCtrl.GetValue()

    def WithTable(self):
        """Returns true if attribute table should be exported too"""
        return self.withTableCtrl.IsChecked()

    def OnOK(self, event):
        """Checks if map exists and can be overwritten."""
        overwrite = UserSettings.Get(group="cmd",
                                     key="overwrite",
                                     subkey="enabled")
        vName = self.GetVectorName()
        res = grass.find_file(vName, element="vector")
        if res["fullname"] and overwrite is False:
            qdlg = wx.MessageDialog(
                parent=self,
                message=_("Vector map <%s> already exists."
                          " Do you want to overwrite it?" % vName),
                caption=_("Vector <%s> exists" % vName),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE,
            )
            if qdlg.ShowModal() == wx.ID_YES:
                event.Skip()
            qdlg.Destroy()
        else:
            event.Skip()