""" @package gui_core.dialogs @brief Various dialogs used in wxGUI. List of classes: - :class:`SimpleDialog` - :class:`LocationDialog` - :class:`MapsetDialog` - :class:`VectorDialog` - :class:`NewVectorDialog` - :class:`SavedRegion` - :class:`GroupDialog` - :class:`MapLayersDialog` - :class:`SetOpacityDialog` - :class:`ImageSizeDialog` - :class:`SqlQueryFrame` - :class:`SymbolDialog` - :class:`QuitDialog` (C) 2008-2015 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa @author Anna Kratochvilova (GroupDialog, SymbolDialog) """ import os import sys import re import wx from grass.script import core as grass from grass.script.utils import natural_sort from grass.pydispatch.signal import Signal from core import globalvar from core.gcmd import GError, RunCommand, GMessage from gui_core.gselect import LocationSelect, MapsetSelect, Select, \ OgrTypeSelect, SubGroupSelect from gui_core.widgets import SingleSymbolPanel, GListCtrl, SimpleValidator, MapValidator from core.utils import _ from core.settings import UserSettings from core.debug import Debug class SimpleDialog(wx.Dialog): def __init__(self, parent, title, id = wx.ID_ANY, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): """General dialog to choose given element (location, mapset, vector map, etc.) :param parent: window :param title: window title """ wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs) self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY) self.panel = wx.Panel(parent = self, id = wx.ID_ANY) self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL) self.btnOK = wx.Button(parent = self.panel, id = wx.ID_OK) self.btnOK.SetDefault() self.__layout() self.warning = _("Required item is not set.") def __layout(self): """Do layout""" self.sizer = wx.BoxSizer(wx.VERTICAL) self.dataSizer = wx.BoxSizer(wx.VERTICAL) # self.informLabel = wx.StaticText(self.panel, id = wx.ID_ANY) # buttons btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnCancel) btnSizer.AddButton(self.btnOK) btnSizer.Realize() self.sizer.Add(item = self.dataSizer, proportion = 1, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) # self.sizer.Add(item = self.informLabel, proportion = 0, flag = wx.ALL, border = 5) self.sizer.Add(item = btnSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5) def ValidatorCallback(self, win): GMessage(parent = self, message = self.warning) # self.informLabel.SetForegroundColour(wx.Colour(255, 0, 0)) # self.informLabel.SetLabel(self.warning) class LocationDialog(SimpleDialog): """Dialog used to select location""" def __init__(self, parent, title = _("Select GRASS location and mapset")): SimpleDialog.__init__(self, parent, title) self.element1 = LocationSelect(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, validator = SimpleValidator(callback = self.ValidatorCallback)) self.element1.Bind(wx.EVT_TEXT, self.OnLocation) self.element2 = MapsetSelect(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, setItems = False, skipCurrent = True, validator = SimpleValidator(callback = self.ValidatorCallback)) self.element1.SetFocus() self.warning = _("Location or mapset is not defined.") self._layout() self.SetMinSize(self.GetSize()) def _layout(self): """Do layout""" self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Name of GRASS location:")), proportion = 0, flag = wx.ALL, border = 1) self.dataSizer.Add(self.element1, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.dataSizer.Add(wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Name of mapset:")), proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.dataSizer.Add(self.element2, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) def OnLocation(self, event): """Select mapset given location name""" location = event.GetString() if location: dbase = grass.gisenv()['GISDBASE'] self.element2.UpdateItems(dbase = dbase, location = location) self.element2.SetSelection(0) mapset = self.element2.GetStringSelection() def GetValues(self): """Get location, mapset""" return (self.element1.GetValue(), self.element2.GetValue()) class MapsetDialog(SimpleDialog): """Dialog used to select mapset""" def __init__(self, parent, title = _("Select mapset in GRASS location"), location = None): SimpleDialog.__init__(self, parent, title) if location: self.SetTitle(self.GetTitle() + ' <%s>' % location) else: self.SetTitle(self.GetTitle() + ' <%s>' % grass.gisenv()['LOCATION_NAME']) self.element = MapsetSelect(parent = self.panel, id = wx.ID_ANY, skipCurrent = True, size = globalvar.DIALOG_GSELECT_SIZE, validator = SimpleValidator(callback = self.ValidatorCallback)) self.element.SetFocus() self.warning = _("Name of mapset is missing.") self._layout() self.SetMinSize(self.GetSize()) def _layout(self): """Do layout""" self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Name of mapset:")), proportion = 0, flag = wx.ALL, border = 1) self.dataSizer.Add(self.element, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) def GetMapset(self): return self.element.GetValue() class VectorDialog(SimpleDialog): def __init__(self, parent, title = _("Select vector map"), layerTree = None): """Dialog for selecting existing vector map :param parent: parent window :param title: window title :param layerTree: show only vector maps in given layer tree if not None :return: dialog instance """ SimpleDialog.__init__(self, parent, title) self.element = Select(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, type = 'vector', layerTree = layerTree, validator = MapValidator()) self.element.SetFocus() self.warning = _("Name of vector map is missing.") wx.CallAfter(self._layout) def _layout(self): """Do layout""" self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Name of vector map:")), proportion = 0, flag = wx.ALL, border = 1) self.dataSizer.Add(item = self.element, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) def GetName(self, full = False): """Get name of vector map to be created :param full: True to get fully qualified name """ name = self.element.GetValue() if full: if '@' in name: return name else: return name + '@' + grass.gisenv()['MAPSET'] return name.split('@', 1)[0] class NewVectorDialog(VectorDialog): def __init__(self, parent, title = _("Create new vector map"), disableAdd = False, disableTable = False, showType = False): """Dialog for creating new vector map :param parent: parent window :param title: window title :param disableAdd: disable 'add layer' checkbox :param disableTable: disable 'create table' checkbox :param showType: True to show feature type selector (used for creating new empty OGR layers) :return: dialog instance """ VectorDialog.__init__(self, parent, title) # determine output format if showType: self.ftype = OgrTypeSelect(parent = self, panel = self.panel) else: self.ftype = None # create attribute table self.table = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _("Create attribute table")) self.table.SetValue(True) if disableTable: self.table.Enable(False) if showType: self.keycol = None else: self.keycol = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, size = globalvar.DIALOG_SPIN_SIZE) self.keycol.SetValue(UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value')) if disableTable: self.keycol.Enable(False) self.addbox = wx.CheckBox(parent = self.panel, label = _('Add created map into layer tree'), style = wx.NO_BORDER) if disableAdd: self.addbox.SetValue(True) self.addbox.Enable(False) else: self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled')) self.table.Bind(wx.EVT_CHECKBOX, self.OnTable) self.warning = _("Name of new vector map is missing.") def OnTable(self, event): if self.keycol: self.keycol.Enable(event.IsChecked()) def _layout(self): """Do layout""" self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Name for new vector map:")), proportion = 0, flag = wx.ALL, border = 1) self.dataSizer.Add(item = self.element, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) if self.ftype: self.dataSizer.AddSpacer(1) self.dataSizer.Add(item = self.ftype, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.dataSizer.Add(item = self.table, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) if self.keycol: keySizer = wx.BoxSizer(wx.HORIZONTAL) keySizer.Add(item = wx.StaticText(parent = self.panel, label = _("Key column:")), proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL) keySizer.AddSpacer(10) keySizer.Add(item = self.keycol, proportion = 0, flag = wx.ALIGN_RIGHT) self.dataSizer.Add(item = keySizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 1) self.dataSizer.AddSpacer(5) self.dataSizer.Add(item = self.addbox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1) self.panel.SetSizer(self.sizer) self.sizer.Fit(self) self.SetMinSize(self.GetSize()) def GetKey(self): """Get key column name""" if self.keycol: return self.keycol.GetValue() return UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value') def IsChecked(self, key): """Get dialog properties :param key: window key ('add', 'table') :return: True/False :return: None on error """ if key == 'add': return self.addbox.IsChecked() elif key == 'table': return self.table.IsChecked() return None def GetFeatureType(self): """Get feature type for OGR :return: feature type as string :return: None for native format """ if self.ftype: return self.ftype.GetType() return None def CreateNewVector(parent, cmd, title = _('Create new vector map'), exceptMap=None, giface=None, disableAdd = False, disableTable = False): """Create new vector map layer :param cmd: (prog, \*\*kwargs) :param title: window title :param exceptMap: list of maps to be excepted :param log: :param disableAdd: disable 'add layer' checkbox :param disableTable: disable 'create table' checkbox :return: dialog instance :return: None on error """ vExternalOut = grass.parse_command('v.external.out', flags='g') isNative = vExternalOut['format'] == 'native' if cmd[0] == 'v.edit' and not isNative: showType = True else: showType = False dlg = NewVectorDialog(parent, title = title, disableAdd = disableAdd, disableTable = disableTable, showType = showType) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return None outmap = dlg.GetName() key = dlg.GetKey() if outmap == exceptMap: GError(parent = parent, message = _("Unable to create vector map <%s>.") % outmap) dlg.Destroy() return None if dlg.table.IsEnabled() and not key: GError(parent = parent, message = _("Invalid or empty key column.\n" "Unable to create vector map <%s>.") % outmap) dlg.Destroy() return if outmap == '': # should not happen dlg.Destroy() return None # update cmd -> output name defined cmd[1][cmd[2]] = outmap if showType: cmd[1]['type'] = dlg.GetFeatureType() curMapset = grass.gisenv()['MAPSET'] if isNative: listOfVectors = grass.list_grouped('vector')[curMapset] else: listOfVectors = RunCommand('v.external', quiet = True, parent = parent, read = True, flags = 'l', input = vExternalOut['dsn']).splitlines() overwrite = False if not UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled') and \ outmap in listOfVectors: dlgOw = wx.MessageDialog(parent, message = _("Vector map <%s> already exists " "in the current mapset. " "Do you want to overwrite it?") % outmap, caption = _("Overwrite?"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) if dlgOw.ShowModal() == wx.ID_YES: overwrite = True else: dlgOw.Destroy() dlg.Destroy() return None if UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'): overwrite = True ret = RunCommand(prog = cmd[0], parent = parent, overwrite = overwrite, **cmd[1]) if ret != 0: dlg.Destroy() return None if not isNative and \ not grass.find_file(outmap, element = 'vector', mapset = curMapset)['fullname']: # create link for OGR layers RunCommand('v.external', overwrite = overwrite, parent = parent, input = vExternalOut['dsn'], layer = outmap) # create attribute table if dlg.table.IsEnabled() and dlg.table.IsChecked(): if isNative: sql = 'CREATE TABLE %s (%s INTEGER)' % (outmap, key) RunCommand('db.connect', flags = 'c') Debug.msg(1, "SQL: %s" % sql) RunCommand('db.execute', quiet = True, parent = parent, input = '-', stdin = sql) RunCommand('v.db.connect', quiet = True, parent = parent, map = outmap, table = outmap, key = key, layer = '1') # TODO: how to deal with attribute tables for OGR layers? # return fully qualified map name if '@' not in outmap: outmap += '@' + grass.gisenv()['MAPSET'] # if giface: # giface.WriteLog(_("New vector map <%s> created") % outmap) return dlg class SavedRegion(wx.Dialog): def __init__(self, parent, title, id = wx.ID_ANY, loadsave = 'load', **kwargs): """Loading or saving of display extents to saved region file :param loadsave: load or save region? """ wx.Dialog.__init__(self, parent, id, title, **kwargs) self.loadsave = loadsave self.wind = '' sizer = wx.BoxSizer(wx.VERTICAL) box = wx.BoxSizer(wx.HORIZONTAL) label = wx.StaticText(parent = self, id = wx.ID_ANY) box.Add(item = label, proportion = 0, flag = wx.ALIGN_CENTRE | wx.ALL, border = 5) if loadsave == 'load': label.SetLabel(_("Load region:")) self._selection = Select(parent=self, size=globalvar.DIALOG_GSELECT_SIZE, type='windows') elif loadsave == 'save': label.SetLabel(_("Save region:")) self._selection = Select(parent=self, size=globalvar.DIALOG_GSELECT_SIZE, type='windows', mapsets=[grass.gisenv()['MAPSET']], fullyQualified = False) box.Add(item=self._selection, proportion=0, flag=wx.ALIGN_CENTRE | wx.ALL, border=5) self._selection.SetFocus() self._selection.Bind(wx.EVT_TEXT, self.OnRegion) sizer.Add(item = box, proportion = 0, flag = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5) line = wx.StaticLine(parent = self, id = wx.ID_ANY, size = (20, -1), style = wx.LI_HORIZONTAL) sizer.Add(item = line, proportion = 0, flag = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, border = 5) btnsizer = wx.StdDialogButtonSizer() btn = wx.Button(parent = self, id = wx.ID_OK) btn.SetDefault() btnsizer.AddButton(btn) btn = wx.Button(parent = self, id = wx.ID_CANCEL) btnsizer.AddButton(btn) btnsizer.Realize() sizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) self.SetSizer(sizer) sizer.Fit(self) self.Layout() def OnRegion(self, event): value = self._selection.GetValue() if '@' in value: value = value.rsplit('@', 1)[0] if not grass.legal_name(value): GMessage(parent=self, message=_("Name cannot begin with '.' " "and must not contain space, quotes, " "'/', '\'', '@', ',', '=', '*', " "and all other non-alphanumeric characters.")) else: self.wind = value def GetName(self): """Return region name""" return self.wind class GroupDialog(wx.Dialog): """Dialog for creating/editing groups""" def __init__(self, parent = None, defaultGroup = None, defaultSubgroup = None, title = _("Create or edit imagery groups"), style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style, **kwargs) self.parent = parent self.defaultGroup = defaultGroup self.defaultSubgroup = defaultSubgroup self.currentGroup = self.defaultGroup self.currentSubgroup = self.defaultGroup self.dataChanged = False # signaling edit subgroup / group mode self.edit_subg = False # sungroup maps dict value - ischecked self.subgmaps = {} # list of group maps self.gmaps = [] # pattern chosen for filtering self.flt_pattern = '' self.bodySizer = self._createDialogBody() # buttons btnOk = wx.Button(parent = self, id = wx.ID_OK) btnApply = wx.Button(parent = self, id = wx.ID_APPLY) btnClose = wx.Button(parent = self, id = wx.ID_CANCEL) btnOk.SetToolTipString(_("Apply changes to selected group and close dialog")) btnApply.SetToolTipString(_("Apply changes to selected group")) btnClose.SetToolTipString(_("Close dialog, changes are not applied")) #btnOk.SetDefault() # sizers & do layout # btnSizer = wx.BoxSizer(wx.HORIZONTAL) # btnSizer.Add(item = btnClose, proportion = 0, # flag = wx.RIGHT | wx.ALIGN_RIGHT | wx.EXPAND, border = 5) # btnSizer.Add(item = btnApply, proportion = 0, # flag = wx.LEFT, border = 5) btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(btnOk) btnSizer.AddButton(btnApply) btnSizer.AddButton(btnClose) btnSizer.Realize() mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(item = self.bodySizer, proportion = 1, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10) mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY, style = wx.LI_HORIZONTAL), proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10) mainSizer.Add(item = btnSizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT, border = 10) self.SetSizer(mainSizer) mainSizer.Fit(self) btnOk.Bind(wx.EVT_BUTTON, self.OnOk) btnApply.Bind(wx.EVT_BUTTON, self.OnApply) btnClose.Bind(wx.EVT_BUTTON, self.OnClose) # set dialog min size self.SetMinSize(self.GetSize()) self.SetSize((-1, 400)) def _createDialogBody(self): bodySizer = wx.BoxSizer(wx.VERTICAL) #TODO same text in MapLayersDialogBase filter_tooltip = _("Put here a regular expression." " Characters '.*' stand for anything," " character '^' stands for the beginning" " and '$' for the end.") # group selection bodySizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Select existing group or " "enter name of new group:")), flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP, border = 10) self.groupSelect = Select(parent = self, type = 'group', mapsets = [grass.gisenv()['MAPSET']], size = globalvar.DIALOG_GSELECT_SIZE, fullyQualified = False) # searchpath? bodySizer.Add(item = self.groupSelect, flag = wx.TOP | wx.EXPAND, border = 5) self.subg_chbox = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Edit/create subgroup")) bodySizer.Add(item = self.subg_chbox, flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP, border = 10) self.subg_panel = wx.Panel(self) subg_sizer = wx.BoxSizer(wx.VERTICAL) subg_sizer.Add(item = wx.StaticText(parent=self.subg_panel, id = wx.ID_ANY, label = _("Select existing subgroup or " "enter name of new subgroup:")), flag = wx.ALIGN_CENTER_VERTICAL) self.subGroupSelect = SubGroupSelect(parent = self.subg_panel) subg_sizer.Add(item=self.subGroupSelect, flag=wx.EXPAND | wx.TOP, border = 5) self.subg_panel.SetSizer(subg_sizer) bodySizer.Add(item = self.subg_panel, flag = wx.TOP | wx.EXPAND, border = 5) bodySizer.AddSpacer(10) buttonSizer = wx.BoxSizer(wx.VERTICAL) # layers in group self.gListPanel = wx.Panel(self) gListSizer = wx.GridBagSizer(vgap=3, hgap=2) self.g_sel_all = wx.CheckBox(parent=self.gListPanel, id=wx.ID_ANY, label=_("Select all")) gListSizer.Add(item=self.g_sel_all, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0,1)) gListSizer.Add(item = wx.StaticText(parent = self.gListPanel, label = _("Pattern:")), flag = wx.ALIGN_CENTER_VERTICAL, pos = (1,0)) self.gfilter = wx.TextCtrl(parent=self.gListPanel, id=wx.ID_ANY, value="", size=(250,-1)) self.gfilter.SetToolTipString(filter_tooltip) gListSizer.Add(item=self.gfilter, flag=wx.EXPAND, pos=(1,1)) gListSizer.Add(item = wx.StaticText(parent = self.gListPanel, label = _("List of maps:")), flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, border = 5, pos=(2,0)) sizer = wx.BoxSizer(wx.HORIZONTAL) self.gLayerBox = wx.ListBox(parent = self.gListPanel, id = wx.ID_ANY, size = (-1, 150), style = wx.LB_MULTIPLE | wx.LB_NEEDED_SB) sizer.Add(item = self.gLayerBox, proportion = 1, flag = wx.EXPAND) self.addLayer = wx.Button(self.gListPanel, id = wx.ID_ADD) self.addLayer.SetToolTipString(_("Select map layers and add them to the list.")) buttonSizer.Add(item = self.addLayer, flag = wx.BOTTOM, border = 10) self.removeLayer = wx.Button(self.gListPanel, id = wx.ID_REMOVE) self.removeLayer.SetToolTipString(_("Remove selected layer(s) from list.")) buttonSizer.Add(item = self.removeLayer) sizer.Add(item = buttonSizer, flag = wx.LEFT, border = 5) gListSizer.Add(item=sizer, flag=wx.EXPAND, pos=(2,1)) gListSizer.AddGrowableCol(1) gListSizer.AddGrowableRow(2) self.gListPanel.SetSizer(gListSizer) bodySizer.Add(item=self.gListPanel, proportion=1, flag=wx.EXPAND) # layers in subgroup self.subgListPanel = wx.Panel(self) subgListSizer = wx.GridBagSizer(vgap=3, hgap=2) # select toggle self.subg_sel_all = wx.CheckBox(parent=self.subgListPanel, id=wx.ID_ANY, label=_("Select all")) subgListSizer.Add(item=self.subg_sel_all, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0,1)) subgListSizer.Add(item = wx.StaticText(parent=self.subgListPanel, label=_("Pattern:")), flag = wx.ALIGN_CENTER_VERTICAL, pos = (1,0)) self.subgfilter = wx.TextCtrl(parent=self.subgListPanel, id=wx.ID_ANY, value="", size=(250,-1)) self.subgfilter.SetToolTipString(filter_tooltip) subgListSizer.Add(item=self.subgfilter, flag=wx.EXPAND, pos=(1,1)) subgListSizer.Add(item = wx.StaticText(parent = self.subgListPanel, label = _("List of maps:")), flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, border = 5, pos=(2,0)) self.subgListBox = wx.CheckListBox(parent = self.subgListPanel, id = wx.ID_ANY, size = (250, 100)) self.subgListBox.SetToolTipString(_("Check maps from group to be included into subgroup.")) subgListSizer.Add(item=self.subgListBox, flag=wx.EXPAND, pos=(2,1)) subgListSizer.AddGrowableCol(1) subgListSizer.AddGrowableRow(2) self.subgListPanel.SetSizer(subgListSizer) bodySizer.Add(item=self.subgListPanel, proportion=1, flag=wx.EXPAND) self.infoLabel = wx.StaticText(parent=self, id=wx.ID_ANY) bodySizer.Add(item = self.infoLabel, flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.BOTTOM, border = 5) # bindings self.gfilter.Bind(wx.EVT_TEXT, self.OnGroupFilter) self.subgfilter.Bind(wx.EVT_TEXT, self.OnSubgroupFilter) self.gLayerBox.Bind(wx.EVT_LISTBOX, self.OnGLayerCheck) self.subgListBox.Bind(wx.EVT_CHECKLISTBOX, self.OnSubgLayerCheck) self.groupSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnGroupSelected) self.addLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer) self.removeLayer.Bind(wx.EVT_BUTTON, self.OnRemoveLayer) self.subg_chbox.Bind(wx.EVT_CHECKBOX, self.OnSubgChbox) self.subGroupSelect.Bind(wx.EVT_TEXT, lambda event : self.SubGroupSelected()) self.subg_sel_all.Bind(wx.EVT_CHECKBOX, self.OnSubgSelAll) self.g_sel_all.Bind(wx.EVT_CHECKBOX, self.OnGSelAll) if self.defaultGroup: self.groupSelect.SetValue(self.defaultGroup) if self.defaultSubgroup is not None: self.subGroupSelect.SetValue(self.defaultSubgroup) self.subg_chbox.SetValue(1) self.SubgChbox(True) else: self.subg_chbox.SetValue(0) self.SubgChbox(False) return bodySizer def OnGLayerCheck(self, event): self._checkGSellAll() def OnSubgSelAll(self, event): check = event.Checked() for item in range(self.subgListBox.GetCount()): self.CheckSubgItem(item, check) self.dataChanged = True event.Skip() def OnGSelAll(self, event): check = event.Checked() if not check: self.gLayerBox.DeselectAll() else: for item in range(self.subgListBox.GetCount()): self.gLayerBox.Select(item) event.Skip() def _checkGSellAll(self): check = False nsel = len(self.gLayerBox.GetSelections()) if self.gLayerBox.GetCount() == nsel and \ self.gLayerBox.GetCount() != 0: check = True self.g_sel_all.SetValue(check) def _checkSubGSellAll(self): not_all_checked = False if self.subgListBox.GetCount() == 0: not_all_checked = True else: for item in range(self.subgListBox.GetCount()): if not self.subgListBox.IsChecked(item): not_all_checked = True self.subg_sel_all.SetValue(not not_all_checked) def OnSubgroupFilter(self, event): text = event.GetString() self.gfilter.ChangeValue(text) self.flt_pattern = text self.FilterGroup() self.FilterSubgroup() event.Skip() def OnGroupFilter(self, event): text = event.GetString() self.subgfilter.ChangeValue(text) self.flt_pattern = text self.FilterGroup() self.FilterSubgroup() event.Skip() def OnSubgLayerCheck(self, event): idx = event.GetInt() m = self.subgListBox.GetString(idx) self.subgmaps[m] = self.subgListBox.IsChecked(idx) self.dataChanged = True self._checkSubGSellAll() def CheckSubgItem(self, idx, val): m = self.subgListBox.GetString(idx) self.subgListBox.Check(idx, val) self.subgmaps[m] = val self.dataChanged = val def DisableSubgroupEdit(self): """Disable editation of subgroups in the dialog .. todo:: used by gcp manager, maybe the gcp m should also support subgroups """ self.edit_subg = False self.subg_panel.Hide() self.subg_chbox.Hide() self.subgListBox.Hide() self.Layout() def OnSubgChbox(self, event): edit_subg = self.subg_chbox.GetValue() self.SubgChbox(edit_subg) def SubgChbox(self, edit_subg): self._checkChange() if edit_subg: self.edit_subg = edit_subg self.SubGroupSelected() self._subgroupLayout() else: self.edit_subg = edit_subg self.GroupSelected() self._groupLayout() self.SetMinSize(self.GetBestSize()) def _groupLayout(self): self.subg_panel.Hide() self.subgListPanel.Hide() self.gListPanel.Show() self.Layout() def _subgroupLayout(self): self.subg_panel.Show() self.subgListPanel.Show() self.gListPanel.Hide() self.Layout() def OnAddLayer(self, event): """Add new layer to listbox""" dlg = MapLayersDialogForGroups(parent = self, title = _("Add selected map layers into group")) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return layers = dlg.GetMapLayers() for layer in layers: if layer not in self.gmaps: self.gLayerBox.Append(layer) self.gmaps.append(layer) self.dataChanged = True def OnRemoveLayer(self, event): """Remove layer from listbox""" while self.gLayerBox.GetSelections(): sel = self.gLayerBox.GetSelections()[0] m = self.gLayerBox.GetString(sel) self.gLayerBox.Delete(sel) self.gmaps.remove(m) self.dataChanged = True def GetLayers(self): """Get layers""" if self.edit_subg: layers = [] for maps, sel in self.subgmaps.iteritems(): if sel: layers.append(maps) else: layers = self.gmaps[:] return layers def OnGroupSelected(self, event): """Text changed in group selector""" # callAfter must be called to close popup before other actions wx.CallAfter(self.GroupSelected) def GroupSelected(self): """Group was selected, check if changes were apllied""" self._checkChange() group, s = self.GetSelectedGroup() maps = list() groups = self.GetExistGroups() if group in groups: maps = self.GetGroupLayers(group) self.subGroupSelect.Insert(group) self.gmaps = maps maps = self._filter(maps) self.ShowGroupLayers(maps) self.currentGroup = group self.SubGroupSelected() self.ClearNotification() self._checkGSellAll() def FilterGroup(self): maps = self._filter(self.gmaps) self.ShowGroupLayers(maps) self._checkGSellAll() def FilterSubgroup(self): maps = self._filter(self.gmaps) self.subgListBox.Set(maps) for i, m in enumerate(maps): if m in self.subgmaps.iterkeys() and self.subgmaps[m]: self.subgListBox.Check(i) self._checkSubGSellAll() def SubGroupSelected(self): """Subgroup was selected, check if changes were apllied""" self._checkChange() subgroup = self.subGroupSelect.GetValue().strip() group = self.currentGroup gmaps = list() groups = self.GetExistGroups() self.subgmaps = {} if group in groups: gmaps = self.GetGroupLayers(group) if subgroup: maps = self.GetGroupLayers(group, subgroup) for m in maps: if m in gmaps: self.subgmaps[m] = True else: self.subgmaps[m] = False gmaps = self._filter(gmaps) self.subgListBox.Set(gmaps) for i, m in enumerate(gmaps): if self.subgmaps.has_key(m): self.subgListBox.Check(i) else: self.subgListBox.Check(i, False) self._checkSubGSellAll() self.currentSubgroup = subgroup self.ClearNotification() def _filter(self, data): """Apply filter for strings in data list""" flt_data = [] if len(self.flt_pattern) == 0: flt_data = data[:] return flt_data for dt in data: try: if re.compile(self.flt_pattern).search(dt): flt_data.append(dt) except: pass return flt_data def _checkChange(self): if self.edit_subg: self._checkSubgroupChange() else: self._checkGroupChange() def _checkGroupChange(self): if self.currentGroup and self.dataChanged: dlg = wx.MessageDialog(self, message = _("Group <%s> was changed, " "do you want to apply changes?") % self.currentGroup, caption = _("Unapplied changes"), style = wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT) if dlg.ShowModal() == wx.ID_YES: self.ApplyChanges() dlg.Destroy() self.dataChanged = False def _checkSubgroupChange(self): if self.currentSubgroup and self.dataChanged: dlg = wx.MessageDialog(self, message = _("Subgroup <%s> was changed, " "do you want to apply changes?") % self.currentSubgroup, caption = _("Unapplied changes"), style = wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT) if dlg.ShowModal() == wx.ID_YES: self.ApplyChanges() dlg.Destroy() self.dataChanged = False def ShowGroupLayers(self, mapList): """Show map layers in currently selected group""" self.gLayerBox.Set(mapList) def EditGroup(self, group, subgroup=None): """Edit selected group""" layersNew = self.GetLayers() layersOld = self.GetGroupLayers(group, subgroup) add = [] remove = [] for layerNew in layersNew: if layerNew not in layersOld: add.append(layerNew) for layerOld in layersOld: if layerOld not in layersNew: remove.append(layerOld) kwargs = {} if subgroup: kwargs["subgroup"] = subgroup ret = None if remove: ret = RunCommand('i.group', parent = self, group = group, flags = 'r', input = ','.join(remove), **kwargs) if add: ret = RunCommand('i.group', parent = self, group = group, input = ','.join(add), **kwargs) return ret def CreateNewGroup(self, group, subgroup): """Create new group""" layers = self.GetLayers() if not layers: GMessage(parent = self, message = _("No raster maps selected.")) return 1 kwargs = {} if subgroup: kwargs["subgroup"] = subgroup ret = RunCommand('i.group', parent = self, group = group, input = layers, **kwargs) #update subgroup select self.SubGroupSelected() return ret def GetExistGroups(self): """Returns existing groups in current mapset""" return grass.list_grouped('group')[grass.gisenv()['MAPSET']] def GetExistSubgroups(self, group): """Returns existing subgroups in a group""" return RunCommand('i.group', group=group, read=True, flags='sg').splitlines() def ShowResult(self, group, returnCode, create): """Show if operation was successfull.""" group += '@' + grass.gisenv()['MAPSET'] if returnCode is None: label = _("No changes to apply in group <%s>.") % group elif returnCode == 0: if create: label = _("Group <%s> was successfully created.") % group else: label = _("Group <%s> was successfully changed.") % group else: if create: label = _("Creating of new group <%s> failed.") % group else: label = _("Changing of group <%s> failed.") % group self.infoLabel.SetLabel(label) wx.FutureCall(4000, self.ClearNotification) def GetSelectedGroup(self): """Return currently selected group (without mapset)""" g = self.groupSelect.GetValue().split('@')[0] if self.edit_subg: s = self.subGroupSelect.GetValue() else: s = None return g, s def GetGroupLayers(self, group, subgroup=None): """Get layers in group""" kwargs = dict() kwargs['group'] = group if subgroup: kwargs['subgroup'] = subgroup res = RunCommand('i.group', parent = self, flags = 'g', read = True, **kwargs) if not res: return [] return res.splitlines() def ClearNotification(self): """Clear notification string""" self.infoLabel.SetLabel("") def ApplyChanges(self): """Create or edit group""" group = self.currentGroup if not group: GMessage(parent = self, message = _("No group selected.")) return False if self.edit_subg and not self.currentSubgroup: GMessage(parent = self, message = _("No subgroup selected.")) return 0 if self.edit_subg: subgroup = self.currentSubgroup else: subgroup = None groups = self.GetExistGroups() if group in groups: ret = self.EditGroup(group, subgroup) self.ShowResult(group = group, returnCode = ret, create = False) else: ret = self.CreateNewGroup(group, subgroup) self.ShowResult(group = group, returnCode = ret, create = True) self.dataChanged = False return True def OnApply(self, event): """Apply changes""" self.ApplyChanges() def OnOk(self, event): """Apply changes and close dialog""" if self.ApplyChanges(): self.OnClose(event) def OnClose(self, event): """Close dialog""" if not self.IsModal(): self.Destroy() event.Skip() class MapLayersDialogBase(wx.Dialog): """Base dialog for selecting map layers (raster, vector). There are 3 subclasses: MapLayersDialogForGroups, MapLayersDialogForModeler, MapLayersDialog. Base class contains core functionality. """ def __init__(self, parent, title, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style, **kwargs) self.parent = parent # GMFrame or ? self.applyAddingMapLayers = Signal('MapLayersDialogBase.applyAddingMapLayers') self.mainSizer = wx.BoxSizer(wx.VERTICAL) # dialog body self.bodySizer = self._createDialogBody() self.mainSizer.Add(item = self.bodySizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5) # update list of layer to be loaded self.map_layers = [] # list of map layers (full list type/mapset) self.LoadMapLayers(self.GetLayerType(cmd = True), self.mapset.GetStringSelection()) self._fullyQualifiedNames() self._modelerDSeries() # buttons btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL) btnOk = wx.Button(parent = self, id = wx.ID_OK) btnOk.SetDefault() # sizers & do layout self.btnSizer = wx.StdDialogButtonSizer() self.btnSizer.AddButton(btnCancel) self.btnSizer.AddButton(btnOk) self._addApplyButton() self.btnSizer.Realize() self.mainSizer.Add(item = self.btnSizer, proportion = 0, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) self.SetSizer(self.mainSizer) self.mainSizer.Fit(self) # set dialog min size self.SetMinSize(self.GetSize()) def _modelerDSeries(self): """Method used only by MapLayersDialogForModeler, for other subclasses does nothing. """ pass def _addApplyButton(self): """Method used only by MapLayersDialog, for other subclasses does nothing. """ pass def _fullyQualifiedNames(self): """Adds CheckBox which determines is fully qualified names are retuned. """ self.fullyQualified = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Use fully-qualified map names")) self.fullyQualified.SetValue(True) self.mainSizer.Add(item = self.fullyQualified, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5) def _useFullyQualifiedNames(self): return self.fullyQualified.IsChecked() def _layerTypes(self): """Determines which layer types can be chosen. Valid values: - raster - raster3d - vector """ return [_('raster'), _('3D raster'), _('vector')] def _selectAll(self): """Check all layers by default""" return True def _createDialogBody(self): bodySizer = wx.GridBagSizer(vgap = 3, hgap = 3) # layer type bodySizer.Add(item = wx.StaticText(parent = self, label = _("Map type:")), flag = wx.ALIGN_CENTER_VERTICAL, pos = (0,0)) self.layerType = wx.Choice(parent = self, id = wx.ID_ANY, choices = self._layerTypes(), size = (100,-1)) self.layerType.SetSelection(0) bodySizer.Add(item = self.layerType, pos = (0,1)) self.layerType.Bind(wx.EVT_CHOICE, self.OnChangeParams) # select toggle self.toggle = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Select toggle")) self.toggle.SetValue(self._selectAll()) bodySizer.Add(item = self.toggle, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0,2)) # mapset filter bodySizer.Add(item = wx.StaticText(parent = self, label = _("Mapset:")), flag = wx.ALIGN_CENTER_VERTICAL, pos = (1,0)) self.mapset = MapsetSelect(parent = self, searchPath = True) self.mapset.SetStringSelection(grass.gisenv()['MAPSET']) bodySizer.Add(item = self.mapset, pos = (1,1), span = (1, 2)) # map name filter bodySizer.Add(item = wx.StaticText(parent = self, label = _("Pattern:")), flag = wx.ALIGN_CENTER_VERTICAL, pos = (2,0)) self.filter = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (250,-1)) bodySizer.Add(item = self.filter, flag = wx.EXPAND, pos = (2,1), span = (1, 2)) self.filter.SetFocus() #TODO same text in GroupDialog self.filter.SetToolTipString(_("Put here a regular expression." " Characters '.*' stand for anything," " character '^' stands for the beginning" " and '$' for the end.")) # layer list bodySizer.Add(item = wx.StaticText(parent = self, label = _("List of maps:")), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_TOP, pos = (3,0)) self.layers = wx.CheckListBox(parent = self, id = wx.ID_ANY, size = (250, 100), choices = []) bodySizer.Add(item = self.layers, flag = wx.EXPAND, pos = (3,1), span = (1, 2)) bodySizer.AddGrowableCol(1) bodySizer.AddGrowableRow(3) # bindings self.mapset.Bind(wx.EVT_TEXT, self.OnChangeParams) self.layers.Bind(wx.EVT_RIGHT_DOWN, self.OnMenu) self.filter.Bind(wx.EVT_TEXT, self.OnFilter) self.toggle.Bind(wx.EVT_CHECKBOX, self.OnToggle) return bodySizer def LoadMapLayers(self, type, mapset): """Load list of map layers :param str type: layer type ('raster' or 'vector') :param str mapset: mapset name """ self.map_layers = grass.list_grouped(type = type)[mapset] self.layers.Set(natural_sort(self.map_layers)) # check all items by default for item in range(self.layers.GetCount()): self.layers.Check(item, check = self._selectAll()) def OnChangeParams(self, event): """Filter parameters changed by user""" # update list of layer to be loaded self.LoadMapLayers(self.GetLayerType(cmd = True), self.mapset.GetStringSelection()) event.Skip() def OnMenu(self, event): """Table description area, context menu""" if not hasattr(self, "popupID1"): self.popupDataID1 = wx.NewId() self.popupDataID2 = wx.NewId() self.popupDataID3 = wx.NewId() self.Bind(wx.EVT_MENU, self.OnSelectAll, id = self.popupDataID1) self.Bind(wx.EVT_MENU, self.OnSelectInvert, id = self.popupDataID2) self.Bind(wx.EVT_MENU, self.OnDeselectAll, id = self.popupDataID3) # generate popup-menu menu = wx.Menu() menu.Append(self.popupDataID1, _("Select all")) menu.Append(self.popupDataID2, _("Invert selection")) menu.Append(self.popupDataID3, _("Deselect all")) self.PopupMenu(menu) menu.Destroy() def OnSelectAll(self, event): """Select all map layer from list""" for item in range(self.layers.GetCount()): self.layers.Check(item, True) def OnSelectInvert(self, event): """Invert current selection""" for item in range(self.layers.GetCount()): if self.layers.IsChecked(item): self.layers.Check(item, False) else: self.layers.Check(item, True) def OnDeselectAll(self, event): """Select all map layer from list""" for item in range(self.layers.GetCount()): self.layers.Check(item, False) def OnFilter(self, event): """Apply filter for map names""" if len(event.GetString()) == 0: self.layers.Set(self.map_layers) return list = [] for layer in self.map_layers: try: if re.compile(event.GetString()).search(layer): list.append(layer) except: pass self.layers.Set(list) self.OnSelectAll(None) event.Skip() def OnToggle(self, event): """Select toggle (check or uncheck all layers)""" check = event.Checked() for item in range(self.layers.GetCount()): self.layers.Check(item, check) event.Skip() def GetMapLayers(self): """Return list of checked map layers""" layerNames = [] for indx in self.layers.GetSelections(): # layers.append(self.layers.GetStringSelec(indx)) pass mapset = self.mapset.GetStringSelection() for item in range(self.layers.GetCount()): if not self.layers.IsChecked(item): continue if self._useFullyQualifiedNames(): layerNames.append(self.layers.GetString(item) + '@' + mapset) else: layerNames.append(self.layers.GetString(item)) return layerNames def GetLayerType(self, cmd = False): """Get selected layer type :param bool cmd: True for g.list """ if not cmd: return self.layerType.GetStringSelection() sel = self.layerType.GetSelection() if sel == 0: ltype = 'raster' elif sel == 1: ltype = 'raster_3d' else: ltype = 'vector' return ltype class MapLayersDialog(MapLayersDialogBase): """Subclass of MapLayersDialogBase used in Layer Manager. Contains apply button, which sends wxApplyMapLayers event. """ def __init__(self, parent, title, **kwargs): MapLayersDialogBase.__init__(self, parent = parent, title = title, **kwargs) def _addApplyButton(self): btnApply = wx.Button(parent = self, id = wx.ID_APPLY) self.btnSizer.AddButton(btnApply) btnApply.Bind(wx.EVT_BUTTON, self.OnApply) def OnApply(self, event): self.applyAddingMapLayers.emit(mapLayers = self.GetMapLayers(), ltype = self.GetLayerType(cmd = True)) class MapLayersDialogForGroups(MapLayersDialogBase): """Subclass of MapLayersDialogBase used for specyfying maps in an imagery group. Shows only raster maps. """ def __init__(self, parent, title, **kwargs): MapLayersDialogBase.__init__(self, parent = parent, title = title, **kwargs) def _layerTypes(self): return [_('raster'),] def _selectAll(self): """Could be overriden""" return False def _fullyQualifiedNames(self): pass def _useFullyQualifiedNames(self): return True class MapLayersDialogForModeler(MapLayersDialogBase): """Subclass of MapLayersDialogBase used in Modeler. """ def __init__(self, parent, title, **kwargs): MapLayersDialogBase.__init__(self, parent = parent, title = title, **kwargs) def _modelerDSeries(self): self.dseries = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Dynamic series (%s)") % 'g.list') self.dseries.SetValue(False) self.mainSizer.Add(item = self.dseries, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5) def GetDSeries(self): """Used by modeler only :return: g.list command """ if not self.dseries or not self.dseries.IsChecked(): return '' cond = 'map in `g.list type=%s ' % self.GetLayerType(cmd = True) patt = self.filter.GetValue() if patt: cond += 'pattern=%s ' % patt cond += 'mapset=%s`' % self.mapset.GetStringSelection() return cond class SetOpacityDialog(wx.Dialog): """Set opacity of map layers. Dialog expects opacity between 0 and 1 and returns this range, too. """ def __init__(self, parent, id = wx.ID_ANY, title = _("Set Map Layer Opacity"), size = wx.DefaultSize, pos = wx.DefaultPosition, style = wx.DEFAULT_DIALOG_STYLE, opacity = 1): self.parent = parent # GMFrame self.opacity = opacity # current opacity super(SetOpacityDialog, self).__init__(parent, id = id, pos = pos, size = size, style = style, title = title) self.applyOpacity = Signal('SetOpacityDialog.applyOpacity') panel = wx.Panel(parent = self, id = wx.ID_ANY) sizer = wx.BoxSizer(wx.VERTICAL) box = wx.GridBagSizer(vgap = 5, hgap = 5) self.value = wx.Slider(panel, id = wx.ID_ANY, value = int(self.opacity * 100), style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \ wx.SL_TOP | wx.SL_LABELS, minValue = 0, maxValue = 100, size = (350, -1)) box.Add(item = self.value, flag = wx.ALIGN_CENTRE, pos = (0, 0), span = (1, 2)) box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("transparent")), pos = (1, 0)) box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("opaque")), flag = wx.ALIGN_RIGHT, pos = (1, 1)) sizer.Add(item = box, proportion = 0, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5) line = wx.StaticLine(parent = panel, id = wx.ID_ANY, style = wx.LI_HORIZONTAL) sizer.Add(item = line, proportion = 0, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5) # buttons btnsizer = wx.StdDialogButtonSizer() btnOK = wx.Button(parent = panel, id = wx.ID_OK) btnOK.SetDefault() btnsizer.AddButton(btnOK) btnCancel = wx.Button(parent = panel, id = wx.ID_CANCEL) btnsizer.AddButton(btnCancel) btnApply = wx.Button(parent = panel, id = wx.ID_APPLY) btnApply.Bind(wx.EVT_BUTTON, self.OnApply) btnsizer.AddButton(btnApply) btnsizer.Realize() sizer.Add(item = btnsizer, proportion = 0, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5) panel.SetSizer(sizer) sizer.Fit(panel) self.SetSize(self.GetBestSize()) self.Layout() def GetOpacity(self): """Button 'OK' pressed""" # return opacity value opacity = float(self.value.GetValue()) / 100 return opacity def OnApply(self, event): self.applyOpacity.emit(value = self.GetOpacity()) def GetImageHandlers(image): """Get list of supported image handlers""" lext = list() ltype = list() for h in image.GetHandlers(): lext.append(h.GetExtension()) filetype = '' if 'png' in lext: filetype += "PNG file (*.png)|*.png|" ltype.append({ 'type' : wx.BITMAP_TYPE_PNG, 'ext' : 'png' }) filetype += "BMP file (*.bmp)|*.bmp|" ltype.append({ 'type' : wx.BITMAP_TYPE_BMP, 'ext' : 'bmp' }) if 'gif' in lext: filetype += "GIF file (*.gif)|*.gif|" ltype.append({ 'type' : wx.BITMAP_TYPE_GIF, 'ext' : 'gif' }) if 'jpg' in lext: filetype += "JPG file (*.jpg)|*.jpg|" ltype.append({ 'type' : wx.BITMAP_TYPE_JPEG, 'ext' : 'jpg' }) if 'pcx' in lext: filetype += "PCX file (*.pcx)|*.pcx|" ltype.append({ 'type' : wx.BITMAP_TYPE_PCX, 'ext' : 'pcx' }) if 'pnm' in lext: filetype += "PNM file (*.pnm)|*.pnm|" ltype.append({ 'type' : wx.BITMAP_TYPE_PNM, 'ext' : 'pnm' }) if 'tif' in lext: filetype += "TIF file (*.tif)|*.tif|" ltype.append({ 'type' : wx.BITMAP_TYPE_TIF, 'ext' : 'tif' }) if 'xpm' in lext: filetype += "XPM file (*.xpm)|*.xpm" ltype.append({ 'type' : wx.BITMAP_TYPE_XPM, 'ext' : 'xpm' }) return filetype, ltype class ImageSizeDialog(wx.Dialog): """Set size for saved graphic file""" def __init__(self, parent, id = wx.ID_ANY, title = _("Set image size"), style = wx.DEFAULT_DIALOG_STYLE, **kwargs): self.parent = parent wx.Dialog.__init__(self, parent, id = id, style = style, title = title, **kwargs) self.panel = wx.Panel(parent = self, id = wx.ID_ANY) self.box = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, label = ' % s' % _("Image size")) size = self.parent.GetWindow().GetClientSize() self.width = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY, style = wx.SP_ARROW_KEYS) self.width.SetRange(20, 1e6) self.width.SetValue(size.width) wx.CallAfter(self.width.SetFocus) self.height = wx.SpinCtrl(parent = self.panel, id = wx.ID_ANY, style = wx.SP_ARROW_KEYS) self.height.SetRange(20, 1e6) self.height.SetValue(size.height) self.template = wx.Choice(parent = self.panel, id = wx.ID_ANY, size = (125, -1), choices = [ "", "640x480", "800x600", "1024x768", "1280x960", "1600x1200", "1920x1440" ]) self.btnOK = wx.Button(parent = self.panel, id = wx.ID_OK) self.btnOK.SetDefault() self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL) self.template.Bind(wx.EVT_CHOICE, self.OnTemplate) self._layout() self.SetSize(self.GetBestSize()) def _layout(self): """Do layout""" sizer = wx.BoxSizer(wx.VERTICAL) # body box = wx.StaticBoxSizer(self.box, wx.HORIZONTAL) fbox = wx.FlexGridSizer(cols = 2, vgap = 5, hgap = 5) fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Width:")), flag = wx.ALIGN_CENTER_VERTICAL) fbox.Add(item = self.width) fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Height:")), flag = wx.ALIGN_CENTER_VERTICAL) fbox.Add(item = self.height) fbox.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Template:")), flag = wx.ALIGN_CENTER_VERTICAL) fbox.Add(item = self.template) box.Add(item = fbox, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5) sizer.Add(item = box, proportion = 1, flag=wx.EXPAND | wx.ALL, border = 3) # buttons btnsizer = wx.StdDialogButtonSizer() btnsizer.AddButton(self.btnOK) btnsizer.AddButton(self.btnCancel) btnsizer.Realize() sizer.Add(item = btnsizer, proportion = 0, flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border=5) self.panel.SetSizer(sizer) sizer.Fit(self.panel) self.Layout() def GetValues(self): """Get width/height values""" return self.width.GetValue(), self.height.GetValue() def OnTemplate(self, event): """Template selected""" sel = event.GetString() if not sel: width, height = self.parent.GetWindow().GetClientSize() else: width, height = map(int, sel.split('x')) self.width.SetValue(width) self.height.SetValue(height) class SqlQueryFrame(wx.Frame): def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS SQL Query Utility"), *kwargs): """SQL Query Utility window """ self.parent = parent wx.Frame.__init__(self, parent = parent, id = id, title = title, *kwargs) 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) self.sqlBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, label = _(" SQL statement ")) self.sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, style = wx.TE_MULTILINE) self.btnApply = wx.Button(parent = self.panel, id = wx.ID_APPLY) self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL) self.Bind(wx.EVT_BUTTON, self.OnCloseWindow, self.btnCancel) self._layout() self.SetMinSize(wx.Size(300, 150)) self.SetSize(wx.Size(500, 200)) def _layout(self): """Do layout""" sizer = wx.BoxSizer(wx.VERTICAL) sqlSizer = wx.StaticBoxSizer(self.sqlBox, wx.HORIZONTAL) sqlSizer.Add(item = self.sql, proportion = 1, flag = wx.EXPAND) btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnApply) btnSizer.AddButton(self.btnCancel) btnSizer.Realize() sizer.Add(item = sqlSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5) sizer.Add(item = btnSizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) self.panel.SetSizer(sizer) self.Layout() def OnCloseWindow(self, event): """Close window """ self.Close() class SymbolDialog(wx.Dialog): """Dialog for GRASS symbols selection. Dialog is called in gui_core::forms module. """ def __init__(self, parent, symbolPath, currentSymbol = None, title = _("Symbols")): """Dialog constructor. It is assumed that symbolPath contains folders with symbols. :param parent: dialog parent :param symbolPath: absolute path to symbols :param currentSymbol: currently selected symbol (e.g. 'basic/x') :param title: dialog title """ wx.Dialog.__init__(self, parent = parent, title = title, id = wx.ID_ANY) self.symbolPath = symbolPath self.currentSymbol = currentSymbol # default basic/x self.selected = None self.selectedDir = None self._layout() def _layout(self): mainPanel = wx.Panel(self, id = wx.ID_ANY) mainSizer = wx.BoxSizer(wx.VERTICAL) vSizer = wx.BoxSizer( wx.VERTICAL) fgSizer = wx.FlexGridSizer(rows = 2, vgap = 5, hgap = 5) self.folderChoice = wx.Choice(mainPanel, id = wx.ID_ANY, choices = os.listdir(self.symbolPath)) self.folderChoice.Bind(wx.EVT_CHOICE, self.OnFolderSelect) fgSizer.Add(item = wx.StaticText(mainPanel, id = wx.ID_ANY, label = _("Symbol directory:")), proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL) fgSizer.Add(item = self.folderChoice, proportion = 0, flag = wx.ALIGN_CENTER, border = 0) self.infoLabel = wx.StaticText(mainPanel, id = wx.ID_ANY) fgSizer.Add(wx.StaticText(mainPanel, id = wx.ID_ANY, label = _("Symbol name:")), flag = wx.ALIGN_CENTRE_VERTICAL) fgSizer.Add(self.infoLabel, proportion = 0, flag = wx.ALIGN_CENTRE_VERTICAL) vSizer.Add(fgSizer, proportion = 0, flag = wx.ALL, border = 5) self.panels = self._createSymbolPanels(mainPanel) for panel in self.panels: vSizer.Add(panel, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5) mainSizer.Add(vSizer, proportion = 1, flag = wx.ALL| wx.EXPAND, border = 5) self.btnCancel = wx.Button(parent = mainPanel, id = wx.ID_CANCEL) self.btnOK = wx.Button(parent = mainPanel, id = wx.ID_OK) self.btnOK.SetDefault() self.btnOK.Enable(False) # buttons btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnCancel) btnSizer.AddButton(self.btnOK) btnSizer.Realize() mainSizer.Add(item = btnSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5) # show panel with the largest number of images and fit size count = [] for folder in os.listdir(self.symbolPath): count.append(len(os.listdir(os.path.join(self.symbolPath, folder)))) index = count.index(max(count)) self.folderChoice.SetSelection(index) self.OnFolderSelect(None) self.infoLabel.Show() mainPanel.SetSizerAndFit(mainSizer) self.SetSize(self.GetBestSize()) # show currently selected symbol if self.currentSymbol: # set directory self.selectedDir, self.selected = os.path.split(self.currentSymbol) self.folderChoice.SetStringSelection(self.selectedDir) # select symbol panelIdx = self.folderChoice.GetSelection() for panel in self.symbolPanels[panelIdx]: if panel.GetName() == self.selected: panel.Select() else: self.folderChoice.SetSelection(0) self.OnFolderSelect(None) def _createSymbolPanels(self, parent): """Creates multiple panels with symbols. Panels are shown/hidden according to selected folder.""" folders = os.listdir(self.symbolPath) panels = [] self.symbolPanels = [] for folder in folders: panel = wx.Panel(parent, style = wx.BORDER_RAISED) sizer = wx.GridSizer(cols = 6, vgap = 3, hgap = 3) images = self._getSymbols(path = os.path.join(self.symbolPath, folder)) symbolPanels = [] for img in images: iP = SingleSymbolPanel(parent = panel, symbolPath = img) iP.symbolSelectionChanged.connect(self.SelectionChanged) sizer.Add(item = iP, proportion = 0, flag = wx.ALIGN_CENTER) symbolPanels.append(iP) panel.SetSizerAndFit(sizer) panel.Hide() panels.append(panel) self.symbolPanels.append(symbolPanels) return panels def _getSymbols(self, path): # we assume that images are in subfolders (1 level only) imageList = [] for image in os.listdir(path): imageList.append(os.path.join(path, image)) return sorted(imageList) def OnFolderSelect(self, event): """Selected folder with symbols changed.""" idx = self.folderChoice.GetSelection() for i in range(len(self.panels)): sizer = self.panels[i].GetContainingSizer() sizer.Show(self.panels[i], i == idx, recursive = True) sizer.Layout() if self.selectedDir == self.folderChoice.GetStringSelection(): self.btnOK.Enable() self.infoLabel.SetLabel(self.selected) else: self.btnOK.Disable() self.infoLabel.SetLabel('') def SelectionChanged(self, name, doubleClick): """Selected symbol changed.""" if doubleClick: self.EndModal(wx.ID_OK) # deselect all for i in range(len(self.panels)): for panel in self.symbolPanels[i]: if panel.GetName() != name: panel.Deselect() self.btnOK.Enable() self.selected = name self.selectedDir = self.folderChoice.GetStringSelection() self.infoLabel.SetLabel(name) def GetSelectedSymbolName(self): """Returns currently selected symbol name (e.g. 'basic/x'). """ # separator must be '/' and not dependent on OS return self.selectedDir + '/' + self.selected def GetSelectedSymbolPath(self): """Returns currently selected symbol full path. """ return os.path.join(self.symbolPath, self.selectedDir, self.selected) class TextEntryDialog(wx.Dialog): """Simple dialog with text field. It differs from wx.TextEntryDialog because it allows adding validator. """ def __init__(self, parent, message, caption='', defaultValue='', validator=wx.DefaultValidator, style=wx.OK | wx.CANCEL, **kwargs): wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=caption, **kwargs) vbox = wx.BoxSizer(wx.VERTICAL) stline = wx.StaticText(self, id=wx.ID_ANY, label=message) vbox.Add(item=stline, proportion=0, flag=wx.EXPAND | wx.ALL, border=10) self._textCtrl = wx.TextCtrl(self, id=wx.ID_ANY, size = (300, -1), value=defaultValue, validator=validator) vbox.Add(item=self._textCtrl, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10) self._textCtrl.SetFocus() sizer = self.CreateSeparatedButtonSizer(style) vbox.Add(item=sizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10) self.SetSizerAndFit(vbox) def GetValue(self): return self._textCtrl.GetValue() def SetValue(self, value): self._textCtrl.SetValue(value) class HyperlinkDialog(wx.Dialog): """Dialog for displaying message with hyperlink.""" def __init__(self, parent, title, message, hyperlink, hyperlinkLabel=None, style=wx.OK): """Constructor :param parent: gui parent :param title: dialog title :param message: message :param hyperlink: url :param hyperlinkLabel: label shown instead of url :param style: button style """ wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=wx.DEFAULT_DIALOG_STYLE) sizer = wx.BoxSizer(wx.VERTICAL) label = wx.StaticText(self, label=message) sizer.Add(item=label, proportion=0, flag=wx.ALIGN_CENTRE|wx.ALL, border=10) hyperlinkLabel = hyperlinkLabel if hyperlinkLabel else hyperlink hyperlinkCtrl = wx.HyperlinkCtrl(self, id=wx.ID_ANY, label=hyperlinkLabel, url=hyperlink, style=wx.HL_ALIGN_LEFT|wx.HL_CONTEXTMENU) sizer.Add(item=hyperlinkCtrl, proportion=0, flag=wx.EXPAND|wx.ALL, border=10) btnsizer = self.CreateSeparatedButtonSizer(style) sizer.Add(item=btnsizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10) self.SetSizer(sizer) sizer.Fit(self) class QuitDialog(wx.Dialog): def __init__(self, parent, title=_("Quit GRASS GIS"), id=wx.ID_ANY, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, size=(350, 150), **kwargs): """Dialog to quit GRASS :param parent: window """ wx.Dialog.__init__(self, parent, id, title, style=style, size=size, **kwargs) self.panel = wx.Panel(parent = self, id = wx.ID_ANY) self.informLabel = wx.StaticText(parent=self.panel, id=wx.ID_ANY, label=_("Do you want to quit GRASS including shell " "prompt or just close the GUI?")) self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL) self.btnClose = wx.Button(parent = self.panel, id = wx.ID_NO, label=_("Close GUI")) self.btnQuit = wx.Button(parent = self.panel, id = wx.ID_YES, label=_("Quit GRASS")) self.btnQuit.SetDefault() self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose) self.btnQuit.Bind(wx.EVT_BUTTON, self.OnQuit) self.__layout() def __layout(self): """Do layout""" sizer = wx.BoxSizer(wx.VERTICAL) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(item=self.btnCancel, flag=wx.RIGHT, border=5) btnSizer.Add(item=self.btnClose, flag=wx.RIGHT, border=5) btnSizer.Add(item=self.btnQuit, flag=wx.RIGHT, border=5) sizer.Add(item = self.informLabel, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 25) sizer.Add(item = btnSizer, proportion = 0, flag = wx.ALL | wx.ALIGN_RIGHT, border = 5) self.panel.SetSizer(sizer) sizer.Fit(self.panel) self.Layout() def OnClose(self, event): self.Close() self.EndModal(wx.ID_NO) event.Skip() def OnQuit(self, event): self.Close() self.EndModal(wx.ID_YES) event.Skip()