def __init__(self, giface, cmdfile=None, mapfile=None): """Map composition (stack of map layers and overlays) :param cmdline: full path to the cmd file (defined by d.mon) :param mapfile: full path to the map file (defined by d.mon) """ Map.__init__(self) self._giface = giface # environment settings self.env = dict() self.cmdfile = cmdfile # list of layers for rendering added from cmd file # TODO temporary solution, layer managment by different tools in GRASS should be resovled self.ownedLayers = [] if mapfile: self.mapfileCmd = mapfile self.maskfileCmd = os.path.splitext(mapfile)[0] + '.pgm' # generated file for g.pnmcomp output for rendering the map self.mapfile = monFile['map'] if os.path.splitext(self.mapfile)[1] != '.ppm': self.mapfile += '.ppm' # signal sent when d.out.file/d.to.rast appears in cmd file, attribute is cmd self.saveToFile = Signal('DMonMap.saveToFile') self.dToRast = Signal('DMonMap.dToRast') # signal sent when d.what.rast/vect appears in cmd file, attribute is cmd self.query = Signal('DMonMap.query')
def __init__(self, parent, giface, Map, properties, tree=None, id=wx.ID_ANY, lmgr=None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): BufferedMapWindow.__init__(self, parent=parent, giface=giface, Map=Map, properties=properties, style=style, **kwargs) self.lmgr = lmgr self.tree = tree self.pdcVector = wx.PseudoDC() self.toolbar = self.parent.GetToolbar('vdigit') self.digit = None # wxvdigit.IVDigit self._digitizingInfo = False # digitizing with info # Emitted when info about digitizing updated # Parameter text is a string with information # currently used only for coordinates of mouse cursor + segmnt and # total feature length self.digitizingInfo = Signal('VDigitWindow.digitizingInfo') # Emitted when some info about digitizing is or will be availbale self.digitizingInfoAvailable = Signal('VDigitWindow.digitizingInfo') # Emitted when some info about digitizing is or will be availbale # digitizingInfo signal is emmited only between digitizingInfoAvailable # and digitizingInfoUnavailable signals self.digitizingInfoUnavailable = Signal('VDigitWindow.digitizingInfo') self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.mouseMoving.connect(self._mouseMovingToDigitizingInfo)
def __init__(self, bitmapPool, mapFilesPool, tempDir, imageWidth=640, imageHeight=480): self._bitmapPool = bitmapPool self._mapFilesPool = mapFilesPool self.imageWidth = imageWidth # width of the image to render with d.rast or d.vect # height of the image to render with d.rast or d.vect self.imageHeight = imageHeight self._tempDir = tempDir self._uniqueCmds = [] self._cmdsForComposition = [] self._opacities = [] self._cmds3D = [] self._regionFor3D = None self._regions = [] self._regionsForUniqueCmds = [] self._renderer = BitmapRenderer(self._mapFilesPool, self._tempDir, self.imageWidth, self.imageHeight) self._composer = BitmapComposer(self._tempDir, self._mapFilesPool, self._bitmapPool, self.imageWidth, self.imageHeight) self.renderingStarted = Signal('BitmapProvider.renderingStarted') self.compositionStarted = Signal('BitmapProvider.compositionStarted') self.renderingContinues = Signal('BitmapProvider.renderingContinues') self.compositionContinues = Signal( 'BitmapProvider.compositionContinues') self.renderingFinished = Signal('BitmapProvider.renderingFinished') self.compositionFinished = Signal('BitmapProvider.compositionFinished') self.mapsLoaded = Signal('BitmapProvider.mapsLoaded') self._renderer.renderingContinues.connect(self.renderingContinues) self._composer.compositionContinues.connect(self.compositionContinues)
class ProfileController(AnalysisControllerBase): """Class controls profiling in map display. It should be used inside ProfileFrame """ def __init__(self, giface, mapWindow): AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow) self.transectChanged = Signal('ProfileController.transectChanged') self._graphicsType = 'line' def _doAnalysis(self, coords): """Informs profile dialog that profile changed. :param coords: EN coordinates """ self.transectChanged.emit(coords=coords) def _disconnectAll(self): self._mapWindow.mouseLeftDown.disconnect(self._start) self._mapWindow.mouseLeftUp.disconnect(self._addPoint) def _connectAll(self): self._mapWindow.mouseLeftDown.connect(self._start) self._mapWindow.mouseLeftUp.connect(self._addPoint) def _getPen(self): return wx.Pen(colour=wx.Colour(0, 100, 0), width=2, style=wx.SHORT_DASH) def Stop(self, restore=True): AnalysisControllerBase.Stop(self, restore=restore) self.transectChanged.emit(coords=[])
def __init__(self, guiparent, giface): self.data = {} self.guiparent = guiparent self.giface = giface self.mapWin = giface.GetMapWindow() self.goutput = GConsole(guiparent = guiparent) self.vnet_data = VNETData(guiparent = guiparent, mapWin = self.mapWin) self.results = {"analysis" : None, "vect_map" : None} #TODO more results # this class instance manages all temporary vector maps created during life of VNETDialog self.tmp_maps = VNETTmpVectMaps(parent = guiparent, mapWin = self.mapWin) # initialization of History class used for saving and reading data from file # it is used for browsing analysis results self.history = VNETHistory(self.guiparent, self.vnet_data, self.tmp_maps) self.analyses = VNETAnalyses(self.vnet_data, self.RunAnDone, self.goutput, self.tmp_maps) self.snap_nodes = SnappingNodes(self.giface, self.vnet_data, self.tmp_maps, self.mapWin) self.ttbCreated = Signal('VNETManager.ttbCreated') self.analysisDone = Signal('VNETManager.analysisDone') self.pointsChanged = self.vnet_data.pointsChanged self.parametersChanged = self.vnet_data.parametersChanged self.snapping = self.snap_nodes.snapping self.pointsChanged.connect(self.PointsChanged)
def __init__(self, giface, mapWindow): wx.EvtHandler.__init__(self) self._giface = giface self._mapWindow = mapWindow self._thread = gThread() self._editedRaster = None self._backgroundRaster = None self._backupRasterName = None self._areas = None self._lines = None self._points = None self._all = [] self._drawing = False self._running = False self._drawColor = wx.GREEN self._drawTransparency = 100 self._graphicsType = 'area' self._currentCellValue = None self._currentWidthValue = None self._oldMouseUse = None self._oldCursor = None self.newRasterCreated = Signal('RDigitController:newRasterCreated') self.newFeatureCreated = Signal('RDigitController:newFeatureCreated') self.uploadMapCategories = Signal('RDigitController:uploadMapCategories') self.quitDigitizer = Signal('RDigitController:quitDigitizer') self.showNotification = Signal('RDigitController:showNotification')
def __init__(self, layer, mapfile, maskfile): if not haveGdal: sys.stderr.write(_("Unable to load GDAL Python bindings.\n"\ "WMS layers can not be displayed without the bindings.\n")) self.layer = layer wx.EvtHandler.__init__(self) # thread for d.wms commands self.thread = CmdThread(self) self.Bind(EVT_CMD_DONE, self.OnDataFetched) self.downloading = False self.renderedRegion = None self.updateMap = True self.fetched_data_cmd = None self.cmdStdErr = GStderr(self) self.mapfile = mapfile self.maskfile = maskfile self.tempMap = grass.tempfile() self.dstSize = {} self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) self.dataFetched = Signal('RenderWMSMgr.dataFetched') self.updateProgress = Signal('RenderWMSMgr.updateProgress')
def __init__(self): self.statisticsDict = {} self.statisticsList = [] self.statisticsAdded = Signal("StatisticsData.statisticsAdded") self.statisticsDeleted = Signal("StatisticsData.statisticsDeleted") self.allStatisticsDeleted = Signal("StatisticsData.allStatisticsDeleted") self.statisticsSet = Signal("StatisticsData.statisticsSet")
def __init__(self, mapframe, statusbar, sbManager, position=0): self.progressShown = Signal("SbProgress.progressShown") self.progressHidden = Signal("SbProgress.progressHidden") SbItem.__init__(self, mapframe, statusbar, position) self.name = "progress" self.sbManager = sbManager # on-render gauge self.widget = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY, range=0, style=wx.GA_HORIZONTAL) self.Hide()
def __init__(self): self._resolution = None self.resolutionChanged = Signal('MapWindowProperties.resolutionChanged') self._autoRender = None self.autoRenderChanged = Signal('MapWindowProperties.autoRenderChanged') self._showRegion = None self.showRegionChanged = Signal('MapWindowProperties.showRegionChanged') self._alignExtent = None self.alignExtentChanged = Signal('MapWindowProperties.alignExtentChanged')
def __init__(self, parent, samplingType, icon=None, map_=None): wx.Panel.__init__(self, parent=parent) self.mapWindowProperties = MapWindowProperties() self.mapWindowProperties.setValuesFromUserSettings() giface = StandaloneGrassInterface() self.samplingtype = samplingType self.parent = parent if map_: self.map_ = map_ else: self.map_ = Map() self.map_.region = self.map_.GetRegion() self._mgr = wx.aui.AuiManager(self) self.mapWindow = BufferedMapWindow(parent=self, giface=giface, Map=self.map_, properties=self.mapWindowProperties) self._mgr.AddPane(self.mapWindow, wx.aui.AuiPaneInfo().CentrePane(). Dockable(True).BestSize((-1, -1)).Name('mapwindow'). CloseButton(False).DestroyOnClose(True). Layer(0)) self._toolSwitcher = ToolSwitcher() self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged) self.toolbar = RLiSetupToolbar(self, self._toolSwitcher) self.catId = 1 self._mgr.AddPane(self.toolbar, wx.aui.AuiPaneInfo(). Name("maptoolbar").Caption(_("Map Toolbar")). ToolbarPane().Left().Name('mapToolbar'). CloseButton(False).Layer(1).Gripper(False). BestSize((self.toolbar.GetBestSize()))) self._mgr.Update() if self.samplingtype == SamplingType.REGIONS: self.afterRegionDrawn = Signal('RLiSetupMapPanel.afterRegionDrawn') self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line') elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged') self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle') elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: self.afterCircleDrawn = Signal('RLiSetupMapPanel.afterCircleDrawn') self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line') else: self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged') self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle') self._registeredGraphics.AddPen('rlisetup', wx.Pen(wx.GREEN, width=2, style=wx.SOLID)) self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]], penName='rlisetup', hide=True) if self.samplingtype != SamplingType.VECT: self.toolbar.SelectDefault()
def __init__(self, giface, mapWindow): """Constructs controller :param giface: grass interface object :param mapWindow: instance of BufferedMapWindow """ wx.EvtHandler.__init__(self) self._giface = giface self._mapWindow = mapWindow # thread for running rasterization process self._thread = gThread() # name of raster map which is edited (also new one) self._editedRaster = None # name of optional background raster self._backgroundRaster = None # name of temporary raster used to backup original state self._backupRasterName = None # if we edit an old raster or a new one (important for setting color # table) self._editOldRaster = False # type of output raster map (CELL, FCELL, DCELL) self._mapType = None # GraphicsSet for drawing areas, lines, points self._areas = None self._lines = None self._points = None # list of all GraphicsItems in the order of drawing self._all = [] # if in state of drawing lin or area self._drawing = False # if running digitizing process in thread (to block drawing) self._running = False # color used to draw (should be moved to settings) self._drawColor = wx.GREEN # transparency used to draw (should be moved to settings) self._drawTransparency = 100 # current selected drawing method self._graphicsType = 'area' # last edited cell value self._currentCellValue = None # last edited buffer value self._currentWidthValue = None self._oldMouseUse = None self._oldCursor = None # signal to add new raster to toolbar items self.newRasterCreated = Signal('RDigitController:newRasterCreated') # signal to add just used cell value in toolbar combo self.newFeatureCreated = Signal('RDigitController:newFeatureCreated') # signal to upload unique categories of background map into toolbar # combo self.uploadMapCategories = Signal( 'RDigitController:uploadMapCategories') self.quitDigitizer = Signal('RDigitController:quitDigitizer') self.showNotification = Signal('RDigitController:showNotification')
def __init__(self, guiparent, giface, iclass_mapwin=None): self.giface = giface self.mapDisp = giface.GetMapDisplay() if iclass_mapwin: self.mapWin = iclass_mapwin else: self.mapWin = giface.GetMapWindow() self.guiparent = guiparent self.show_add_scatt_plot = False self.core = Core() self.cats_mgr = CategoriesManager(self, self.core) self.render_mgr = PlotsRenderingManager(scatt_mgr=self, cats_mgr=self.cats_mgr, core=self.core) self.thread = gThread() self.plots = {} self.plot_mode = None self.pol_sel_mode = [False, None] self.data_set = False self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove") self.renderingStarted = self.render_mgr.renderingStarted self.renderingFinished = self.render_mgr.renderingFinished self.computingStarted = Signal("ScattsManager.computingStarted") if iclass_mapwin: self.digit_conn = IClassDigitConnection(self, self.mapWin, self.core.CatRastUpdater()) self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr) else: self.digit_conn = IMapWinDigitConnection() self.iclass_conn = IMapDispConnection(scatt_mgr=self, cats_mgr=self.cats_mgr, giface=self.giface) self._initSettings() self.modeSet = Signal("ScattsManager.mondeSet")
def __init__(self, parent, menuModel, margin = False): GPrompt.__init__(self, parent = parent, menuModel = menuModel) wx.stc.StyledTextCtrl.__init__(self, self.panel, id = wx.ID_ANY) # # styles # self.SetWrapMode(True) self.SetUndoCollection(True) # # create command and map lists for autocompletion # self.AutoCompSetIgnoreCase(False) # # line margins # # TODO print number only from cmdlog self.SetMarginWidth(1, 0) self.SetMarginWidth(2, 0) if margin: self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER) self.SetMarginWidth(0, 30) else: self.SetMarginWidth(0, 0) # # miscellaneous # self.SetViewWhiteSpace(False) self.SetUseTabs(False) self.UsePopUp(True) self.SetSelBackground(True, "#FFFF00") self.SetUseHorizontalScrollBar(True) # # bindings # self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) self.Bind(wx.EVT_CHAR, self.OnChar) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged) if sys.platform != 'darwin': # unstable on Mac with wxPython 3 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) # signal which requests showing of a notification self.showNotification = Signal('GPromptSTC.showNotification') # signal to notify selected command self.commandSelected = Signal('GPromptSTC.commandSelected')
def __init__(self, giface, cmdfile=None, mapfile=None): """Map composition (stack of map layers and overlays) :param cmdline: full path to the cmd file (defined by d.mon) :param mapfile: full path to the map file (defined by d.mon) """ Map.__init__(self) self._giface = giface # environment settings self.env = dict() self.cmdfile = cmdfile # list of layers for rendering added from cmd file # TODO temporary solution, layer managment by different tools in GRASS # should be resovled self.ownedLayers = [] self.oldOverlays = [] if mapfile: self.mapfileCmd = mapfile self.maskfileCmd = os.path.splitext(mapfile)[0] + '.pgm' # generated file for g.pnmcomp output for rendering the map self.mapfile = monFile['map'] if os.path.splitext(self.mapfile)[1] != '.ppm': self.mapfile += '.ppm' # signal sent when d.out.file/d.to.rast appears in cmd file, attribute # is cmd self.saveToFile = Signal('DMonMap.saveToFile') self.dToRast = Signal('DMonMap.dToRast') # signal sent when d.what.rast/vect appears in cmd file, attribute is # cmd self.query = Signal('DMonMap.query') self.renderMgr = RenderMapMgr(self) # update legend file variable with the one d.mon uses with open(monFile['env'], 'r') as f: lines = f.readlines() for line in lines: if 'GRASS_LEGEND_FILE' in line: legfile = line.split('=', 1)[1].strip() self.renderMgr.UpdateRenderEnv({'GRASS_LEGEND_FILE': legfile}) break
def __init__(self, gisrc = None): """Map composition (stack of map layers and overlays) :param gisrc: alternative gisrc (used eg. by georectifier) """ # region/extent settigns self.wind = dict() # WIND settings (wind file) self.region = dict() # region settings (g.region) self.width = 640 # map width self.height = 480 # map height # list of layers self.layers = list() # stack of available GRASS layer self.overlays = list() # stack of available overlays self.ovlookup = dict() # lookup dictionary for overlay items and overlays # path to external gisrc self.gisrc = gisrc # generated file for g.pnmcomp output for rendering the map self.mapfile = grass.tempfile(create = False) + '.ppm' # setting some initial env. variables if not self.GetWindow(): sys.stderr.write(_("Trying to recover from default region...")) RunCommand('g.region', flags='d') # info to report progress self.progressInfo = None # GRASS environment variable (for rendering) self.default_env = {"GRASS_RENDER_BACKGROUNDCOLOR" : "000000", "GRASS_RENDER_FILE_COMPRESSION" : "0", "GRASS_RENDER_TRUECOLOR" : "TRUE", "GRASS_RENDER_TRANSPARENT" : "TRUE" } # projection info self.projinfo = self._projInfo() # is some layer being downloaded? self.downloading = False self.layerChanged = Signal('Map.layerChanged') self.updateProgress = Signal('Map.updateProgress') self.layerRemoved = Signal('Map:layerRemoved') self.layerAdded = Signal('Map:layerAdded')
def __init__(self, scatt_mgr, core): self.core = core self.scatt_mgr = scatt_mgr self.cats = {} self.cats_ids = [] self.sel_cat_id = None self.exportRaster = None self.initialized = Signal('CategoriesManager.initialized') self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs') self.deletedCategory = Signal('CategoriesManager.deletedCategory') self.addedCategory = Signal('CategoriesManager.addedCategory')
class GConsole(wx.EvtHandler): """ """ def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None): """ :param guiparent: parent window for created GUI objects :param lmgr: layer manager window (TODO: replace by giface) :param ignoredCmdPattern: regular expression specifying commads to be ignored (e.g. @c '^d\..*' for display commands) """ wx.EvtHandler.__init__(self) # Signal when some map is created or updated by a module. # attributes: name: map name, ltype: map type, self.mapCreated = Signal('GConsole.mapCreated') # emitted when map display should be re-render self.updateMap = Signal('GConsole.updateMap') # emitted when log message should be written self.writeLog = Signal('GConsole.writeLog') # emitted when command log message should be written self.writeCmdLog = Signal('GConsole.writeCmdLog') # emitted when warning message should be written self.writeWarning = Signal('GConsole.writeWarning') # emitted when error message should be written self.writeError = Signal('GConsole.writeError') self._guiparent = guiparent self._giface = giface self._ignoredCmdPattern = ignoredCmdPattern # create queues self.requestQ = Queue.Queue() self.resultQ = Queue.Queue() self.cmdOutputTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents) self.Bind(EVT_CMD_RUN, self.OnCmdRun) self.Bind(EVT_CMD_DONE, self.OnCmdDone) self.Bind(EVT_CMD_ABORT, self.OnCmdAbort) # stream redirection self.cmdStdOut = GStdout(receiver=self) self.cmdStdErr = GStderr(receiver=self) # thread self.cmdThread = CmdThread(self, self.requestQ, self.resultQ) def Redirect(self): """Redirect stdout/stderr """ if Debug.GetLevel() == 0 and int(grass.gisenv().get('DEBUG', 0)) == 0: # don't redirect when debugging is enabled sys.stdout = self.cmdStdOut sys.stderr = self.cmdStdErr else: enc = locale.getdefaultlocale()[1] if enc: sys.stdout = codecs.getwriter(enc)(sys.__stdout__) sys.stderr = codecs.getwriter(enc)(sys.__stderr__) else: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ def WriteLog(self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT): """Generic method for writing log message in given style :param text: text line :param notification: form of notification """ self.writeLog.emit(text=text, wrap=wrap, notification=notification) def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE): """Write message in selected style :param text: message to be printed :param pid: process pid or None :param notification: form of notification """ self.writeCmdLog.emit(text=text, pid=pid, notification=notification) def WriteWarning(self, text): """Write message in warning style""" self.writeWarning.emit(text=text) def WriteError(self, text): """Write message in error style""" self.writeError.emit(text=text) def RunCmd(self, command, compReg=True, skipInterface=False, onDone=None, onPrepare=None, userData=None, notification=Notification.MAKE_VISIBLE): """Run command typed into console command prompt (GPrompt). .. todo:: Document the other event. .. todo:: Solve problem with the other event (now uses gOutputText event but there is no text, use onPrepare handler instead?) Posts event EVT_IGNORED_CMD_RUN when command which should be ignored (according to ignoredCmdPattern) is run. For example, see layer manager which handles d.* on its own. :param command: command given as a list (produced e.g. by utils.split()) :param compReg: True use computation region :param notification: form of notification :param bool skipInterface: True to do not launch GRASS interface parser when command has no arguments given :param onDone: function to be called when command is finished :param onPrepare: function to be called before command is launched :param userData: data defined for the command """ if len(command) == 0: Debug.msg(2, "GPrompt:RunCmd(): empty command") return # update history file self.UpdateHistoryFile(' '.join(command)) if command[0] in globalvar.grassCmd: # send GRASS command without arguments to GUI command interface # except ignored commands (event is emitted) if self._ignoredCmdPattern and \ re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \ '--help' not in command and '--ui' not in command: event = gIgnoredCmdRun(cmd=command) wx.PostEvent(self, event) return else: # other GRASS commands (r|v|g|...) try: task = GUI(show=None).ParseCommand(command) except GException as e: GError(parent=self._guiparent, message=unicode(e), showTraceback=False) return hasParams = False if task: options = task.get_options() hasParams = options['params'] and options['flags'] # check for <input>=- for p in options['params']: if p.get('prompt', '') == 'input' and \ p.get('element', '') == 'file' and \ p.get('age', 'new') == 'old' and \ p.get('value', '') == '-': GError( parent=self._guiparent, message= _("Unable to run command:\n%(cmd)s\n\n" "Option <%(opt)s>: read from standard input is not " "supported by wxGUI") % { 'cmd': ' '.join(command), 'opt': p.get('name', '') }) return if len(command) == 1 and hasParams and \ command[0] != 'v.krige': # no arguments given try: GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command) except GException as e: print >> sys.stderr, e return # activate computational region (set with g.region) # for all non-display commands. if compReg: tmpreg = os.getenv("GRASS_REGION") if "GRASS_REGION" in os.environ: del os.environ["GRASS_REGION"] # process GRASS command with argument self.cmdThread.RunCmd(command, stdout=self.cmdStdOut, stderr=self.cmdStdErr, onDone=onDone, onPrepare=onPrepare, userData=userData, env=os.environ.copy(), notification=notification) self.cmdOutputTimer.Start(50) # deactivate computational region and return to display settings if compReg and tmpreg: os.environ["GRASS_REGION"] = tmpreg else: # Send any other command to the shell. Send output to # console output window # # Check if the script has an interface (avoid double-launching # of the script) # check if we ignore the command (similar to grass commands part) if self._ignoredCmdPattern and \ re.compile(self._ignoredCmdPattern).search(' '.join(command)): event = gIgnoredCmdRun(cmd=command) wx.PostEvent(self, event) return skipInterface = True if os.path.splitext(command[0])[1] in ('.py', '.sh'): try: sfile = open(command[0], "r") for line in sfile.readlines(): if len(line) < 2: continue if line[0] is '#' and line[1] is '%': skipInterface = False break sfile.close() except IOError: pass if len(command) == 1 and not skipInterface: try: task = gtask.parse_interface(command[0]) except: task = None else: task = None if task: # process GRASS command without argument GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command) else: self.cmdThread.RunCmd(command, stdout=self.cmdStdOut, stderr=self.cmdStdErr, onDone=onDone, onPrepare=onPrepare, userData=userData, notification=notification) self.cmdOutputTimer.Start(50) def GetLog(self, err=False): """Get widget used for logging .. todo:: what's this? :param bool err: True to get stderr widget """ if err: return self.cmdStdErr return self.cmdStdOut def GetCmd(self): """Get running command or None""" return self.requestQ.get() def OnCmdAbort(self, event): """Abort running command""" self.cmdThread.abort() event.Skip() def OnCmdRun(self, event): """Run command""" self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)), notification=event.notification) event.Skip() def OnCmdDone(self, event): """Command done (or aborted) Sends signal mapCreated if map is recognized in output parameters or for specific modules (as r.colors). """ # Process results here try: ctime = time.time() - event.time if ctime < 60: stime = _("%d sec") % int(ctime) else: mtime = int(ctime / 60) stime = _("%(min)d min %(sec)d sec") % { 'min': mtime, 'sec': int(ctime - (mtime * 60)) } except KeyError: # stopped deamon stime = _("unknown") if event.aborted: # Thread aborted (using our convention of None return) self.WriteWarning( _('Please note that the data are left in' ' inconsistent state and may be corrupted')) msg = _('Command aborted') else: msg = _('Command finished') self.WriteCmdLog('(%s) %s (%s)' % (str(time.ctime()), msg, stime), notification=event.notification) if event.onDone: event.onDone(cmd=event.cmd, returncode=event.returncode) self.cmdOutputTimer.Stop() if event.cmd[0] == 'g.gisenv': Debug.SetLevel() self.Redirect() # do nothing when no map added if event.returncode != 0 or event.aborted: event.Skip() return if event.cmd[0] not in globalvar.grassCmd: return # find which maps were created try: task = GUI(show=None).ParseCommand(event.cmd) except GException as e: print >> sys.stderr, e task = None return name = task.get_name() for p in task.get_options()['params']: prompt = p.get('prompt', '') if prompt in ('raster', 'vector', '3d-raster') and p.get( 'value', None): if p.get('age', 'old') == 'new' or \ name in ('r.colors', 'r3.colors', 'v.colors', 'v.proj', 'r.proj'): # if multiple maps (e.g. r.series.interp), we need add each if p.get('multiple', False): lnames = p.get('value').split(',') # in case multiple input (old) maps in r.colors # we don't want to rerender it multiple times! just once if p.get('age', 'old') == 'old': lnames = lnames[0:1] else: lnames = [p.get('value')] for lname in lnames: if '@' not in lname: lname += '@' + grass.gisenv()['MAPSET'] self.mapCreated.emit(name=lname, ltype=prompt) if name == 'r.mask': self.updateMap.emit() event.Skip() def OnProcessPendingOutputWindowEvents(self, event): wx.GetApp().ProcessPendingEvents() def UpdateHistoryFile(self, command): """Update history file :param command: the command given as a string """ env = grass.gisenv() try: filePath = os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'], '.bash_history') fileHistory = codecs.open(filePath, encoding='utf-8', mode='a') except IOError as e: GError( _("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") % { 'filePath': filePath, 'error': e }, parent=self._guiparent) return try: fileHistory.write(command + os.linesep) finally: fileHistory.close() # update wxGUI prompt if self._giface: self._giface.UpdateCmdHistory(command)
class RLiSetupMapPanel(wx.Panel): """Panel with mapwindow used in r.li.setup""" def __init__(self, parent, samplingType, icon=None, map_=None): wx.Panel.__init__(self, parent=parent) self.mapWindowProperties = MapWindowProperties() self.mapWindowProperties.setValuesFromUserSettings() giface = StandaloneGrassInterface() self.samplingtype = samplingType self.parent = parent if map_: self.map_ = map_ else: self.map_ = Map() self.map_.region = self.map_.GetRegion() self._mgr = wx.aui.AuiManager(self) self.mapWindow = BufferedMapWindow( parent=self, giface=giface, Map=self.map_, properties=self.mapWindowProperties, ) self._mgr.AddPane( self.mapWindow, wx.aui.AuiPaneInfo().CentrePane().Dockable(True).BestSize( (-1, -1)).Name("mapwindow").CloseButton(False).DestroyOnClose( True).Layer(0), ) self._toolSwitcher = ToolSwitcher() self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged) self.toolbar = RLiSetupToolbar(self, self._toolSwitcher) self.catId = 1 self._mgr.AddPane( self.toolbar, wx.aui.AuiPaneInfo().Name("maptoolbar").Caption( _("Map Toolbar")).ToolbarPane().Left().Name("mapToolbar"). CloseButton(False).Layer(1).Gripper(False).BestSize( (self.toolbar.GetBestSize())), ) self._mgr.Update() if self.samplingtype == SamplingType.REGIONS: self.afterRegionDrawn = Signal("RLiSetupMapPanel.afterRegionDrawn") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line") elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: self.sampleFrameChanged = Signal( "RLiSetupMapPanel.sampleFrameChanged") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="rectangle") elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: self.afterCircleDrawn = Signal("RLiSetupMapPanel.afterCircleDrawn") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line") else: self.sampleFrameChanged = Signal( "RLiSetupMapPanel.sampleFrameChanged") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="rectangle") self._registeredGraphics.AddPen( "rlisetup", wx.Pen(wx.GREEN, width=2, style=wx.SOLID)) self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]], penName="rlisetup", hide=True) if self.samplingtype != SamplingType.VECT: self.toolbar.SelectDefault() def GetMap(self): return self.map_ def OnPan(self, event): """Panning, set mouse to drag.""" self.mapWindow.SetModePan() def OnZoomIn(self, event): """Zoom in the map.""" self.mapWindow.SetModeZoomIn() def OnZoomOut(self, event): """Zoom out the map.""" self.mapWindow.SetModeZoomOut() def OnZoomToMap(self, event): layers = self.map_.GetListOfLayers() self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True) def OnDrawRadius(self, event): """Start draw mode""" self.mapWindow.mouse["use"] = "None" self.mapWindow.mouse["box"] = "line" self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1, style=wx.SHORT_DASH) self.mapWindow.SetNamedCursor("cross") self.mapWindow.mouseLeftUp.connect(self._radiusDrawn) def OnDigitizeRegion(self, event): """Start draw mode""" self.mapWindow.mouse["use"] = "None" self.mapWindow.mouse["box"] = "line" self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1, style=wx.SHORT_DASH) self.mapWindow.SetNamedCursor("cross") self.mapWindow.mouseLeftUp.connect(self._lineSegmentDrawn) self.mapWindow.mouseDClick.connect(self._mouseDbClick) self._registeredGraphics.GetItem(0).SetCoords([]) def OnDraw(self, event): """Start draw mode""" self.mapWindow.mouse["use"] = "None" self.mapWindow.mouse["box"] = "box" self.mapWindow.pen = wx.Pen(colour=wx.RED, width=2, style=wx.SHORT_DASH) self.mapWindow.SetNamedCursor("cross") self.mapWindow.mouseLeftUp.connect(self._rectangleDrawn) def _lineSegmentDrawn(self, x, y): item = self._registeredGraphics.GetItem(0) coords = item.GetCoords() if len(coords) == 0: coords.extend( [self.mapWindow.Pixel2Cell(self.mapWindow.mouse["begin"])]) coords.extend([[x, y]]) item.SetCoords(coords) item.SetPropertyVal("hide", False) self.mapWindow.ClearLines() self._registeredGraphics.Draw() def _mouseDbClick(self, x, y): item = self._registeredGraphics.GetItem(0) coords = item.GetCoords() coords.extend([[x, y]]) item.SetCoords(coords) item.SetPropertyVal("hide", False) self.mapWindow.ClearLines() self._registeredGraphics.Draw() self.createRegion() def createRegion(self): dlg = wx.TextEntryDialog(None, "Name of sample region", "Create region", "region" + str(self.catId)) ret = dlg.ShowModal() while True: if ret == wx.ID_OK: raster = dlg.GetValue() if checkMapExists(raster): GMessage( parent=self, message=_("The raster file %s already" " exists, please change name") % raster, ) ret = dlg.ShowModal() else: dlg.Destroy() marea = self.writeArea( self._registeredGraphics.GetItem(0).GetCoords(), raster) self.nextRegion(next=True, area=marea) break else: self.nextRegion(next=False) break def nextRegion(self, next=True, area=None): self.mapWindow.ClearLines() item = self._registeredGraphics.GetItem(0) item.SetCoords([]) item.SetPropertyVal("hide", True) layers = self.map_.GetListOfLayers() self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True) if next is True: self.afterRegionDrawn.emit(marea=area) else: gcmd.GMessage( parent=self.parent, message=_("Raster map not created. Please redraw region."), ) def writeArea(self, coords, rasterName): polyfile = tempfile.NamedTemporaryFile(delete=False) polyfile.write("AREA\n") for coor in coords: east, north = coor point = " %s %s\n" % (east, north) polyfile.write(point) catbuf = "=%d a\n" % self.catId polyfile.write(catbuf) self.catId = self.catId + 1 polyfile.close() region_settings = grass.parse_command("g.region", flags="p", delimiter=":") pname = polyfile.name.split("/")[-1] tmpraster = "rast_" + pname tmpvector = "vect_" + pname wx.BeginBusyCursor() wx.GetApp().Yield() RunCommand( "r.in.poly", input=polyfile.name, output=tmpraster, rows=region_settings["rows"], overwrite=True, ) RunCommand("r.to.vect", input=tmpraster, output=tmpvector, type="area", overwrite=True) RunCommand("v.to.rast", input=tmpvector, output=rasterName, value=1, use="val") wx.EndBusyCursor() grass.use_temp_region() grass.run_command("g.region", vector=tmpvector) region = grass.region() marea = MaskedArea(region, rasterName) RunCommand("g.remove", flags="f", type="raster", name=tmpraster) RunCommand("g.remove", flags="f", type="vector", name=tmpvector) os.unlink(polyfile.name) return marea def _onToolChanged(self): """Helper function to disconnect drawing""" try: self.mapWindow.mouseLeftUp.disconnect(self._rectangleDrawn) self.mapWindow.mouseLeftUp.disconnect(self._radiusDrawn) self.mapWindow.mouseMoving.disconnect(self._mouseMoving) self.mapWindow.mouseLeftDown.disconnect(self._mouseLeftDown) self.mapWindow.mouseDClick.disconnect(self._mouseDbClick) except DispatcherKeyError: pass def _radiusDrawn(self, x, y): """When drawing finished, get region values""" mouse = self.mapWindow.mouse item = self._registeredGraphics.GetItem(0) p1 = mouse["begin"] p2 = mouse["end"] dist, (north, east) = self.mapWindow.Distance(p1, p2, False) circle = Circle(p1, dist) self.mapWindow.ClearLines() self.mapWindow.pdcTmp.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT)) pen = wx.Pen(colour=wx.RED, width=2) self.mapWindow.pdcTmp.SetPen(pen) self.mapWindow.pdcTmp.DrawCircle(circle.point[0], circle.point[1], circle.radius) self._registeredGraphics.Draw() self.createCricle(circle) def createCricle(self, c): dlg = wx.TextEntryDialog( None, "Name of sample circle region", "Create circle region", "circle" + str(self.catId), ) ret = dlg.ShowModal() while True: if ret == wx.ID_OK: raster = dlg.GetValue() if checkMapExists(raster): GMessage( parent=self, message=_("The raster file %s already" " exists, please change name") % raster, ) ret = dlg.ShowModal() else: dlg.Destroy() circle = self.writeCircle(c, raster) self.nextCircle(next=True, circle=circle) break else: self.nextCircle(next=False) break def nextCircle(self, next=True, circle=None): self.mapWindow.ClearLines() item = self._registeredGraphics.GetItem(0) item.SetPropertyVal("hide", True) layers = self.map_.GetListOfLayers() self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True) if next is True: self.afterCircleDrawn.emit(region=circle) else: gcmd.GMessage( parent=self.parent, message=_("Raster map not created. redraw region again."), ) def writeCircle(self, circle, rasterName): coords = self.mapWindow.Pixel2Cell(circle.point) RunCommand( "r.circle", output=rasterName, max=circle.radius, coordinate=coords, flags="b", ) grass.use_temp_region() grass.run_command("g.region", zoom=rasterName) region = grass.region() marea = MaskedArea(region, rasterName, circle.radius) return marea def _rectangleDrawn(self): """When drawing finished, get region values""" mouse = self.mapWindow.mouse item = self._registeredGraphics.GetItem(0) p1 = self.mapWindow.Pixel2Cell(mouse["begin"]) p2 = self.mapWindow.Pixel2Cell(mouse["end"]) item.SetCoords([p1, p2]) region = { "n": max(p1[1], p2[1]), "s": min(p1[1], p2[1]), "w": min(p1[0], p2[0]), "e": max(p1[0], p2[0]), } item.SetPropertyVal("hide", False) self.mapWindow.ClearLines() self._registeredGraphics.Draw() if self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: dlg = wx.MessageDialog( self, "Is this area ok?", "select sampling unit", wx.YES_NO | wx.ICON_QUESTION, ) ret = dlg.ShowModal() if ret == wx.ID_YES: grass.use_temp_region() grass.run_command( "g.region", n=region["n"], s=region["s"], e=region["e"], w=region["w"], ) tregion = grass.region() self.sampleFrameChanged.emit(region=tregion) self.mapWindow.ClearLines() item = self._registeredGraphics.GetItem(0) item.SetPropertyVal("hide", True) layers = self.map_.GetListOfLayers() self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True) else: self.nextRegion(next=False) dlg.Destroy() elif self.samplingtype != SamplingType.WHOLE: """When drawing finished, get region values""" self.sampleFrameChanged.emit(region=region)
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._restricted = True self._initVariablesCatalog() self.beginDrag = Signal('DataCatalogTree.beginDrag') self.endDrag = Signal('DataCatalogTree.endDrag') self.startEdit = Signal('DataCatalogTree.startEdit') self.endEdit = Signal('DataCatalogTree.endEdit') self.Bind( wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.beginDrag, event=evt)) self.Bind( wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind( wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.startEdit, event=evt)) self.Bind( wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.endEdit, event=evt)) self.startEdit.connect(self.OnStartEditLabel) self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_mode = False self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def SetRestriction(self, restrict): self._restricted = restrict def _runCommand(self, prog, **kwargs): cmdString = ' '.join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnMoveMap(self, event): """Move layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_mode = False self.copy_layer = self.selected_layer[:] self.copy_type = self.selected_type[:] self.copy_mapset = self.selected_mapset[:] self.copy_location = self.selected_location[:] if len(self.copy_layer) > 1: label = _("{c} maps marked for moving.").format( c=len(self.selected_layer)) else: label = _("Map <{layer}> marked for moving.").format( layer=self.copy_layer[0].label) self.showNotification.emit(message=label) def OnCopyMap(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_mode = True self.copy_layer = self.selected_layer[:] self.copy_type = self.selected_type[:] self.copy_mapset = self.selected_mapset[:] self.copy_location = self.selected_location[:] if len(self.copy_layer) > 1: label = _("{c} maps marked for copying.").format( c=len(self.selected_layer)) else: label = _("Map <{layer}> marked for copying.").format( layer=self.copy_layer[0].label) self.showNotification.emit(message=label) def OnRenameMap(self, event): """Rename layer with dialog""" old_name = self.selected_layer[0].label gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location[0].label, mapset=self.selected_mapset[0].label) new_name = self._getNewMapName(_('New name'), _('Rename map'), old_name, env=env, mapset=self.selected_mapset[0].label, element=self.selected_type[0].label) if new_name: self.Rename(old_name, new_name) def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems([node]) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): old_name = node.label Debug.msg(1, "End label edit {name}".format(name=old_name)) new_name = event.GetLabel() self.Rename(old_name, new_name) def Rename(self, old, new): """Rename layer""" string = old + ',' + new gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location[0].label, self.selected_mapset[0].label) label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type[0].label == 'vector': renamed, cmd = self._runCommand('g.rename', vector=string, env=env) elif self.selected_type[0].label == 'raster': renamed, cmd = self._runCommand('g.rename', raster=string, env=env) else: renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env) if renamed == 0: self.selected_layer[0].label = new self.selected_layer[0].data['name'] = new self.RefreshNode(self.selected_layer[0]) self.showNotification.emit(message=_("{cmd} -- completed").format( cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + new) gscript.try_remove(gisrc) def OnPasteMap(self, event): # copying between mapsets of one location if not self.copy_layer: if self.copy_mode: GMessage(_("No map selected for copying."), parent=self) else: GMessage(_("No map selected for moving."), parent=self) return for i in range(len(self.copy_layer)): gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location[0].label, mapset=self.selected_mapset[0].label) gisrc2, env2 = gscript.create_environment( gisenv()['GISDBASE'], self.copy_location[i].label, mapset=self.copy_mapset[i].label) new_name = self.copy_layer[i].label if self.selected_location[0] == self.copy_location[i]: # within one mapset if self.selected_mapset[0] == self.copy_mapset[i]: # ignore when just moves map if self.copy_mode is False: return new_name = self._getNewMapName( _('New name for <{n}>').format( n=self.copy_layer[i].label), _('Select new name'), self.copy_layer[i].label, env=env, mapset=self.selected_mapset[0].label, element=self.copy_type[i].label) if not new_name: return # within one location, different mapsets else: if map_exists(new_name, element=self.copy_type[i].label, env=env, mapset=self.selected_mapset[0].label): new_name = self._getNewMapName( _('New name for <{n}>').format( n=self.copy_layer[i].label), _('Select new name'), self.copy_layer[i].label, env=env, mapset=self.selected_mapset[0].label, element=self.copy_type[i].label) if not new_name: return string = self.copy_layer[i].label + '@' + self.copy_mapset[ i].label + ',' + new_name pasted = 0 if self.copy_mode: label = _("Copying <{name}>...").format(name=string) else: label = _("Moving <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type[i].label == 'vector': pasted, cmd = self._runCommand('g.copy', vector=string, env=env) node = 'vector' elif self.copy_type[i].label == 'raster': pasted, cmd = self._runCommand('g.copy', raster=string, env=env) node = 'raster' else: pasted, cmd = self._runCommand('g.copy', raster_3d=string, env=env) node = 'raster_3d' if pasted == 0: self.InsertLayer(name=new_name, mapset_node=self.selected_mapset[0], element_name=node) Debug.msg(1, "COPIED TO: " + new_name) if self.copy_mode: self.showNotification.emit( message=_("g.copy completed")) else: self.showNotification.emit( message=_("g.copy completed")) # remove old if not self.copy_mode: self._removeMapAfterCopy(self.copy_layer[i], self.copy_type[i], env2) gscript.try_remove(gisrc) gscript.try_remove(gisrc2) # expand selected mapset else: if self.copy_type[i].label == 'raster_3d': GError(_("Reprojection is not implemented for 3D rasters"), parent=self) return if map_exists(new_name, element=self.copy_type[i].label, env=env, mapset=self.selected_mapset[0].label): new_name = self._getNewMapName( _('New name'), _('Select new name'), self.copy_layer[i].label, env=env, mapset=self.selected_mapset[0].label, element=self.copy_type[i].label) if not new_name: continue gisdbase = gisenv()['GISDBASE'] callback = lambda gisrc2=gisrc2, gisrc=gisrc, cLayer=self.copy_layer[i], \ cType=self.copy_type[i], cMode=self.copy_mode, name=new_name: \ self._onDoneReprojection(env2, gisrc2, gisrc, cLayer, cType, cMode, name) dlg = CatalogReprojectionDialog( self, self._giface, gisdbase, self.copy_location[i].label, self.copy_mapset[i].label, self.copy_layer[i].label, env2, gisdbase, self.selected_location[0].label, self.selected_mapset[0].label, new_name, self.copy_type[i].label, env, callback) dlg.ShowModal() self.ExpandNode(self.selected_mapset[0], recursive=True) self._initVariablesCatalog() def _onDoneReprojection(self, iEnv, iGisrc, oGisrc, cLayer, cType, cMode, name): self.InsertLayer(name=name, mapset_node=self.selected_mapset[0], element_name=cType.label) if not cMode: self._removeMapAfterCopy(cLayer, cType, iEnv) gscript.try_remove(iGisrc) gscript.try_remove(oGisrc) self.ExpandNode(self.selected_mapset[0], recursive=True) def _removeMapAfterCopy(self, cLayer, cType, env): removed, cmd = self._runCommand('g.remove', type=cType.label, name=cLayer.label, flags='f', env=env) if removed == 0: self._model.RemoveNode(cLayer) self.RefreshNode(cType, recursive=True) Debug.msg(1, "LAYER " + cLayer.label + " DELETED") self.showNotification.emit(message=_("g.remove completed")) def InsertLayer(self, name, mapset_node, element_name): """Insert layer into model and refresh tree""" found_element = self._model.SearchNodes(parent=mapset_node, type='element', name=element_name) found_element = found_element[0] if found_element else None if not found_element: # add type node if not exists found_element = self._model.AppendNode(parent=mapset_node, label=element_name, data=dict( type='element', name=element_name)) found = self._model.SearchNodes(parent=found_element, name=name) if len(found) == 0: self._model.AppendNode(parent=found_element, label=name, data=dict(type=element_name, name=name)) self._model.SortChildren(found_element) self.RefreshNode(mapset_node, recursive=True) def OnDeleteMap(self, event): """Delete layer or mapset""" names = [ self.selected_layer[i].label + '@' + self.selected_mapset[i].label for i in range(len(self.selected_layer)) ] if len(names) < 10: question = _("Do you really want to delete map(s) <{m}>?").format( m=', '.join(names)) else: question = _("Do you really want to delete {n} maps?").format( n=len(names)) if self._confirmDialog(question, title=_('Delete map')) == wx.ID_YES: label = _("Deleting {name}...").format(name=names) self.showNotification.emit(message=label) for i in range(len(self.selected_layer)): gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location[i].label, self.selected_mapset[i].label) removed, cmd = self._runCommand( 'g.remove', flags='f', type=self.selected_type[i].label, name=self.selected_layer[i].label, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer[i]) self.RefreshNode(self.selected_type[i], recursive=True) Debug.msg( 1, "LAYER " + self.selected_layer[i].label + " DELETED") # remove map layer from layer tree if exists if not isinstance(self._giface, StandaloneGrassInterface): name = self.selected_layer[ i].label + '@' + self.selected_mapset[i].label layers = self._giface.GetLayerList().GetLayersByName( name) for layer in layers: self._giface.GetLayerList().DeleteLayer(layer) gscript.try_remove(gisrc) self.UnselectAll() self.showNotification.emit(message=_("g.remove completed")) def OnDisplayLayer(self, event): """Display layer in current graphics view""" self.DisplayLayer() def DisplayLayer(self): """Display selected layer in current graphics view""" all_names = [] names = {'raster': [], 'vector': [], 'raster3d': []} for i in range(len(self.selected_layer)): name = self.selected_layer[i].label + '@' + self.selected_mapset[ i].label names[self.selected_type[i].label].append(name) all_names.append(name) #if self.selected_location[0].label == gisenv()['LOCATION_NAME'] and self.selected_mapset[0]: for ltype in names: if names[ltype]: self._giface.lmgr.AddMaps(list(reversed(names[ltype])), ltype, True) if len(self._giface.GetLayerList()) == 1: # zoom to map if there is only one map layer self._giface.GetMapWindow().ZoomToMap() Debug.msg(1, "Displayed layer(s): " + str(all_names)) def OnBeginDrag(self, node, event): """Just copy necessary data""" self.DefineItems(self.GetSelected()) if self.selected_layer and not (self._restricted and gisenv()['LOCATION_NAME'] != self.selected_location[0].label): event.Allow() self.OnCopyMap(event) Debug.msg(1, "DRAG") else: event.Veto() def OnEndDrag(self, node, event): """Copy layer into target""" self.copy_mode = wx.GetMouseState().ControlDown() if node: self.DefineItems([node]) if self._restricted and gisenv( )['MAPSET'] != self.selected_mapset[0].label: GMessage(_( "To move or copy maps to other mapsets, unlock editing of other mapsets" ), parent=self) event.Veto() return event.Allow() Debug.msg(1, "DROP DONE") self.OnPasteMap(event) def OnSwitchLocationMapset(self, event): genv = gisenv() if self.selected_location[0].label == genv['LOCATION_NAME']: self.changeMapset.emit(mapset=self.selected_mapset[0].label) else: self.changeLocation.emit(mapset=self.selected_mapset[0].label, location=self.selected_location[0].label) self.ExpandCurrentMapset() def OnMetadata(self, event): """Show metadata of any raster/vector/3draster""" def done(event): gscript.try_remove(event.userData) for i in range(len(self.selected_layer)): if self.selected_type[i].label == 'raster': cmd = ['r.info'] elif self.selected_type[i].label == 'vector': cmd = ['v.info'] elif self.selected_type[i].label == 'raster_3d': cmd = ['r3.info'] cmd.append( 'map=%s@%s' % (self.selected_layer[i].label, self.selected_mapset[i].label)) gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location[i].label, self.selected_mapset[i].label) # print output to command log area # temp gisrc file must be deleted onDone self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc) def OnCopyName(self, event): """Copy layer name to clipboard""" if wx.TheClipboard.Open(): do = wx.TextDataObject() text = [] for i in range(len(self.selected_layer)): text.append('%s@%s' % (self.selected_layer[i].label, self.selected_mapset[i].label)) do.SetText(','.join(text)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() def Filter(self, text): """Filter tree based on name and type.""" text = text.strip() if len(text.split(':')) > 1: name = text.split(':')[1].strip() elem = text.split(':')[0].strip() if 'r' == elem: element = 'raster' elif 'r3' == elem: element = 'raster_3d' elif 'v' == elem: element = 'vector' else: element = None else: element = None name = text.strip() self._model = filterModel(self._orig_model, name=name, element=element) self.RefreshItems() self.ExpandCurrentMapset() def _getNewMapName(self, message, title, value, element, mapset, env): """Dialog for simple text entry""" dlg = NameEntryDialog(parent=self, message=message, caption=title, element=element, env=env, mapset=mapset) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _isCurrent(self, genv): if self._restricted: currentMapset = currentLocation = True for i in range(len(self.selected_location)): if self.selected_location[i].label != genv['LOCATION_NAME']: currentLocation = False currentMapset = False break if currentMapset: for i in range(len(self.selected_mapset)): if self.selected_mapset[i].label != genv['MAPSET']: currentMapset = False break return currentLocation, currentMapset else: return True, True def _popupMenuLayer(self): """Create popup menu for layers""" menu = Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Cut")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnMoveMap, item) if not currentMapset: item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyMap, item) item = wx.MenuItem(menu, wx.NewId(), _("Copy &name")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyName, item) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentMapset and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDeleteMap, item) item.Enable(currentMapset) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMap, item) item.Enable(currentMapset and len(self.selected_layer) == 1) menu.AppendSeparator() if not isinstance(self._giface, StandaloneGrassInterface): if all([ each.label == genv['LOCATION_NAME'] for each in self.selected_location ]): if len(self.selected_layer) > 1: item = wx.MenuItem(menu, wx.NewId(), _("&Display layers")) else: item = wx.MenuItem(menu, wx.NewId(), _("&Display layer")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) item = wx.MenuItem(menu, wx.NewId(), _("Show &metadata")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnMetadata, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentMapset and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Switch mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnSwitchLocationMapset, item) if (self.selected_location[0].label == genv['LOCATION_NAME'] and self.selected_mapset[0].label == genv['MAPSET']): item.Enable(False) self.PopupMenu(menu) menu.Destroy() def _popupMenuElement(self): """Create popup menu for elements""" menu = Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) if not (currentMapset and self.copy_layer): item.Enable(False) self.PopupMenu(menu) menu.Destroy() def _popupMenuEmpty(self): """Create empty popup when multiple different types of items are selected""" menu = Menu() item = wx.MenuItem(menu, wx.NewId(), _("No available options")) menu.AppendItem(item) item.Enable(False) self.PopupMenu(menu) menu.Destroy()
def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY, **kwargs): self.parent = parent self._handlerObj = handlerObj self._giface = giface self.showNotification = Signal('SearchModuleWindow.showNotification') wx.Panel.__init__(self, parent=parent, id=id, **kwargs) # tree self._tree = CTreeView(model=model, parent=self) self._tree.SetToolTip( _("Double-click or Ctrl-Enter to run selected module")) # self._dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY, # label = " %s " % _("Module tree")) # search widget self._search = SearchModuleWidget(parent=self, model=model, showChoice=False) self._search.showSearchResult.connect( lambda result: self._tree.Select(result)) self._search.showNotification.connect(self.showNotification) self._helpText = StaticText( parent=self, id=wx.ID_ANY, label="Press Enter for next match, Ctrl+Enter to run command") self._helpText.SetForegroundColour( wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) # buttons self._btnRun = Button(self, id=wx.ID_OK, label=_("&Run")) self._btnRun.SetToolTip(_("Run selected module from the tree")) self._btnHelp = Button(self, id=wx.ID_ANY, label=_("H&elp")) self._btnHelp.SetToolTip( _("Show manual for selected module from the tree")) self._btnAdvancedSearch = Button(self, id=wx.ID_ANY, label=_("Adva&nced search...")) self._btnAdvancedSearch.SetToolTip( _("Do advanced search using %s module") % 'g.search.module') # bindings self._btnRun.Bind(wx.EVT_BUTTON, lambda evt: self.Run()) self._btnHelp.Bind(wx.EVT_BUTTON, lambda evt: self.Help()) self._btnAdvancedSearch.Bind(wx.EVT_BUTTON, lambda evt: self.AdvancedSearch()) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) self._tree.selectionChanged.connect(self.OnItemSelected) self._tree.itemActivated.connect(lambda node: self.Run(node)) self._layout() self._search.SetFocus()
def __init__(self, parent, giface, settings, scaniface): wx.Panel.__init__(self, parent) self.group = None self.segment = 'segment' self.segment_clump = 'segment_clump' self.signature = 'signature' self.classification = 'classification' self.filtered_classification = 'fclassification' self.reject = 'reject' self.output = 'objects' self.env = None self.giface = giface self.parent = parent self.settings = settings self.scaniface = scaniface self.settingsChanged = Signal('ColorInteractionPanel.settingsChanged') if 'color' not in self.settings: self.settings['color'] = {} self.settings['color']['active'] = False self.settings['color']['name'] = '' self.settings['color']['training'] = '' self.hide = [] self.ifColor = wx.CheckBox(self, label=_("Save color rasters:")) self.ifColor.SetValue(self.settings['color']['active']) self.ifColor.Bind(wx.EVT_CHECKBOX, self.OnChange) self.exportColor = Select(self, size=(-1, -1), type='raster') self.exportColor.SetValue(self.settings['color']['name']) self.exportColor.Bind(wx.EVT_TEXT, self.OnChange) self.hide.append(self.exportColor) if self.settings['color']['name']: self.group = self.settings['color']['name'] self.trainingAreas = Select(self, size=(-1, -1), type='raster') self.trainingAreas.SetValue(self.settings['color']['training']) self.trainingAreas.Bind(wx.EVT_TEXT, self.OnChange) labelTraining = wx.StaticText(self, label=_("Training areas:")) self.hide.append(self.trainingAreas) self.hide.append(labelTraining) calibrateBtn = wx.Button(self, label=_("Calibrate")) calibrateBtn.Bind(wx.EVT_BUTTON, self.OnCalibration) self.hide.append(calibrateBtn) analyzeBtn = wx.Button(self, label=_("Scan and process")) analyzeBtn.Bind(wx.EVT_BUTTON, self.OnAnalysis) self.hide.append(analyzeBtn) self.mainSizer = wx.BoxSizer(wx.VERTICAL) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.ifColor, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.exportColor, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(labelTraining, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.trainingAreas, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(calibrateBtn, flag=wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.AddStretchSpacer() sizer.Add(analyzeBtn, flag=wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) self.SetSizer(self.mainSizer) self.mainSizer.Fit(self) self._enable()
def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None): """ :param guiparent: parent window for created GUI objects :param lmgr: layer manager window (TODO: replace by giface) :param ignoredCmdPattern: regular expression specifying commads to be ignored (e.g. @c '^d\..*' for display commands) """ wx.EvtHandler.__init__(self) # Signal when some map is created or updated by a module. # attributes: name: map name, ltype: map type, self.mapCreated = Signal("GConsole.mapCreated") # emitted when map display should be re-render self.updateMap = Signal("GConsole.updateMap") # emitted when log message should be written self.writeLog = Signal("GConsole.writeLog") # emitted when command log message should be written self.writeCmdLog = Signal("GConsole.writeCmdLog") # emitted when warning message should be written self.writeWarning = Signal("GConsole.writeWarning") # emitted when error message should be written self.writeError = Signal("GConsole.writeError") self._guiparent = guiparent self._giface = giface self._ignoredCmdPattern = ignoredCmdPattern # create queues self.requestQ = Queue.Queue() self.resultQ = Queue.Queue() self.cmdOutputTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents) self.Bind(EVT_CMD_RUN, self.OnCmdRun) self.Bind(EVT_CMD_DONE, self.OnCmdDone) self.Bind(EVT_CMD_ABORT, self.OnCmdAbort) # stream redirection self.cmdStdOut = GStdout(receiver=self) self.cmdStdErr = GStderr(receiver=self) # thread self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
class GConsole(wx.EvtHandler): """Backend for command execution, esp. interactive command execution""" def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None): """ :param guiparent: parent window for created GUI objects :param lmgr: layer manager window (TODO: replace by giface) :param ignoredCmdPattern: regular expression specifying commads to be ignored (e.g. @c '^d\..*' for display commands) """ wx.EvtHandler.__init__(self) # Signal when some map is created or updated by a module. # attributes: name: map name, ltype: map type, self.mapCreated = Signal("GConsole.mapCreated") # emitted when map display should be re-render self.updateMap = Signal("GConsole.updateMap") # emitted when log message should be written self.writeLog = Signal("GConsole.writeLog") # emitted when command log message should be written self.writeCmdLog = Signal("GConsole.writeCmdLog") # emitted when warning message should be written self.writeWarning = Signal("GConsole.writeWarning") # emitted when error message should be written self.writeError = Signal("GConsole.writeError") self._guiparent = guiparent self._giface = giface self._ignoredCmdPattern = ignoredCmdPattern # create queues self.requestQ = Queue.Queue() self.resultQ = Queue.Queue() self.cmdOutputTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents) self.Bind(EVT_CMD_RUN, self.OnCmdRun) self.Bind(EVT_CMD_DONE, self.OnCmdDone) self.Bind(EVT_CMD_ABORT, self.OnCmdAbort) # stream redirection self.cmdStdOut = GStdout(receiver=self) self.cmdStdErr = GStderr(receiver=self) # thread self.cmdThread = CmdThread(self, self.requestQ, self.resultQ) def Redirect(self): """Redirect stdout/stderr""" if Debug.GetLevel() == 0 and grass.debug_level(force=True) == 0: # don't redirect when debugging is enabled sys.stdout = self.cmdStdOut sys.stderr = self.cmdStdErr else: enc = locale.getdefaultlocale()[1] if enc: if sys.version_info.major == 2: sys.stdout = codecs.getwriter(enc)(sys.__stdout__) sys.stderr = codecs.getwriter(enc)(sys.__stderr__) else: # https://stackoverflow.com/questions/4374455/how-to-set-sys-stdout-encoding-in-python-3 sys.stdout = codecs.getwriter(enc)(sys.__stdout__.detach()) sys.stderr = codecs.getwriter(enc)(sys.__stderr__.detach()) else: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ def WriteLog( self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT ): """Generic method for writing log message in given style :param text: text line :param notification: form of notification """ self.writeLog.emit(text=text, wrap=wrap, notification=notification) def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE): """Write message in selected style :param text: message to be printed :param pid: process pid or None :param notification: form of notification """ self.writeCmdLog.emit(text=text, pid=pid, notification=notification) def WriteWarning(self, text): """Write message in warning style""" self.writeWarning.emit(text=text) def WriteError(self, text): """Write message in error style""" self.writeError.emit(text=text) def RunCmd( self, command, compReg=True, env=None, skipInterface=False, onDone=None, onPrepare=None, userData=None, addLayer=None, notification=Notification.MAKE_VISIBLE, ): """Run command typed into console command prompt (GPrompt). .. todo:: Document the other event. .. todo:: Solve problem with the other event (now uses gOutputText event but there is no text, use onPrepare handler instead?) .. todo:: Skip interface is ignored and determined always automatically. Posts event EVT_IGNORED_CMD_RUN when command which should be ignored (according to ignoredCmdPattern) is run. For example, see layer manager which handles d.* on its own. :param command: command given as a list (produced e.g. by utils.split()) :param compReg: True use computation region :param notification: form of notification :param bool skipInterface: True to do not launch GRASS interface parser when command has no arguments given :param onDone: function to be called when command is finished :param onPrepare: function to be called before command is launched :param addLayer: to be passed in the mapCreated signal :param userData: data defined for the command """ if len(command) == 0: Debug.msg(2, "GPrompt:RunCmd(): empty command") return # update history file self.UpdateHistoryFile(" ".join(command)) if command[0] in globalvar.grassCmd: # send GRASS command without arguments to GUI command interface # except ignored commands (event is emitted) if ( self._ignoredCmdPattern and re.compile(self._ignoredCmdPattern).search(" ".join(command)) and "--help" not in command and "--ui" not in command ): event = gIgnoredCmdRun(cmd=command) wx.PostEvent(self, event) return else: # other GRASS commands (r|v|g|...) try: task = GUI(show=None).ParseCommand(command) except GException as e: GError(parent=self._guiparent, message=str(e), showTraceback=False) return hasParams = False if task: options = task.get_options() hasParams = options["params"] and options["flags"] # check for <input>=- for p in options["params"]: if ( p.get("prompt", "") == "input" and p.get("element", "") == "file" and p.get("age", "new") == "old" and p.get("value", "") == "-" ): GError( parent=self._guiparent, message=_( "Unable to run command:\n%(cmd)s\n\n" "Option <%(opt)s>: read from standard input is not " "supported by wxGUI" ) % {"cmd": " ".join(command), "opt": p.get("name", "")}, ) return if len(command) == 1: if command[0].startswith("g.gui."): import imp import inspect pyFile = command[0] if sys.platform == "win32": pyFile += ".py" pyPath = os.path.join(os.environ["GISBASE"], "scripts", pyFile) if not os.path.exists(pyPath): pyPath = os.path.join( os.environ["GRASS_ADDON_BASE"], "scripts", pyFile ) if not os.path.exists(pyPath): GError( parent=self._guiparent, message=_("Module <%s> not found.") % command[0], ) pymodule = imp.load_source(command[0].replace(".", "_"), pyPath) try: # PY3 pymain = inspect.getfullargspec(pymodule.main) except AttributeError: pymain = inspect.getargspec(pymodule.main) if pymain and "giface" in pymain.args: pymodule.main(self._giface) return # no arguments given if hasParams and not isinstance(self._guiparent, FormNotebook): # also parent must be checked, see #3135 for details try: GUI( parent=self._guiparent, giface=self._giface ).ParseCommand(command) except GException as e: print(e, file=sys.stderr) return if env: env = env.copy() else: env = os.environ.copy() # activate computational region (set with g.region) # for all non-display commands. if compReg and "GRASS_REGION" in env: del env["GRASS_REGION"] # process GRASS command with argument self.cmdThread.RunCmd( command, stdout=self.cmdStdOut, stderr=self.cmdStdErr, onDone=onDone, onPrepare=onPrepare, userData=userData, addLayer=addLayer, env=env, notification=notification, ) self.cmdOutputTimer.Start(50) # we don't need to change computational region settings # because we work on a copy else: # Send any other command to the shell. Send output to # console output window # # Check if the script has an interface (avoid double-launching # of the script) # check if we ignore the command (similar to grass commands part) if self._ignoredCmdPattern and re.compile(self._ignoredCmdPattern).search( " ".join(command) ): event = gIgnoredCmdRun(cmd=command) wx.PostEvent(self, event) return skipInterface = True if os.path.splitext(command[0])[1] in (".py", ".sh"): try: sfile = open(command[0], "r") for line in sfile.readlines(): if len(line) < 2: continue if line[0] == "#" and line[1] == "%": skipInterface = False break sfile.close() except IOError: pass if len(command) == 1 and not skipInterface: try: task = gtask.parse_interface(command[0]) except: task = None else: task = None if task: # process GRASS command without argument GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command) else: self.cmdThread.RunCmd( command, stdout=self.cmdStdOut, stderr=self.cmdStdErr, onDone=onDone, onPrepare=onPrepare, userData=userData, addLayer=addLayer, env=env, notification=notification, ) self.cmdOutputTimer.Start(50) def GetLog(self, err=False): """Get widget used for logging .. todo:: what's this? :param bool err: True to get stderr widget """ if err: return self.cmdStdErr return self.cmdStdOut def GetCmd(self): """Get running command or None""" return self.requestQ.get() def OnCmdAbort(self, event): """Abort running command""" self.cmdThread.abort() event.Skip() def OnCmdRun(self, event): """Run command""" self.WriteCmdLog( "(%s)\n%s" % (str(time.ctime()), " ".join(event.cmd)), notification=event.notification, ) event.Skip() def OnCmdDone(self, event): """Command done (or aborted) Sends signal mapCreated if map is recognized in output parameters or for specific modules (as r.colors). """ # Process results here try: ctime = time.time() - event.time if ctime < 60: stime = _("%d sec") % int(ctime) else: mtime = int(ctime / 60) stime = _("%(min)d min %(sec)d sec") % { "min": mtime, "sec": int(ctime - (mtime * 60)), } except KeyError: # stopped deamon stime = _("unknown") if event.aborted: # Thread aborted (using our convention of None return) self.WriteWarning( _( "Please note that the data are left in" " inconsistent state and may be corrupted" ) ) msg = _("Command aborted") else: msg = _("Command finished") self.WriteCmdLog( "(%s) %s (%s)" % (str(time.ctime()), msg, stime), notification=event.notification, ) if event.onDone: event.onDone(event) self.cmdOutputTimer.Stop() if event.cmd[0] == "g.gisenv": Debug.SetLevel() self.Redirect() # do nothing when no map added if event.returncode != 0 or event.aborted: event.Skip() return if event.cmd[0] not in globalvar.grassCmd: return # find which maps were created try: task = GUI(show=None).ParseCommand(event.cmd) except GException as e: print(e, file=sys.stderr) task = None return name = task.get_name() for p in task.get_options()["params"]: prompt = p.get("prompt", "") if prompt in ("raster", "vector", "raster_3d") and p.get("value", None): if p.get("age", "old") == "new" or name in ( "r.colors", "r3.colors", "v.colors", "v.proj", "r.proj", ): # if multiple maps (e.g. r.series.interp), we need add each if p.get("multiple", False): lnames = p.get("value").split(",") # in case multiple input (old) maps in r.colors # we don't want to rerender it multiple times! just # once if p.get("age", "old") == "old": lnames = lnames[0:1] else: lnames = [p.get("value")] for lname in lnames: if "@" not in lname: lname += "@" + grass.gisenv()["MAPSET"] if grass.find_file(lname, element=p.get("element"))["fullname"]: self.mapCreated.emit( name=lname, ltype=prompt, add=event.addLayer ) gisenv = grass.gisenv() self._giface.grassdbChanged.emit( grassdb=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], mapset=gisenv["MAPSET"], action="new", map=lname.split("@")[0], element=prompt, ) if name == "r.mask": self.updateMap.emit() event.Skip() def OnProcessPendingOutputWindowEvents(self, event): wx.GetApp().ProcessPendingEvents() def UpdateHistoryFile(self, command): """Update history file :param command: the command given as a string """ env = grass.gisenv() try: filePath = os.path.join( env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], ".wxgui_history" ) fileHistory = codecs.open(filePath, encoding="utf-8", mode="a") except IOError as e: GError( _("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") % {"filePath": filePath, "error": e}, parent=self._guiparent, ) return try: fileHistory.write(command + os.linesep) finally: fileHistory.close() # update wxGUI prompt if self._giface: self._giface.UpdateCmdHistory(command)
class DMonMap(Map): def __init__(self, giface, cmdfile=None, mapfile=None): """Map composition (stack of map layers and overlays) :param cmdline: full path to the cmd file (defined by d.mon) :param mapfile: full path to the map file (defined by d.mon) """ Map.__init__(self) self._giface = giface # environment settings self.env = dict() self.cmdfile = cmdfile # list of layers for rendering added from cmd file # TODO temporary solution, layer management by different tools in GRASS # should be resolved self.ownedLayers = [] self.oldOverlays = [] if mapfile: self.mapfileCmd = mapfile self.maskfileCmd = os.path.splitext(mapfile)[0] + ".pgm" # generated file for g.pnmcomp output for rendering the map self.mapfile = monFile["map"] if os.path.splitext(self.mapfile)[1] != ".ppm": self.mapfile += ".ppm" # signal sent when d.out.file/d.to.rast appears in cmd file, attribute # is cmd self.saveToFile = Signal("DMonMap.saveToFile") self.dToRast = Signal("DMonMap.dToRast") # signal sent when d.what.rast/vect appears in cmd file, attribute is # cmd self.query = Signal("DMonMap.query") self.renderMgr = RenderMapMgr(self) # update legend file variable with the one d.mon uses with open(monFile["env"], "r") as f: lines = f.readlines() for line in lines: if "GRASS_LEGEND_FILE" in line: legfile = line.split("=", 1)[1].strip() self.renderMgr.UpdateRenderEnv( {"GRASS_LEGEND_FILE": legfile}) break def GetLayersFromCmdFile(self): """Get list of map layers from cmdfile""" if not self.cmdfile: return nlayers = 0 try: fd = open(self.cmdfile, "r") lines = fd.readlines() fd.close() # detect d.out.file, delete the line from the cmd file and export # graphics if len(lines) > 0: if lines[-1].startswith("d.out.file") or lines[-1].startswith( "d.to.rast"): dCmd = lines[-1].strip() fd = open(self.cmdfile, "w") fd.writelines(lines[:-1]) fd.close() if lines[-1].startswith("d.out.file"): self.saveToFile.emit(cmd=utils.split(dCmd)) else: self.dToRast.emit(cmd=utils.split(dCmd)) return if lines[-1].startswith("d.what"): dWhatCmd = lines[-1].strip() fd = open(self.cmdfile, "w") fd.writelines(lines[:-1]) fd.close() if "=" in utils.split(dWhatCmd)[1]: maps = utils.split(dWhatCmd)[1].split("=")[1].split( ",") else: maps = utils.split(dWhatCmd)[1].split(",") self.query.emit( ltype=utils.split(dWhatCmd)[0].split(".")[-1], maps=maps) return else: # clean overlays after erase self.oldOverlays = [] overlays = list( self._giface.GetMapDisplay().decorations.keys()) for each in overlays: self._giface.GetMapDisplay().RemoveOverlay(each) existingLayers = self.GetListOfLayers() # holds new rendreing order for every layer in existingLayers layersOrder = [-1] * len(existingLayers) # next number in rendering order next_layer = 0 mapFile = None render_env = dict() for line in lines: if line.startswith("#"): if "GRASS_RENDER_FILE" in line: mapFile = line.split("=", 1)[1].strip() try: k, v = line[2:].strip().split("=", 1) except: pass render_env[k] = v continue cmd = utils.split(line.strip()) ltype = None try: ltype = utils.command2ltype[cmd[0]] except KeyError: grass.warning(_("Unsupported command %s.") % cmd[0]) continue name = utils.GetLayerNameFromCmd(cmd, fullyQualified=True, layerType=ltype)[0] args = {} if ltype in ("barscale", "rastleg", "northarrow", "text", "vectleg"): # TODO: this is still not optimal # it is there to prevent adding the same overlay multiple times if cmd in self.oldOverlays: continue if ltype == "rastleg": self._giface.GetMapDisplay().AddLegendRast(cmd=cmd) elif ltype == "barscale": self._giface.GetMapDisplay().AddBarscale(cmd=cmd) elif ltype == "northarrow": self._giface.GetMapDisplay().AddArrow(cmd=cmd) elif ltype == "text": self._giface.GetMapDisplay().AddDtext(cmd=cmd) elif ltype == "vectleg": self._giface.GetMapDisplay().AddLegendVect(cmd=cmd) self.oldOverlays.append(cmd) continue classLayer = MapLayer args["ltype"] = ltype exists = False for i, layer in enumerate(existingLayers): if layer.GetCmd(string=True) == utils.GetCmdString( cmdlist_to_tuple(cmd)): exists = True if layersOrder[i] == -1: layersOrder[i] = next_layer next_layer += 1 # layer must be put higher in render order (same cmd was insered more times) # TODO delete rendurant cmds from cmd file? else: for j, l_order in enumerate(layersOrder): if l_order > layersOrder[i]: layersOrder[j] -= 1 layersOrder[i] = next_layer - 1 break if exists: continue mapLayer = classLayer( name=name, cmd=cmd, Map=None, hidden=True, render=False, mapfile=mapFile, **args, ) mapLayer.GetRenderMgr().updateProgress.connect( self.GetRenderMgr().ReportProgress) if render_env: mapLayer.GetRenderMgr().UpdateRenderEnv(render_env) render_env = dict() newLayer = self._addLayer(mapLayer) existingLayers.append(newLayer) self.ownedLayers.append(newLayer) layersOrder.append(next_layer) next_layer += 1 nlayers += 1 reorderedLayers = [-1] * next_layer for i, layer in enumerate(existingLayers): # owned layer was not found in cmd file -> is deleted if layersOrder[i] == -1 and layer in self.ownedLayers: self.ownedLayers.remove(layer) self.DeleteLayer(layer) # other layer e. g. added by wx.vnet are added to the top elif layersOrder[i] == -1 and layer not in self.ownedLayers: reorderedLayers.append(layer) # owned layer found in cmd file is added into proper rendering # position else: reorderedLayers[layersOrder[i]] = layer self.SetLayers(reorderedLayers) except IOError as e: grass.warning( _("Unable to read cmdfile '%(cmd)s'. Details: %(det)s") % { "cmd": self.cmdfile, "det": e }) return Debug.msg( 1, "Map.GetLayersFromCmdFile(): cmdfile=%s, nlayers=%d" % (self.cmdfile, nlayers), ) self._giface.updateMap.emit(render=False) def Render(self, *args, **kwargs): """Render layer to image. For input params and returned data see overridden method in Map class. """ return Map.Render(self, *args, **kwargs) def AddLayer(self, *args, **kwargs): """Adds generic map layer to list of layers. For input params and returned data see overridden method in Map class. """ driver = UserSettings.Get(group="display", key="driver", subkey="type") if driver == "png": os.environ["GRASS_RENDER_IMMEDIATE"] = "png" else: os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo" layer = Map.AddLayer(self, *args, **kwargs) del os.environ["GRASS_RENDER_IMMEDIATE"] return layer
def __init__( self, parent, layerList, lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT, toolbarCls=None, modal=False): wx.Panel.__init__(self, parent=parent, name='SimpleLayerManager') self._style = lmgrStyle self._layerList = layerList self._checkList = CheckListBox(self, style=wx.LB_EXTENDED) if not toolbarCls: toolbarCls = SimpleLmgrToolbar self._toolbar = toolbarCls(self, lmgrStyle=self._style) self._auimgr = wx.aui.AuiManager(self) self._modal = modal # d.* dialogs are recreated each time, attempt to hide it resulted # in completely mysterious memory corruption and crash when opening # any dialog with stock labels (wx.ID_OK and so on) # needed in order not to change selection when moving layers self._blockSelectionChanged = False self._checkList.Bind( wx.EVT_LISTBOX, lambda evt: self._selectionChanged()) self._checkList.Bind( wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties) self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked) self._checkList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) # signal emitted when somethin in layer list changes self.opacityChanged = Signal('SimpleLayerManager.opacityChanged') self.cmdChanged = Signal('SimpleLayerManager.cmdChanged') self.layerAdded = Signal('SimpleLayerManager.layerAdded') self.layerRemoved = Signal('SimpleLayerManager.layerRemoved') self.layerActivated = Signal('SimpleLayerManager.layerActivated') self.layerMovedUp = Signal('SimpleLayerManager.layerMovedUp') self.layerMovedDown = Signal('SimpleLayerManager.layerMovedDown') # emitted by any change (e.g. for rerendering) self.anyChange = Signal('SimpleLayerManager.layerChange') self._layout() self.SetMinSize((200, -1)) self._update()
class SimpleLayerManager(wx.Panel): """Simple layer manager class provides similar functionality to Layertree, but it's just list, not tree.""" def __init__( self, parent, layerList, lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT, toolbarCls=None, modal=False): wx.Panel.__init__(self, parent=parent, name='SimpleLayerManager') self._style = lmgrStyle self._layerList = layerList self._checkList = CheckListBox(self, style=wx.LB_EXTENDED) if not toolbarCls: toolbarCls = SimpleLmgrToolbar self._toolbar = toolbarCls(self, lmgrStyle=self._style) self._auimgr = wx.aui.AuiManager(self) self._modal = modal # d.* dialogs are recreated each time, attempt to hide it resulted # in completely mysterious memory corruption and crash when opening # any dialog with stock labels (wx.ID_OK and so on) # needed in order not to change selection when moving layers self._blockSelectionChanged = False self._checkList.Bind( wx.EVT_LISTBOX, lambda evt: self._selectionChanged()) self._checkList.Bind( wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties) self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked) self._checkList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) # signal emitted when somethin in layer list changes self.opacityChanged = Signal('SimpleLayerManager.opacityChanged') self.cmdChanged = Signal('SimpleLayerManager.cmdChanged') self.layerAdded = Signal('SimpleLayerManager.layerAdded') self.layerRemoved = Signal('SimpleLayerManager.layerRemoved') self.layerActivated = Signal('SimpleLayerManager.layerActivated') self.layerMovedUp = Signal('SimpleLayerManager.layerMovedUp') self.layerMovedDown = Signal('SimpleLayerManager.layerMovedDown') # emitted by any change (e.g. for rerendering) self.anyChange = Signal('SimpleLayerManager.layerChange') self._layout() self.SetMinSize((200, -1)) self._update() def _layout(self): self._auimgr.AddPane(self._checkList, wx.aui.AuiPaneInfo(). Name("checklist"). CenterPane(). CloseButton(False). BestSize((self._checkList.GetBestSize()))) paneInfo = wx.aui.AuiPaneInfo(). \ Name("toolbar").Caption(_("Toolbar")).ToolbarPane(). \ CloseButton(False).Layer(1).Gripper(False). \ BestSize((self._toolbar.GetBestSize())) if self._style & SIMPLE_LMGR_TB_LEFT: paneInfo.Left() elif self._style & SIMPLE_LMGR_TB_RIGHT: paneInfo.Right() elif self._style & SIMPLE_LMGR_TB_TOP: paneInfo.Top() else: paneInfo.Bottom() self._auimgr.AddPane(self._toolbar, paneInfo) self._auimgr.Update() def _selectionChanged(self): """Selection was changed externally, updates selection info in layers.""" if self._blockSelectionChanged: return selected = self._checkList.GetSelections() for i, layer in enumerate(self._layerList): layer.Select(i in selected) def OnContextMenu(self, event): """Show context menu. So far offers only copying layer list to clipboard """ if len(self._layerList) < 1: event.Skip() return menu = Menu() llist = [layer.name for layer in self._layerList] texts = [','.join(llist), ','.join(reversed(llist))] labels = [_("Copy map names to clipboard (top to bottom)"), _("Copy map names to clipboard (bottom to top)")] for label, text in zip(labels, texts): id = wx.NewId() self.Bind( wx.EVT_MENU, lambda evt, t=text, id=id: self._copyText(t), id=id) menu.Append(id, label) # show the popup menu self.PopupMenu(menu) menu.Destroy() event.Skip() def _copyText(self, text): """Helper function for copying TODO: move to utils? """ if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText(text) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() def OnLayerChecked(self, event): """Layer was (un)checked, update layer's info.""" checkedIdxs = self._checkList.GetChecked() for i, layer in enumerate(self._layerList): if i in checkedIdxs and not layer.IsActive(): layer.Activate() self.layerActivated.emit(index=i, layer=layer) elif i not in checkedIdxs and layer.IsActive(): layer.Activate(False) self.layerActivated.emit(index=i, layer=layer) self.anyChange.emit() event.Skip() def OnAddRaster(self, event): """Opens d.rast dialog and adds layer. Dummy layer is added first.""" cmd = ['d.rast'] layer = self.AddRaster(name='', cmd=cmd, hidden=True, dialog=None) GUI(parent=self, giface=None, modal=self._modal).ParseCommand( cmd=cmd, completed=(self.GetOptData, layer, '')) event.Skip() def OnAddVector(self, event): """Opens d.vect dialog and adds layer. Dummy layer is added first.""" cmd = ['d.vect'] layer = self.AddVector(name='', cmd=cmd, hidden=True, dialog=None) GUI(parent=self, giface=None, modal=self._modal).ParseCommand( cmd=cmd, completed=(self.GetOptData, layer, '')) event.Skip() def OnAddRast3d(self, event): """Opens d.rast3d dialog and adds layer. Dummy layer is added first.""" cmd = ['d.rast3d'] layer = self.AddRast3d(name='', cmd=cmd, hidden=True, dialog=None) GUI(parent=self, giface=None, modal=self._modal).ParseCommand( cmd=cmd, completed=(self.GetOptData, layer, '')) event.Skip() def OnAddRGB(self, event): """Opens d.rgb dialog and adds layer. Dummy layer is added first.""" cmd = ['d.rgb'] layer = self.AddRGB(name='', cmd=cmd, hidden=True, dialog=None) GUI(parent=self, giface=None, modal=self._modal).ParseCommand( cmd=cmd, completed=(self.GetOptData, layer, '')) event.Skip() def OnRemove(self, event): """Removes selected layers from list.""" layers = self._layerList.GetSelectedLayers(activeOnly=False) for layer in layers: self.layerRemoved.emit( index=self._layerList.GetLayerIndex(layer), layer=layer) self._layerList.RemoveLayer(layer) self._update() self.anyChange.emit() event.Skip() def OnLayerUp(self, event): """Moves selected layers one step up. Note: not completely correct for multiple layers.""" layers = self._layerList.GetSelectedLayers() self._blockSelectionChanged = True for layer in layers: idx = self._layerList.GetLayerIndex(layer) if idx > 0: self.layerMovedUp.emit(index=idx, layer=layer) self._layerList.MoveLayerUp(layer) self._update() self._blockSelectionChanged = False self.anyChange.emit() event.Skip() def OnLayerDown(self, event): """Moves selected layers one step down. Note: not completely correct for multiple layers.""" layers = self._layerList.GetSelectedLayers() self._blockSelectionChanged = True for layer in layers: idx = self._layerList.GetLayerIndex(layer) if idx < len(self._layerList) - 1: self.layerMovedDown.emit( index=self._layerList.GetLayerIndex(layer), layer=layer) self._layerList.MoveLayerDown(layer) self._update() self._blockSelectionChanged = False self.anyChange.emit() event.Skip() def OnLayerChangeProperties(self, event): """Opens module dialog to edit layer command.""" layers = self._layerList.GetSelectedLayers() if not layers or len(layers) > 1: return self._layerChangeProperties(layers[0]) event.Skip() def _layerChangeProperties(self, layer): """Opens new module dialog or recycles it.""" GUI(parent=self, giface=None, modal=self._modal).ParseCommand( cmd=layer.cmd, completed=(self.GetOptData, layer, '')) def OnLayerChangeOpacity(self, event): """Opacity of a layer is changing.""" layers = self._layerList.GetSelectedLayers() if not layers or len(layers) > 1: return layer = layers[0] dlg = SetOpacityDialog(self, opacity=layer.opacity, title=_("Set opacity of <%s>") % layer.name) dlg.applyOpacity.connect(lambda value: self._setLayerOpacity(layer, value)) dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: self._setLayerOpacity(layer, dlg.GetOpacity()) dlg.Destroy() event.Skip() def _setLayerOpacity(self, layer, value): """Sets layer's opacity.'""" layer.opacity = value self._update() self.opacityChanged.emit( index=self._layerList.GetLayerIndex(layer), layer=layer) self.anyChange.emit() def _update(self): """Updates checklistbox according to layerList structure.""" items = [] active = [] selected = [] # remove hidden (temporary) layers first for layer in reversed(self._layerList): if layer.hidden: self._layerList.RemoveLayer(layer) for layer in self._layerList: if layer.opacity < 1: items.append( "{name} (opacity {opacity}%)".format( name=layer.name, opacity=int( layer.opacity * 100))) else: items.append(layer.name) active.append(layer.IsActive()) selected.append(layer.IsSelected()) self._checkList.SetItems(items) for i, check in enumerate(active): self._checkList.Check(i, check) for i, layer in enumerate(self._layerList): if selected[i]: self._checkList.Select(i) else: self._checkList.Deselect(i) def GetOptData(self, dcmd, layer, params, propwin): """Handler for module dialogs.""" if dcmd: layer.cmd = dcmd layer.selected = True mapName, found = GetLayerNameFromCmd(dcmd) if found: try: if layer.hidden: layer.hidden = False signal = self.layerAdded else: signal = self.cmdChanged layer.name = mapName signal.emit( index=self._layerList.GetLayerIndex(layer), layer=layer) except ValueError as e: self._layerList.RemoveLayer(layer) GError(parent=self, message=str(e), showTraceback=False) self._update() self.anyChange.emit() def AddRaster(self, name, cmd, hidden, dialog): """Ads new raster layer.""" layer = self._layerList.AddNewLayer(name=name, mapType='raster', active=True, cmd=cmd, hidden=hidden) return layer def AddRast3d(self, name, cmd, hidden, dialog): """Ads new raster3d layer.""" layer = self._layerList.AddNewLayer(name=name, mapType='raster_3d', active=True, cmd=cmd, hidden=hidden) return layer def AddVector(self, name, cmd, hidden, dialog): """Ads new vector layer.""" layer = self._layerList.AddNewLayer(name=name, mapType='vector', active=True, cmd=cmd, hidden=hidden) return layer def AddRGB(self, name, cmd, hidden, dialog): """Ads new vector layer.""" layer = self._layerList.AddNewLayer(name=name, mapType='rgb', active=True, cmd=cmd, hidden=hidden) return layer def GetLayerInfo(self, layer, key): """Just for compatibility, should be removed in the future""" value = getattr(layer, key) # hack to return empty list, required in OnCancel in forms # not sure why it should be empty if key == 'cmd' and len(value) == 1: return [] return value def Delete(self, layer): """Just for compatibility, should be removed in the future""" self._layerList.RemoveLayer(layer)
class GPrompt(object): """Abstract class for interactive wxGUI prompt Signal promptRunCmd - emitted to run command from prompt - attribute 'cmd' See subclass GPromptPopUp and GPromptSTC. """ def __init__(self, parent, giface, menuModel): self.parent = parent # GConsole self.panel = self.parent.GetPanel() self.promptRunCmd = Signal("GPrompt.promptRunCmd") # probably only subclasses need this self._menuModel = menuModel self.mapList = self._getListOfMaps() self.mapsetList = utils.ListOfMapsets() # auto complete items self.autoCompList = list() self.autoCompFilter = None # command description (gtask.grassTask) self.cmdDesc = None self._loadHistory() if giface: giface.currentMapsetChanged.connect(self._loadHistory) # list of traced commands self.commands = list() # reload map lists when needed if giface: giface.currentMapsetChanged.connect(self._reloadListOfMaps) giface.grassdbChanged.connect(self._reloadListOfMaps) def _readHistory(self): """Get list of commands from history file""" hist = list() env = grass.gisenv() try: fileHistory = codecs.open( os.path.join( env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], ".wxgui_history", ), encoding="utf-8", mode="r", errors="replace", ) except IOError: return hist try: for line in fileHistory.readlines(): hist.append(line.replace("\n", "")) finally: fileHistory.close() return hist def _loadHistory(self): """Load history from a history file to data structures""" self.cmdbuffer = self._readHistory() self.cmdindex = len(self.cmdbuffer) def _getListOfMaps(self): """Get list of maps""" result = dict() result["raster"] = grass.list_strings("raster") result["vector"] = grass.list_strings("vector") return result def _reloadListOfMaps(self): self.mapList = self._getListOfMaps() def _runCmd(self, cmdString): """Run command :param str cmdString: command to run """ if not cmdString: return # parse command into list try: cmd = utils.split(str(cmdString)) except UnicodeError: cmd = utils.split(EncodeString((cmdString))) cmd = list(map(DecodeString, cmd)) self.promptRunCmd.emit(cmd=cmd) self.OnCmdErase(None) self.ShowStatusText("") def GetCommands(self): """Get list of launched commands""" return self.commands def ClearCommands(self): """Clear list of commands""" del self.commands[:]
def __init__(self, parent, giface, menuModel, margin=False): GPrompt.__init__(self, parent=parent, giface=giface, menuModel=menuModel) wx.stc.StyledTextCtrl.__init__(self, self.panel, id=wx.ID_ANY) # # styles # self.SetWrapMode(True) self.SetUndoCollection(True) # # create command and map lists for autocompletion # self.AutoCompSetIgnoreCase(False) # # line margins # # TODO print number only from cmdlog self.SetMarginWidth(1, 0) self.SetMarginWidth(2, 0) if margin: self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER) self.SetMarginWidth(0, 30) else: self.SetMarginWidth(0, 0) # # miscellaneous # self.SetViewWhiteSpace(False) self.SetUseTabs(False) self.UsePopUp(True) self.SetUseHorizontalScrollBar(True) # support light and dark mode bg_color = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW) fg_color = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOWTEXT) selection_color = wx.SystemSettings().GetColour( wx.SYS_COLOUR_HIGHLIGHT) self.StyleSetBackground(wx.stc.STC_STYLE_DEFAULT, bg_color) self.StyleSetForeground(wx.stc.STC_STYLE_DEFAULT, fg_color) self.SetCaretForeground(fg_color) self.SetSelBackground(True, selection_color) self.StyleClearAll() # # bindings # self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) self.Bind(wx.EVT_CHAR, self.OnChar) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged) if sys.platform != "darwin": # unstable on Mac with wxPython 3 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) # signal which requests showing of a notification self.showNotification = Signal("GPromptSTC.showNotification") # signal to notify selected command self.commandSelected = Signal("GPromptSTC.commandSelected")
class ScanningPanel(wx.Panel): def __init__(self, parent, giface, settings, scaniface): wx.Panel.__init__(self, parent) self.giface = giface self.settings = settings self.scaniface = scaniface if 'scan' not in self.settings: self.settings['scan'] = {} self.settings['scan']['elevation'] = '' self.settings['scan']['region'] = '' self.settings['scan']['zexag'] = 1 self.settings['scan']['smooth'] = 8 self.settings['scan']['numscans'] = 1 self.settings['scan']['rotation_angle'] = 180 self.settings['scan']['resolution'] = 2 self.settings['scan']['trim_nsewtb'] = '30,30,30,30,50,150' self.settings['scan']['interpolate'] = False self.settings['scan']['trim_tolerance'] = '' self.settings['scan']['resolution'] = 2 self.scan = self.settings['scan'] self.settingsChanged = Signal('ScanningPanel.settingsChanged') mainSizer = wx.BoxSizer(wx.VERTICAL) # define static boxes before all widgets are defined georefBox = wx.StaticBox(self, label=' Georeferencing ') georefSizer = wx.StaticBoxSizer(georefBox, wx.VERTICAL) geomBox = wx.StaticBox(self, label=' Scan geometry ') geomSizer = wx.StaticBoxSizer(geomBox, wx.VERTICAL) demBox = wx.StaticBox(self, label=' DEM quality ') demSizer = wx.StaticBoxSizer(demBox, wx.VERTICAL) # create widgets self.btnCalibrateTilt = wx.Button(self, label="Calibration 1") self.btnCalibrateTilt.SetToolTipString('Calibrate to remove tilt of the scanner and to set suitable distance from the scanner') self.btnCalibrateExtent = wx.Button(self, label="Calibration 2") self.btnCalibrateExtent.SetToolTipString('Calibrate to identify the extent and position of the scanned object') # widgets for model self.elevInput = Select(self, size=(-1, -1), type='raster') self.elevInput.SetToolTipString('Raster from which we take the georeferencing information') self.regionInput = Select(self, size=(-1, -1), type='region') self.regionInput.SetToolTipString('Saved region from which we take the georeferencing information') self.zexag = wx.TextCtrl(self) self.zexag.SetToolTipString('Set vertical exaggeration of the physical model') self.numscans = wx.SpinCtrl(self, min=1, max=5, initial=1) self.numscans.SetToolTipString('Set number of scans to integrate') self.rotate = wx.SpinCtrl(self, min=0, max=360, initial=180) self.rotate.SetToolTipString('Set angle of rotation of the sensor around Z axis (typically 180 degrees)') self.smooth = wx.TextCtrl(self) self.smooth.SetToolTipString('Set smoothing of the DEM (typically between 7 to 12, higher value means more smoothing)') self.resolution = wx.TextCtrl(self) self.resolution.SetToolTipString('Raster resolution in mm of the ungeoreferenced scan') self.trim = {} for each in 'tbnsew': self.trim[each] = wx.TextCtrl(self, size=(40, -1)) if each in 'tb': self.trim[each].SetToolTipString('Distance from the scanner') else: self.trim[each].SetToolTipString('Distance from the center of scanning to the scanning boundary') self.trim_tolerance = wx.TextCtrl(self) self.trim_tolerance.SetToolTipString('Automatic trimming of the edges for rectangular models') self.interpolate = wx.CheckBox(self, label="Use interpolation instead of binning") self.interpolate.SetToolTipString('Interpolation avoids gaps in the scan, but takes longer') self.elevInput.SetValue(self.scan['elevation']) self.regionInput.SetValue(self.scan['region']) self.zexag.SetValue(str(self.scan['zexag'])) self.rotate.SetValue(self.scan['rotation_angle']) self.numscans.SetValue(self.scan['numscans']) self.interpolate.SetValue(self.scan['interpolate']) for i, each in enumerate('nsewtb'): self.trim[each].SetValue(self.scan['trim_nsewtb'].split(',')[i]) self.smooth.SetValue(str(self.scan['smooth'])) self.resolution.SetValue(str(self.scan['resolution'])) self.trim_tolerance.SetValue(str(self.scan['trim_tolerance'])) # layout # # Geometry box # # rotation hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Rotation angle:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.rotate, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) geomSizer.Add(hSizer, flag=wx.EXPAND) # trimming hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Trim vertically [cm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) for each in 'tb': hSizer.Add(wx.StaticText(self, label=each.upper() + ':'), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=2) hSizer.Add(self.trim[each], flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=2) hSizer.Add(self.btnCalibrateTilt, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=2) geomSizer.Add(hSizer, flag=wx.EXPAND) hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Trim horizontally [cm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) for each in 'nsew': hSizer.Add(wx.StaticText(self, label=each.upper() + ':'), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3) hSizer.Add(self.trim[each], flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=2) hSizer.Add(self.btnCalibrateExtent, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=2) geomSizer.Add(hSizer, flag=wx.EXPAND) # automatic trim hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Trim tolerance [0-1]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.trim_tolerance, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) geomSizer.Add(hSizer, flag=wx.EXPAND) mainSizer.Add(geomSizer, flag=wx.EXPAND|wx.ALL, border=10) hSizer2 = wx.BoxSizer(wx.HORIZONTAL) # # Georeferencing box # # model parameters hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Reference DEM:"), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.elevInput, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) georefSizer.Add(hSizer, flag=wx.EXPAND) # region hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Reference region:"), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.regionInput, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) georefSizer.Add(hSizer, flag=wx.EXPAND) hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Z-exaggeration:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.zexag, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) georefSizer.Add(hSizer, flag=wx.EXPAND) hSizer2.Add(georefSizer, proportion=1, flag=wx.EXPAND|wx.RIGHT, border=10) # # DEM properties box # # number of scans hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Number of scans:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.numscans, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) demSizer.Add(hSizer, flag=wx.EXPAND) # smooth hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Smooth value:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.smooth, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) demSizer.Add(hSizer, flag=wx.EXPAND) # resolution hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(wx.StaticText(self, label="Resolution [mm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) hSizer.Add(self.resolution, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) demSizer.Add(hSizer, flag=wx.EXPAND) hSizer = wx.BoxSizer(wx.HORIZONTAL) hSizer.Add(self.interpolate, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) demSizer.Add(hSizer, flag=wx.EXPAND) hSizer2.Add(demSizer, proportion=1, flag=wx.EXPAND) mainSizer.Add(hSizer2, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=10) self.SetSizer(mainSizer) mainSizer.Fit(self) self.BindModelProperties() def BindModelProperties(self): self.btnCalibrateTilt.Bind(wx.EVT_BUTTON, self.scaniface.Calibrate) self.btnCalibrateExtent.Bind(wx.EVT_BUTTON, self.scaniface.CalibrateModelBBox) # model parameters self.elevInput.Bind(wx.EVT_TEXT, self.OnModelProperties) self.regionInput.Bind(wx.EVT_TEXT, self.OnModelProperties) self.zexag.Bind(wx.EVT_TEXT, self.OnModelProperties) self.rotate.Bind(wx.EVT_SPINCTRL, self.OnModelProperties) self.rotate.Bind(wx.EVT_TEXT, self.OnModelProperties) self.numscans.Bind(wx.EVT_SPINCTRL, self.OnModelProperties) self.numscans.Bind(wx.EVT_TEXT, self.OnModelProperties) self.interpolate.Bind(wx.EVT_CHECKBOX, self.OnModelProperties) self.smooth.Bind(wx.EVT_TEXT, self.OnModelProperties) self.resolution.Bind(wx.EVT_TEXT, self.OnModelProperties) self.trim_tolerance.Bind(wx.EVT_TEXT, self.OnModelProperties) for each in 'nsewtb': self.trim[each].Bind(wx.EVT_TEXT, self.OnModelProperties) def OnModelProperties(self, event): self.scan['elevation'] = self.elevInput.GetValue() self.scan['region'] = self.regionInput.GetValue() self.scan['rotation_angle'] = self.rotate.GetValue() self.scan['numscans'] = self.numscans.GetValue() self.scan['interpolate'] = self.interpolate.IsChecked() self.scan['smooth'] = self.smooth.GetValue() self.scan['resolution'] = self.resolution.GetValue() trim_tol = self.trim_tolerance.GetValue() self.scan['trim_tolerance'] = float(trim_tol) if trim_tol else trim_tol try: self.scan['zexag'] = float(self.zexag.GetValue()) nsewtb_list = [] for each in 'nsewtb': nsewtb_list.append(self.trim[each].GetValue()) self.scan['trim_nsewtb'] = ','.join(nsewtb_list) except ValueError: pass self.settingsChanged.emit()
class SQLBuilderUpdate(SQLBuilder): """Class for building UPDATE SQL statement""" def __init__(self, parent, vectmap, id=wx.ID_ANY, layer=1, column=None): self.column = column # set dialog title title = _("GRASS SQL Builder (%(type)s) - <%(map)s>") % \ {'type': "UPDATE", 'map': vectmap} modeChoices = [_("Column to set (SET clause)"), _("Constraint for query (WHERE clause)"), _("Calculate column value to set")] SQLBuilder.__init__(self, parent, title, vectmap, id=wx.ID_ANY, modeChoices=modeChoices, layer=layer) # signals self.sqlApplied = Signal("SQLBuilder.sqlApplied") if parent: # TODO: replace by giface self.sqlApplied.connect(parent.Update) def _doLayout(self, modeChoices): """Do dialog layout""" SQLBuilder._doLayout(self, modeChoices) self.initText = "UPDATE %s SET" % self.tablename if self.column: self.initText += " %s = " % self.column self.text_sql.SetValue(self.initText) self.btn_arithmetic = {'eq': ['=', ], 'brac': ['()', ], 'plus': ['+', ], 'minus': ['-', ], 'divide': ['/', ], 'multiply': ['*', ]} self.btn_arithmeticpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) for key, value in six.iteritems(self.btn_arithmetic): btn = Button(parent=self.btn_arithmeticpanel, id=wx.ID_ANY, label=value[0]) self.btn_arithmetic[key].append(btn.GetId()) btn_arithmeticsizer = wx.GridBagSizer(hgap=5, vgap=5) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['eq'][1]), pos=( 0, 0)) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['brac'][1]), pos=( 1, 0)) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['plus'][1]), pos=( 0, 1)) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['minus'][1]), pos=( 1, 1)) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['divide'][1]), pos=( 0, 2)) btn_arithmeticsizer.Add( self.FindWindowById( self.btn_arithmetic['multiply'][1]), pos=( 1, 2)) self.btn_arithmeticpanel.SetSizer(btn_arithmeticsizer) self.pagesizer.Insert(3, self.btn_arithmeticpanel, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL) self.funcpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) self._initSqlFunctions() funcsbox = StaticBox(parent=self.funcpanel, id=wx.ID_ANY, label=" %s " % _("Functions")) funcsizer = wx.StaticBoxSizer(funcsbox, wx.VERTICAL) self.list_func = wx.ListBox(parent=self.funcpanel, id=wx.ID_ANY, choices=list(self.sqlFuncs['sqlite'].keys()), style=wx.LB_SORT) funcsizer.Add(self.list_func, proportion=1, flag=wx.EXPAND) self.funcpanel.SetSizer(funcsizer) self.hsizer.Insert(2, self.funcpanel, proportion=1, flag=wx.EXPAND) self.list_func.Bind(wx.EVT_LISTBOX, self.OnAddFunc) for key, value in six.iteritems(self.btn_arithmetic): self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark) self.mode.SetSelection(0) self.OnMode(None) self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition()) def OnApply(self, event): """Apply button pressed""" ret, msg = RunCommand('db.execute', getErrorMsg=True, parent=self, stdin=self.text_sql.GetValue(), input='-', driver=self.driver, database=self.database) if ret != 0 and msg: self.statusbar.SetStatusText(_("SQL statement was not applied"), 0) else: self.statusbar.SetStatusText(_("SQL statement applied"), 0) self.sqlApplied.emit() def OnClear(self, event): """Clear button pressed""" self.text_sql.SetValue(self.initText) def OnMode(self, event): """Adjusts builder for chosen mode""" if self.mode.GetSelection() == 0: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 1: self.valuespanel.Show() self.btn_logicpanel.Show() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 2: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Show() self.funcpanel.Show() self.pagesizer.Layout() def OnAddFunc(self, event): """Add function to the query""" if self.driver == 'dbf': GMessage( parent=self, message=_( "Dbf driver does not support usage of SQL functions.")) return idx = self.list_func.GetSelections() for i in idx: func = self.sqlFuncs['sqlite'][self.list_func.GetString(i)][0] self._add(element='func', value=func) 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 in ['value', 'mark', 'func'] or \ (element == 'column' and self.mode.GetSelection() == 2): addstr = ' ' + value + ' ' newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:] curspos += len(addstr) elif element == 'column': if self.mode.GetSelection() == 0: # -> column idx1 = sqlstr.lower().find('set') + len('set') idx2 = sqlstr.lower().find('where') if idx2 >= 0: colstr = sqlstr[idx1:idx2].strip() else: colstr = sqlstr[idx1:].strip() cols = [col.split('=')[0].strip() for col in colstr.split(',')] if value in cols: self.text_sql.SetInsertionPoint(curspos) wx.CallAfter(self.text_sql.SetFocus) return if colstr: colstr += ',' colstr = ' ' + colstr colstr += ' ' + value + '= ' newsqlstr = sqlstr[:idx1] + colstr if idx2 >= 0: newsqlstr += sqlstr[idx2:] curspos = idx1 + len(colstr) elif self.mode.GetSelection() == 1: # -> where newsqlstr = '' if sqlstr.lower().find('where') < 0: newsqlstr += ' WHERE' newsqlstr += ' ' + value curspos = self.text_sql.GetLastPosition() + len(newsqlstr) newsqlstr = sqlstr + newsqlstr if newsqlstr: self.text_sql.SetValue(newsqlstr) wx.CallAfter(self.text_sql.SetFocus) self.text_sql.SetInsertionPoint(curspos) def _initSqlFunctions(self): self.sqlFuncs = {} # TODO add functions for other drivers self.sqlFuncs['sqlite'] = { 'ABS': ['ABS()'], 'LENGTH': ['LENGTH()'], 'LOWER': ['LOWER()'], 'LTRIM': ['LTRIM(,)'], 'MAX': ['MAX()'], 'MIN': ['MIN()'], 'RTRIM': ['RTRIM(,)'], 'SUBSTR': ['SUBSTR (,[,])'], 'TRIM': ['TRIM (,)'] }
class DrawingPanel(wx.Panel): def __init__(self, parent, giface, settings): wx.Panel.__init__(self, parent) self.giface = giface self.settings = settings self.settingsChanged = Signal('ScanningPanel.settingsChanged') if 'drawing' not in self.settings: self.settings['drawing'] = {} self.settings['drawing']['active'] = False self.settings['drawing']['name'] = '' self.settings['drawing']['type'] = 'point' self.settings['drawing']['append'] = False self.settings['drawing']['appendName'] = '' self.settings['drawing']['threshold'] = 760 mainSizer = wx.BoxSizer(wx.VERTICAL) self.ifDraw = wx.CheckBox(self, label=_("Draw vector:")) self.ifDraw.SetValue(self.settings['drawing']['active']) self.ifDraw.Bind(wx.EVT_CHECKBOX, self.OnDrawChange) self.ifDraw.Bind(wx.EVT_CHECKBOX, self.OnEnableDrawing) self.draw_vector = Select(self, size=(-1, -1), type='vector') self.draw_vector.SetValue(self.settings['drawing']['name']) self.draw_vector.Bind(wx.EVT_TEXT, self.OnDrawChange) self.draw_type = wx.RadioBox(parent=self, label="Vector type", choices=['point', 'line', 'area']) {'point': 0, 'line': 1, 'area': 2}[self.settings['drawing']['type']] self.draw_type.SetSelection({'point': 0, 'line': 1, 'area': 2}[self.settings['drawing']['type']]) self.draw_type.Bind(wx.EVT_RADIOBOX, self.OnDrawChange) self.threshold = wx.SpinCtrl(parent=self, min=0, max=765, initial=int(self.settings['drawing']['threshold'])) self.threshold.SetValue(int(self.settings['drawing']['threshold'])) self.threshold.Bind(wx.EVT_SPINCTRL, self.OnDrawChange) self.append = wx.CheckBox(parent=self, label="Append vector") self.append.SetValue(self.settings['drawing']['append']) self.append.Bind(wx.EVT_CHECKBOX, self.OnDrawChange) self.appendName = Select(self, size=(-1, -1), type='vector') self.appendName.SetValue(self.settings['drawing']['appendName']) self.appendName.Bind(wx.EVT_TEXT, self.OnDrawChange) self.clearBtn = wx.Button(parent=self, label="Clear") self.clearBtn.Bind(wx.EVT_BUTTON, lambda evt: self._newAppendedVector(evt)) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.ifDraw, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.draw_vector, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.draw_type, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(wx.StaticText(self, label='Brightness threshold:'), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.threshold, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.append, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.appendName, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.clearBtn, flag=wx.ALIGN_CENTER_VERTICAL, border=5) mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) self.SetSizer(mainSizer) mainSizer.Fit(self) self.EnableDrawing(self.ifDraw.IsChecked()) def OnDrawChange(self, event): self.settings['drawing']['active'] = self.ifDraw.GetValue() self.settings['drawing']['name'] = self.draw_vector.GetValue().split('@')[0] self.settings['drawing']['appendName'] = self.appendName.GetValue().split('@')[0] self.settings['drawing']['type'] = ['point', 'line', 'area'][self.draw_type.GetSelection()] self.settings['drawing']['append'] = self.append.IsChecked() self.settings['drawing']['threshold'] = self.threshold.GetValue() event.Skip() self.settingsChanged.emit() def OnEnableDrawing(self, event): self.EnableDrawing(self.ifDraw.IsChecked()) event.Skip() def EnableDrawing(self, enable): self.draw_vector.Enable(enable) self.appendName.Enable(enable) self.draw_type.Enable(enable) self.append.Enable(enable) self.threshold.Enable(enable) self.clearBtn.Enable(enable) def appendVector(self): if not self.settings['drawing']['append']: return ff = gscript.find_file(self.settings['drawing']['appendName'], element='vector', mapset=gscript.gisenv()['MAPSET']) if not(ff and ff['fullname']): self._newAppendedVector() gscript.run_command('v.patch', input=self.settings['drawing']['name'], output=self.settings['drawing']['appendName'], flags='a', overwrite=True, quiet=True) def _newAppendedVector(self, event=None): gscript.run_command('v.edit', tool='create', map=self.settings['drawing']['appendName'], overwrite=True, quiet=True)
class LayersList(TreeCtrl): def __init__(self, parent, web_service, style, pos=wx.DefaultPosition): """List of layers and styles available in capabilities file""" self.parent = parent self.ws = web_service TreeCtrl.__init__(self, parent=parent, id=wx.ID_ANY, style=style) self.root = None self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnListSelChanging) self.layerSelected = Signal("LayersList.layerSelected") def LoadData(self, cap=None): """Load data into list""" # detete first all items self.DeleteAllItems() if not cap: return def AddLayerChildrenToTree(parent_layer, parent_item): """Recursive function which adds all capabilities layers/styles to the LayersList. """ def gettitle(layer): """Helper function""" if layer.GetLayerData("title") is not None: layer_title = layer.GetLayerData("title") elif layer.GetLayerData("name") is not None: layer_title = layer.GetLayerData("name") else: layer_title = str(layer.GetId()) return layer_title def addlayer(layer, item): styles = layer.GetLayerData("styles") def_st = None for st in styles: if st["name"]: style_name = st["name"] else: continue if st["title"]: style_name = st["title"] if st["isDefault"]: def_st = st style_item = self.AppendItem(item, style_name) self.SetItemData( style_item, { "type": "style", "layer": layer, # it is parent layer of style "style": st, }, ) self.SetItemData( item, { "type": "layer", # is it layer or style? "layer": layer, # Layer instance from web_services.cap_interface "style": def_st, }, ) # layer can have assigned default style if parent_layer is None: parent_layer = cap.GetRootLayer() layer_title = gettitle(parent_layer) parent_item = self.AddRoot(layer_title) addlayer(parent_layer, parent_item) for layer in parent_layer.GetChildren(): item = self.AppendItem(parent_item, gettitle(layer)) addlayer(layer, item) AddLayerChildrenToTree(layer, item) AddLayerChildrenToTree(None, None) # self.ExpandAll(self.GetRootItem()) def GetSelectedLayers(self): """Get selected layers/styles in LayersList :return: dict with these items: * 'name' : layer name used for request if it is style, it is name of parent layer * 'title' : layer title * 'style' : {'name' : 'style name', title : 'style title'} * 'cap_intf_l' : \*Layer instance from web_services.cap_interface """ sel_layers = self.GetSelections() sel_layers_dict = [] for s in sel_layers: try: layer = self.GetItemData(s)["layer"] except ValueError: continue sel_layers_dict.append( { "name": layer.GetLayerData("name"), "title": layer.GetLayerData("title"), "style": self.GetItemData(s)["style"], "cap_intf_l": layer, } ) return sel_layers_dict def OnListSelChanging(self, event): """Do not allow selecting items, which cannot be requested from server.""" def _emitSelected(layer): title = layer.GetLayerData("title") self.layerSelected.emit(title=title) def _selectRequestableChildren(item, list_to_check, items_to_sel): self.Expand(item) child_item, cookie = self.GetFirstChild(item) while child_item and child_item.IsOk(): if self.GetItemData(child_item)[ "layer" ].IsRequestable() and not self.IsSelected(child_item): items_to_sel.append(child_item) elif not self.GetItemData(child_item)["layer"].IsRequestable(): list_to_check.append(child_item) child_item, cookie = self.GetNextChild(item, cookie) cur_item = event.GetItem() if not self.GetItemData(cur_item)["layer"].IsRequestable(): event.Veto() if not self.HasFlag(wx.TR_MULTIPLE): return _emitSelected(self.GetItemData(cur_item)["layer"]) items_to_chck = [] items_to_sel = [] chck_item = cur_item while True: _selectRequestableChildren(chck_item, items_to_chck, items_to_sel) if items_to_chck: chck_item = items_to_chck.pop() else: break while items_to_sel: self.SelectItem(items_to_sel.pop(), select=True) else: _emitSelected(self.GetItemData(cur_item)["layer"]) def GetItemCount(self): """Required for listmix.ListCtrlAutoWidthMixin""" return 0 def GetCountPerPage(self): """Required for listmix.ListCtrlAutoWidthMixin""" return 0 def SelectLayers(self, l_st_list): """Select layers/styles in LayersList :param l_st_list: [{style : 'style_name', layer : 'layer_name'}, ...] :return: items from l_st_list which were not found """ def checknext(root_item, l_st_list, items_to_sel): def compare(item, l_name, st_name): it_l_name = self.GetItemData(item)["layer"].GetLayerData("name") it_st = self.GetItemData(item)["style"] it_type = self.GetItemData(item)["type"] if it_l_name == l_name and ( (not it_st and not st_name) or (it_st and it_st["name"] == st_name and it_type == "style") ): return True return False (child, cookie) = self.GetFirstChild(root_item) while child.IsOk(): for i, l_st in enumerate(l_st_list): l_name = l_st["layer"] st_name = l_st["style"] if compare(child, l_name, st_name): items_to_sel[i] = [child, l_st] break if len(items_to_sel) == len(l_st_list): if self.ItemHasChildren(child): checknext(child, l_st_list, items_to_sel) child = self.GetNextSibling(child) self.UnselectAll() l_st_list = deepcopy(l_st_list) root_item = self.GetRootItem() items_to_sel = [None] * len(l_st_list) checknext(root_item, l_st_list, items_to_sel) self.CollapseAll() # items are selected according to position in l_st_list # to be added to Layers order list in right order for i in items_to_sel: if not i: continue item, l_st = i keep = False if self.HasFlag(wx.TR_MULTIPLE): keep = True self.SelectItem(item, select=keep) self.SetFocusedItem(item) self.Expand(item) l_st_list.remove(l_st) return l_st_list
class SearchModuleWindow(wx.Panel): """Menu tree and search widget for searching modules. Signal: showNotification - attribute 'message' """ def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY, **kwargs): self.parent = parent self._handlerObj = handlerObj self._giface = giface self.showNotification = Signal('SearchModuleWindow.showNotification') wx.Panel.__init__(self, parent=parent, id=id, **kwargs) # tree self._tree = CTreeView(model=model, parent=self) self._tree.SetToolTip( _("Double-click or Ctrl-Enter to run selected module")) # self._dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY, # label = " %s " % _("Module tree")) # search widget self._search = SearchModuleWidget(parent=self, model=model, showChoice=False) self._search.showSearchResult.connect( lambda result: self._tree.Select(result)) self._search.showNotification.connect(self.showNotification) self._helpText = StaticText( parent=self, id=wx.ID_ANY, label="Press Enter for next match, Ctrl+Enter to run command") self._helpText.SetForegroundColour( wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) # buttons self._btnRun = Button(self, id=wx.ID_OK, label=_("&Run")) self._btnRun.SetToolTip(_("Run selected module from the tree")) self._btnHelp = Button(self, id=wx.ID_ANY, label=_("H&elp")) self._btnHelp.SetToolTip( _("Show manual for selected module from the tree")) self._btnAdvancedSearch = Button(self, id=wx.ID_ANY, label=_("Adva&nced search...")) self._btnAdvancedSearch.SetToolTip( _("Do advanced search using %s module") % 'g.search.module') # bindings self._btnRun.Bind(wx.EVT_BUTTON, lambda evt: self.Run()) self._btnHelp.Bind(wx.EVT_BUTTON, lambda evt: self.Help()) self._btnAdvancedSearch.Bind(wx.EVT_BUTTON, lambda evt: self.AdvancedSearch()) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) self._tree.selectionChanged.connect(self.OnItemSelected) self._tree.itemActivated.connect(lambda node: self.Run(node)) self._layout() self._search.SetFocus() def _layout(self): """Do dialog layout""" sizer = wx.BoxSizer(wx.VERTICAL) # body dataSizer = wx.BoxSizer(wx.HORIZONTAL) dataSizer.Add(self._tree, proportion=1, flag=wx.EXPAND) # buttons btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(self._btnAdvancedSearch, proportion=0) btnSizer.AddStretchSpacer() btnSizer.Add(self._btnHelp, proportion=0) btnSizer.Add(self._btnRun, proportion=0) sizer.Add(dataSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add(self._search, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) sizer.Add(self._helpText, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5) sizer.Fit(self) sizer.SetSizeHints(self) self.SetSizer(sizer) self.Fit() self.SetAutoLayout(True) self.Layout() def _GetSelectedNode(self): selection = self._tree.GetSelected() if not selection: return None return selection[0] def Run(self, node=None): """Run selected command. :param node: a tree node associated with the module or other item """ if not node: node = self._GetSelectedNode() # nothing selected if not node: return data = node.data # non-leaf nodes if not data: return # extract name of the handler and create a new call handler = 'self._handlerObj.' + data['handler'].lstrip('self.') if data['command']: eval(handler)(event=None, cmd=data['command'].split()) else: eval(handler)(event=None) def Help(self, node=None): """Show documentation for a module""" if not node: node = self._GetSelectedNode() # nothing selected if not node: return data = node.data # non-leaf nodes if not data: return if not data['command']: # showing nothing for non-modules return # strip parameters from command if present name = data['command'].split()[0] self._giface.Help(name) self.showNotification.emit( message=_("Documentation for %s is now open in the web browser") % name) def AdvancedSearch(self): """Show advanced search window""" self._handlerObj.RunMenuCmd(cmd=['g.search.modules']) def OnKeyUp(self, event): """Key or key combination pressed""" if event.ControlDown() and \ event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): self.Run() def OnItemSelected(self, node): """Item selected""" data = node.data if not data or 'command' not in data: return if data['command']: label = data['command'] if data['description']: label += ' -- ' + data['description'] else: label = data['description'] self.showNotification.emit(message=label)
class SwipeMapDialog(wx.Dialog): """Dialog used to select maps. There are two modes - simple (only two raster maps), or two layer lists. """ def __init__(self, parent, title=_("Select raster maps"), first=None, second=None, firstLayerList=None, secondLayerList=None): wx.Dialog.__init__(self, parent=parent, title=title, style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE) if firstLayerList is None: self._firstLayerList = LayerList() else: self._firstLayerList = copy.deepcopy(firstLayerList) if secondLayerList is None: self._secondLayerList = LayerList() else: self._secondLayerList = copy.deepcopy(secondLayerList) self._firstPanel = self._createSimplePanel() self._secondPanel = self._createAdvancedPanel() self.btnSwitch = Button(self) self.btnCancel = Button(self, id=wx.ID_CANCEL) self.btnApply = Button(self, id=wx.ID_APPLY) self.btnOK = Button(self, id=wx.ID_OK) self.btnOK.SetDefault() self.btnSwitch.Bind(wx.EVT_BUTTON, self.OnSwitchMode) self.btnApply.Bind(wx.EVT_BUTTON, lambda evt: self._apply()) self.btnOK.Bind(wx.EVT_BUTTON, lambda evt: self._ok()) self.btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.Close()) self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide()) self.applyChanges = Signal('SwipeMapDialog.applyChanges') if first: self._firstRaster.SetValue(first) if second: self._secondRaster.SetValue(second) self._layout() def UnInit(self): self._firstLmgr.UnInit() self._secondLmgr.UnInit() def _layout(self): """Do layout""" mainSizer = wx.BoxSizer(wx.VERTICAL) self._switchSizer = wx.BoxSizer() self._switchSizer.Add(self._firstPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self._switchSizer.Add(self._secondPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) mainSizer.Add(self._switchSizer, proportion=1, flag=wx.EXPAND | wx.ALL) self.btnSizer = wx.StdDialogButtonSizer() self.btnSizer.AddButton(self.btnCancel) self.btnSizer.AddButton(self.btnOK) self.btnSizer.AddButton(self.btnApply) self.btnSizer.Realize() mainSizer.Add(self.btnSwitch, proportion=0, flag=wx.ALL | wx.ALIGN_LEFT, border=5) mainSizer.Add(self.btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) self.mainSizer = mainSizer self._switchMode(simple=True) self.SetSizer(mainSizer) mainSizer.Fit(self) def _createSimplePanel(self): panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) self._firstRaster = gselect.Select( parent=panel, type='raster', size=globalvar.DIALOG_GSELECT_SIZE, validator=SimpleValidator( callback=self.ValidatorCallback)) self._secondRaster = gselect.Select( parent=panel, type='raster', size=globalvar.DIALOG_GSELECT_SIZE, validator=SimpleValidator( callback=self.ValidatorCallback)) sizer.Add( StaticText( panel, label=_("Name of top/left raster map:")), proportion=0, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add(self._firstRaster, proportion=0, flag=wx.EXPAND | wx.ALL, border=1) sizer.Add( StaticText( panel, label=_("Name of bottom/right raster map:")), proportion=0, flag=wx.EXPAND | wx.ALL, border=1) sizer.Add(self._secondRaster, proportion=0, flag=wx.EXPAND | wx.ALL, border=1) self._firstRaster.SetFocus() panel.SetSizer(sizer) sizer.Fit(panel) return panel def _createAdvancedPanel(self): panel = wx.Panel(self) sizer = wx.BoxSizer(wx.HORIZONTAL) self._firstLmgr = SimpleLayerManager( parent=panel, layerList=self._firstLayerList, lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT) self._secondLmgr = SimpleLayerManager( parent=panel, layerList=self._secondLayerList, lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_RIGHT) sizer.Add( self._firstLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) sizer.Add( self._secondLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) panel.SetSizer(sizer) sizer.Fit(panel) return panel def _switchMode(self, simple): if simple: self._switchSizer.Show(self._firstPanel, show=True, recursive=True) self._switchSizer.Show( self._secondPanel, show=False, recursive=True) self.btnSwitch.SetLabel(_("Switch to advanced mode")) self.btnCancel.SetLabel(_("Cancel")) else: self._switchSizer.Show( self._firstPanel, show=False, recursive=True) self._switchSizer.Show( self._secondPanel, show=True, recursive=True) self.btnSwitch.SetLabel(_("Switch to simple mode")) self.btnCancel.SetLabel(_("Close")) self.Freeze() # doesn't do anything (at least on Ubuntu) self.btnSizer.Show(self.btnApply, simple) self.btnSizer.Show(self.btnOK, simple) self.btnSizer.Layout() self._switchSizer.Layout() self.Fit() self.Thaw() self.applyChanges.emit() def OnSwitchMode(self, event): if self._switchSizer.IsShown(self._secondPanel): self._switchMode(simple=True) else: self._switchMode(simple=False) def ValidatorCallback(self, win): if self._switchSizer.IsShown(self._secondPanel): return if win == self._firstRaster.GetTextCtrl(): GMessage(parent=self, message=_( "Name of the first map is missing.")) else: GMessage(parent=self, message=_( "Name of the second map is missing.")) def _ok(self): self._apply() self.Close() def _apply(self): # TODO check if not empty self.applyChanges.emit() def GetValues(self): """Get raster maps""" if self.IsSimpleMode(): return (self._firstRaster.GetValue(), self._secondRaster.GetValue()) else: return (self._firstLayerList, self._secondLayerList) def IsSimpleMode(self): if self._switchSizer.IsShown(self._firstPanel): return True return False def GetFirstSimpleLmgr(self): return self._firstLmgr def GetSecondSimpleLmgr(self): return self._secondLmgr
def __init__(self, parent, samplingType, icon=None, map_=None): wx.Panel.__init__(self, parent=parent) self.mapWindowProperties = MapWindowProperties() self.mapWindowProperties.setValuesFromUserSettings() giface = StandaloneGrassInterface() self.samplingtype = samplingType self.parent = parent if map_: self.map_ = map_ else: self.map_ = Map() self.map_.region = self.map_.GetRegion() self._mgr = wx.aui.AuiManager(self) self.mapWindow = BufferedMapWindow( parent=self, giface=giface, Map=self.map_, properties=self.mapWindowProperties, ) self._mgr.AddPane( self.mapWindow, wx.aui.AuiPaneInfo().CentrePane().Dockable(True).BestSize( (-1, -1)).Name("mapwindow").CloseButton(False).DestroyOnClose( True).Layer(0), ) self._toolSwitcher = ToolSwitcher() self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged) self.toolbar = RLiSetupToolbar(self, self._toolSwitcher) self.catId = 1 self._mgr.AddPane( self.toolbar, wx.aui.AuiPaneInfo().Name("maptoolbar").Caption( _("Map Toolbar")).ToolbarPane().Left().Name("mapToolbar"). CloseButton(False).Layer(1).Gripper(False).BestSize( (self.toolbar.GetBestSize())), ) self._mgr.Update() if self.samplingtype == SamplingType.REGIONS: self.afterRegionDrawn = Signal("RLiSetupMapPanel.afterRegionDrawn") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line") elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: self.sampleFrameChanged = Signal( "RLiSetupMapPanel.sampleFrameChanged") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="rectangle") elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: self.afterCircleDrawn = Signal("RLiSetupMapPanel.afterCircleDrawn") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line") else: self.sampleFrameChanged = Signal( "RLiSetupMapPanel.sampleFrameChanged") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="rectangle") self._registeredGraphics.AddPen( "rlisetup", wx.Pen(wx.GREEN, width=2, style=wx.SOLID)) self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]], penName="rlisetup", hide=True) if self.samplingtype != SamplingType.VECT: self.toolbar.SelectDefault()
class GConsoleWindow(wx.SplitterWindow): """Create and manage output console for commands run by GUI. """ def __init__(self, parent, gconsole, menuModel=None, margin=False, style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE, gcstyle=GC_EMPTY, **kwargs): """ :param parent: gui parent :param gconsole: console logic :param menuModel: tree model of modules (from menu) :param margin: use margin in output pane (GStc) :param style: wx.SplitterWindow style :param gcstyle: GConsole style (GC_EMPTY, GC_PROMPT to show command prompt, GC_SEARCH to show search widget) """ wx.SplitterWindow.__init__(self, parent, id=wx.ID_ANY, style=style, **kwargs) self.SetName("GConsole") self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY) self.panelProgress = wx.Panel(parent=self.panelOutput, id=wx.ID_ANY, name='progressPanel') self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY) # initialize variables self.parent = parent # GMFrame | CmdPanel | ? self._gconsole = gconsole self._menuModel = menuModel self._gcstyle = gcstyle self.lineWidth = 80 # signal which requests showing of a notification self.showNotification = Signal("GConsoleWindow.showNotification") # signal emitted when text appears in the console # parameter 'notification' suggests form of notification (according to # core.giface.Notification) self.contentChanged = Signal("GConsoleWindow.contentChanged") # progress bar self.progressbar = wx.Gauge(parent=self.panelProgress, id=wx.ID_ANY, range=100, pos=(110, 50), size=(-1, 25), style=wx.GA_HORIZONTAL) self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress) self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun) self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone) self._gconsole.writeLog.connect(self.WriteLog) self._gconsole.writeCmdLog.connect(self.WriteCmdLog) self._gconsole.writeWarning.connect(self.WriteWarning) self._gconsole.writeError.connect(self.WriteError) # text control for command output self.cmdOutput = GStc(parent=self.panelOutput, id=wx.ID_ANY, margin=margin, wrap=None) # search & command prompt # move to the if below # search depends on cmd prompt self.cmdPrompt = GPromptSTC(parent=self, menuModel=self._menuModel) self.cmdPrompt.promptRunCmd.connect( lambda cmd: self._gconsole.RunCmd(command=cmd)) self.cmdPrompt.showNotification.connect(self.showNotification) if not self._gcstyle & GC_PROMPT: self.cmdPrompt.Hide() if self._gcstyle & GC_SEARCH: self.infoCollapseLabelExp = _( "Click here to show search module engine") self.infoCollapseLabelCol = _( "Click here to hide search module engine") self.searchPane = wx.CollapsiblePane( parent=self.panelOutput, label=self.infoCollapseLabelExp, style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND) self.MakeSearchPaneContent(self.searchPane.GetPane(), self._menuModel) self.searchPane.Collapse(True) self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSearchPaneChanged, self.searchPane) self.search.moduleSelected.connect( lambda name: self.cmdPrompt.SetTextAndFocus(name + ' ')) else: self.search = None if self._gcstyle & GC_PROMPT: cmdLabel = _("Command prompt") self.outputBox = wx.StaticBox(parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % _("Output window")) self.cmdBox = wx.StaticBox(parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % cmdLabel) # buttons self.btnOutputClear = wx.Button(parent=self.panelOutput, id=wx.ID_CLEAR) self.btnOutputClear.SetToolTipString(_("Clear output window content")) self.btnCmdClear = wx.Button(parent=self.panelOutput, id=wx.ID_CLEAR) self.btnCmdClear.SetToolTipString(_("Clear command prompt content")) self.btnOutputSave = wx.Button(parent=self.panelOutput, id=wx.ID_SAVE) self.btnOutputSave.SetToolTipString( _("Save output window content to the file")) self.btnCmdAbort = wx.Button(parent=self.panelProgress, id=wx.ID_STOP) self.btnCmdAbort.SetToolTipString(_("Abort running command")) self.btnCmdProtocol = wx.ToggleButton(parent=self.panelOutput, id=wx.ID_ANY, label=_("&Log file"), size=self.btnCmdClear.GetSize()) self.btnCmdProtocol.SetToolTipString( _("Toggle to save list of executed commands into " "a file; content saved when switching off.")) if not self._gcstyle & GC_PROMPT: self.btnCmdClear.Hide() self.btnCmdProtocol.Hide() self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase) self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear) self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave) self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort) self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol) self._layout() def _layout(self): """Do layout""" self.outputSizer = wx.BoxSizer(wx.VERTICAL) progressSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer = wx.BoxSizer(wx.HORIZONTAL) if self._gcstyle & GC_PROMPT: outBtnSizer = wx.StaticBoxSizer(self.outputBox, wx.HORIZONTAL) cmdBtnSizer = wx.StaticBoxSizer(self.cmdBox, wx.HORIZONTAL) else: outBtnSizer = wx.BoxSizer(wx.HORIZONTAL) cmdBtnSizer = wx.BoxSizer(wx.HORIZONTAL) if self._gcstyle & GC_PROMPT: promptSizer = wx.BoxSizer(wx.VERTICAL) promptSizer.Add(item=self.cmdPrompt, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3) helpText = wx.StaticText( self.panelPrompt, id=wx.ID_ANY, label= "Press Tab to display command help, Ctrl+Space to autocomplete" ) helpText.SetForegroundColour( wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT)) promptSizer.Add(item=helpText, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5) if self._gcstyle & GC_SEARCH: self.outputSizer.Add(item=self.searchPane, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) self.outputSizer.Add(item=self.cmdOutput, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) if self._gcstyle & GC_PROMPT: proportion = 1 else: proportion = 0 outBtnSizer.AddStretchSpacer() outBtnSizer.Add(item=self.btnOutputClear, proportion=proportion, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT, border=5) outBtnSizer.Add(item=self.btnOutputSave, proportion=proportion, flag=wx.ALIGN_RIGHT | wx.RIGHT, border=5) cmdBtnSizer.Add(item=self.btnCmdProtocol, proportion=1, flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, border=5) cmdBtnSizer.Add(item=self.btnCmdClear, proportion=1, flag=wx.ALIGN_CENTER | wx.RIGHT, border=5) progressSizer.Add(item=self.btnCmdAbort, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=5) progressSizer.Add(item=self.progressbar, proportion=1, flag=wx.ALIGN_CENTER | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5) self.panelProgress.SetSizer(progressSizer) progressSizer.Fit(self.panelProgress) btnSizer.Add(item=outBtnSizer, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER, border=5) btnSizer.Add(item=cmdBtnSizer, proportion=1, flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT, border=5) self.outputSizer.Add(item=self.panelProgress, proportion=0, flag=wx.EXPAND) self.outputSizer.Add(item=btnSizer, proportion=0, flag=wx.EXPAND) self.outputSizer.Fit(self) self.outputSizer.SetSizeHints(self) self.panelOutput.SetSizer(self.outputSizer) # eliminate gtk_widget_size_allocate() warnings # avoid to use a deprecated method in wxPython >= 2.9 getattr(self.outputSizer, 'FitInside', self.outputSizer.SetVirtualSizeHints)(self.panelOutput) if self._gcstyle & GC_PROMPT: promptSizer.Fit(self) promptSizer.SetSizeHints(self) self.panelPrompt.SetSizer(promptSizer) # split window if self._gcstyle & GC_PROMPT: self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50) else: self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45) self.Unsplit() self.SetMinimumPaneSize(self.btnCmdClear.GetSize()[1] + 25) self.SetSashGravity(1.0) self.outputSizer.Hide(self.panelProgress) # layout self.SetAutoLayout(True) self.Layout() def MakeSearchPaneContent(self, pane, model): """Create search pane""" border = wx.BoxSizer(wx.VERTICAL) self.search = SearchModuleWidget(parent=pane, model=model) self.search.showNotification.connect(self.showNotification) border.Add(item=self.search, proportion=0, flag=wx.EXPAND | wx.ALL, border=1) pane.SetSizer(border) border.Fit(pane) def OnSearchPaneChanged(self, event): """Collapse search module box""" if self.searchPane.IsExpanded(): self.searchPane.SetLabel(self.infoCollapseLabelCol) else: self.searchPane.SetLabel(self.infoCollapseLabelExp) self.panelOutput.Layout() self.panelOutput.SendSizeEvent() def GetPanel(self, prompt=True): """Get panel :param prompt: get prompt / output panel :return: wx.Panel reference """ if prompt: return self.panelPrompt return self.panelOutput def WriteLog(self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT): """Generic method for writing log message in given style. Emits contentChanged signal. :param line: text line :param style: text style (see GStc) :param stdout: write to stdout or stderr :param notification: form of notification """ self.cmdOutput.SetStyle() # documenting old behavior/implementation: # switch notebook if required # now, let user to bind to the old event if not style: style = self.cmdOutput.StyleDefault # p1 = self.cmdOutput.GetCurrentPos() p1 = self.cmdOutput.GetEndStyled() # self.cmdOutput.GotoPos(p1) self.cmdOutput.DocumentEnd() for line in text.splitlines(): # fill space if len(line) < self.lineWidth: diff = self.lineWidth - len(line) line += diff * ' ' self.cmdOutput.AddTextWrapped(line, wrap=wrap) # adds '\n' p2 = self.cmdOutput.GetCurrentPos() self.cmdOutput.StartStyling(p1, 0xff) self.cmdOutput.SetStyling(p2 - p1, style) self.cmdOutput.EnsureCaretVisible() self.contentChanged.emit(notification=notification) def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE): """Write message in selected style :param text: message to be printed :param pid: process pid or None :param switchPage: True to switch page """ if pid: text = '(' + str(pid) + ') ' + text self.WriteLog(text, style=self.cmdOutput.StyleCommand, notification=notification) def WriteWarning(self, text): """Write message in warning style""" self.WriteLog(text, style=self.cmdOutput.StyleWarning, notification=Notification.MAKE_VISIBLE) def WriteError(self, text): """Write message in error style""" self.WriteLog(text, style=self.cmdOutput.StyleError, notification=Notification.MAKE_VISIBLE) def OnOutputClear(self, event): """Clear content of output window""" self.cmdOutput.SetReadOnly(False) self.cmdOutput.ClearAll() self.cmdOutput.SetReadOnly(True) self.progressbar.SetValue(0) def GetProgressBar(self): """Return progress bar widget""" return self.progressbar def OnOutputSave(self, event): """Save (selected) text from output window to the file""" text = self.cmdOutput.GetSelectedText() if not text: text = self.cmdOutput.GetText() # add newline if needed if len(text) > 0 and text[-1] != '\n': text += '\n' dlg = wx.FileDialog( self, message=_("Save file as..."), defaultFile="grass_cmd_output.txt", wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") % { 'txt': _("Text files"), 'files': _("Files") }, style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) # Show the dialog and retrieve the user response. If it is the OK response, # process the data. if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() try: output = open(path, "w") output.write(text) except IOError as e: GError( _("Unable to write file '%(path)s'.\n\nDetails: %(error)s") % { 'path': path, 'error': e }) finally: output.close() message = _("Command output saved into '%s'") % path self.showNotification.emit(message=message) dlg.Destroy() def SetCopyingOfSelectedText(self, copy): """Enable or disable copying of selected text in to clipboard. Effects prompt and output. :param bool copy: True for enable, False for disable """ if copy: self.cmdPrompt.Bind(stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged) self.cmdOutput.Bind(stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged) else: self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED) self.cmdOutput.Unbind(stc.EVT_STC_PAINTED) def OnCmdOutput(self, event): """Prints command output. Emits contentChanged signal. """ message = event.text type = event.type self.cmdOutput.AddStyledMessage(message, type) if event.type in ('warning', 'error'): self.contentChanged.emit(notification=Notification.MAKE_VISIBLE) else: self.contentChanged.emit(notification=Notification.HIGHLIGHT) def OnCmdProgress(self, event): """Update progress message info""" self.progressbar.SetValue(event.value) event.Skip() def CmdProtocolSave(self): """Save list of manually entered commands into a text log file""" if not hasattr(self, 'cmdFileProtocol'): return # it should not happen try: output = open(self.cmdFileProtocol, "a") cmds = self.cmdPrompt.GetCommands() output.write('\n'.join(cmds)) if len(cmds) > 0: output.write('\n') except IOError as e: GError( _("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") % { 'filePath': self.cmdFileProtocol, 'error': e }) finally: output.close() message = _("Command log saved to '%s'") % self.cmdFileProtocol self.showNotification.emit(message=message) del self.cmdFileProtocol def OnCmdProtocol(self, event=None): """Save commands into file""" if not event.IsChecked(): # stop capturing commands, save list of commands to the # protocol file self.CmdProtocolSave() else: # start capturing commands self.cmdPrompt.ClearCommands() # ask for the file dlg = wx.FileDialog( self, message=_("Save file as..."), defaultFile="grass_cmd_log.txt", wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") % { 'txt': _("Text files"), 'files': _("Files") }, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: self.cmdFileProtocol = dlg.GetPath() else: wx.CallAfter(self.btnCmdProtocol.SetValue, False) dlg.Destroy() event.Skip() def OnCmdRun(self, event): """Run command""" self.outputSizer.Show(self.panelProgress) self.outputSizer.Layout() event.Skip() def OnCmdDone(self, event): """Command done (or aborted) """ self.progressbar.SetValue(0) # reset progress bar on '0%' wx.CallLater(100, self._hideProgress) event.Skip() def _hideProgress(self): self.outputSizer.Hide(self.panelProgress) self.outputSizer.Layout() def ResetFocus(self): """Reset focus""" self.cmdPrompt.SetFocus() def GetPrompt(self): """Get prompt""" return self.cmdPrompt
class VectorSelectBase(): """@brief Main class of vector selection function It allows selecting vector features from map display and to export them as a new vector map. Current version allows selecting features one-by-one by single click in map display. This class can be initialized with (see CreateDialog()) or without (see gselect) dialog (see VectorSelectDialog). """ def __init__(self, parent, giface): self.parent = parent self._giface = giface self.register = False self.mapWin = self._giface.GetMapWindow() self.mapDisp = giface.GetMapDisplay() self.RegisterMapEvtHandler() self.selectedFeatures = [] self.mapName = None # chosen map for selecting features self._dialog = None self.onCloseDialog = None self.updateLayer = Signal('VectorSelectBase.updateLayer') self.painter = VectorSelectHighlighter(self.mapDisp, giface) def CreateDialog(self, createButton=True): """Create dialog :param createButton: True to add 'create new map' button """ if self._dialog: return self._dialog = VectorSelectDialog(parent=self.parent) self._dialog.Bind(wx.EVT_CLOSE, self.OnCloseDialog) if createButton: createMap = Button(self._dialog, wx.ID_ANY, _("Create a new map")) createMap.Bind(wx.EVT_BUTTON, self.OnExportMap) self._dialog.AddWidget(createMap, proportion=0.1) self.slist = VectorSelectList(self._dialog) self.slist.Bind(wx.EVT_LIST_KEY_DOWN, self.OnDelete) self.slist.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnDeleteRow) self._dialog.AddWidget(self.slist) self.onCloseDialog = Signal('VectorSelectBase.onCloseDialog') def OnDeleteRow(self, event=None): """Delete row in widget """ index = self.slist.GetFocusedItem() category = self.slist.GetItemText(index) for item in self.selectedFeatures: if int(item['Category']) == int(category): self.selectedFeatures.remove(item) break self.slist.DeleteItem(index) self._draw() def OnDelete(self, event): """Delete row in widget by press key(delete) """ keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE: self.OnDeleteRow() def RegisterMapEvtHandler(self): if not self.register: self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, self._onMapClickHandler, 'cross') self.register = True def UnregisterMapEvtHandler(self): """Unregistrates _onMapClickHandler from mapWin""" if self.register: self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self._onMapClickHandler) self.register = False def OnClose(self): self.selectedFeatures = [] self._draw() self.UnregisterMapEvtHandler() def OnCloseDialog(self, evt=None): if not self.onCloseDialog: return self.onCloseDialog.emit() self.selectedFeatures = [] self.painter.Clear() self._dialog.Destroy() self.UnregisterMapEvtHandler() def Reset(self): """Remove items from dialog list""" self.selectedFeatures = [] if self._dialog: self.slist.DeleteAllItems() self._dialog.Raise() self.RegisterMapEvtHandler() def _onMapClickHandler(self, event): """Registred handler for clicking on grass disp """ if event == "unregistered": return vWhatDic = self.QuerySelectedMap() if 'Category' in vWhatDic: self.AddVecInfo(vWhatDic) self._draw() if self._dialog: self._dialog.Raise() def AddVecInfo(self, vInfoDictTMP): """Update vector in list Note: click on features add category second click on the same vector remove category from list """ if len(self.selectedFeatures) > 0: for sel in self.selectedFeatures: if sel['Category'] == vInfoDictTMP[ 'Category']: # features is selected=> remove features self.selectedFeatures.remove(sel) if self._dialog: # if dialog initilized->update dialog self.slist.RemoveItem(vInfoDictTMP) return True self.selectedFeatures.append(vInfoDictTMP) if self._dialog: self.slist.AddItem(vInfoDictTMP) else: # only one is selected self.selectedFeatures.append(vInfoDictTMP) if self._dialog: self.slist.AddItem(vInfoDictTMP) if len(self.selectedFeatures) == 0: return False return True def _draw(self): """Call class 'VectorSelectHighlighter' to draw selected features""" self.updateLayer.emit() if len(self.selectedFeatures) > 0: self.painter.SetLayer(self.selectedFeatures[0]['Layer']) self.painter.SetMap(self.selectedFeatures[0]['Map'] + '@' + self.selectedFeatures[0]['Mapset']) tmp = list() for i in self.selectedFeatures: tmp.append(i['Category']) self.painter.SetCats(tmp) self.painter.DrawSelected() else: self.painter.Clear() def GetSelectedMap(self): """Return name of selected map in layer tree""" layerList = self._giface.GetLayerList() layerSelected = layerList.GetSelectedLayer() if layerSelected is None: return None if not layerSelected.maplayer.IsActive(): GWarning(_("Selected map <%s> has been disabled for rendering. " "Operation canceled.") % str(layerSelected), parent=self.mapWin) return None if layerSelected: mapName = str(layerSelected) if self.mapName is not None: if self.mapName != mapName: self.Reset() else: mapName = None self.UnregisterMapEvtHandler() GError(_("No map layer selected. Operation canceled.")) return mapName def QuerySelectedMap(self): """Return w.what info from last clicked coords on display """ self.mapName = self.GetSelectedMap() if not self.mapName: return {} mapInfo = self.mapWin.GetMap() threshold = 10.0 * ( (mapInfo.region['e'] - mapInfo.region['w']) / mapInfo.width) try: query = grass.vector_what(map=[self.mapName], coord=self.mapWin.GetLastEN(), distance=threshold, skip_attributes=True) except grass.ScriptError: GError(parent=self, message=_("Failed to query vector map(s) <%s>.") % self.map) return None return query[0] def GetLineStringSelectedCats(self): """Return line of categories separated by comma""" strTMP = '' for cat in self.selectedFeatures: strTMP += str(cat['Category']) + ',' return strTMP[:-1] def _id_generator(self, size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for _ in range(size)) def OnExportMap(self, event): """Export selected features to a new map Add new map layer to layer tree and checked it @todo: set color of map to higlight color """ if len(self.selectedFeatures) == 0: GMessage(_('No features selected')) return lst = '' for cat in self.selectedFeatures: # build text string of categories for v.extract input lst += str(cat['Category']) + ',' lst = lst[:-1] outMap = str(self.selectedFeatures[0]['Map']) + '_selection' + str( self._id_generator(3)) ret, err = RunCommand('v.extract', input=self.selectedFeatures[0]['Map'], layer=self.selectedFeatures[0]['Layer'], output=outMap, cats=lst, getErrorMsg=True) if ret == 0: tree = self._giface.GetLayerTree() if tree: tree.AddLayer(ltype='vector', lname=outMap, lcmd=['d.vect', 'map=%s' % outMap], lchecked=True) # TODO colorize new map self.Reset() else: GMessage(_('Vector map <%s> was created') % outMap) self.Reset() else: GError(_("Unable to create a new vector map.\n\nReason: %s") % err) """
def __init__(self, parent, gconsole, menuModel=None, margin=False, style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE, gcstyle=GC_EMPTY, **kwargs): """ :param parent: gui parent :param gconsole: console logic :param menuModel: tree model of modules (from menu) :param margin: use margin in output pane (GStc) :param style: wx.SplitterWindow style :param gcstyle: GConsole style (GC_EMPTY, GC_PROMPT to show command prompt, GC_SEARCH to show search widget) """ wx.SplitterWindow.__init__(self, parent, id=wx.ID_ANY, style=style, **kwargs) self.SetName("GConsole") self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY) self.panelProgress = wx.Panel(parent=self.panelOutput, id=wx.ID_ANY, name='progressPanel') self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY) # initialize variables self.parent = parent # GMFrame | CmdPanel | ? self._gconsole = gconsole self._menuModel = menuModel self._gcstyle = gcstyle self.lineWidth = 80 # signal which requests showing of a notification self.showNotification = Signal("GConsoleWindow.showNotification") # signal emitted when text appears in the console # parameter 'notification' suggests form of notification (according to # core.giface.Notification) self.contentChanged = Signal("GConsoleWindow.contentChanged") # progress bar self.progressbar = wx.Gauge(parent=self.panelProgress, id=wx.ID_ANY, range=100, pos=(110, 50), size=(-1, 25), style=wx.GA_HORIZONTAL) self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress) self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun) self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone) self._gconsole.writeLog.connect(self.WriteLog) self._gconsole.writeCmdLog.connect(self.WriteCmdLog) self._gconsole.writeWarning.connect(self.WriteWarning) self._gconsole.writeError.connect(self.WriteError) # text control for command output self.cmdOutput = GStc(parent=self.panelOutput, id=wx.ID_ANY, margin=margin, wrap=None) # search & command prompt # move to the if below # search depends on cmd prompt self.cmdPrompt = GPromptSTC(parent=self, menuModel=self._menuModel) self.cmdPrompt.promptRunCmd.connect( lambda cmd: self._gconsole.RunCmd(command=cmd)) self.cmdPrompt.showNotification.connect(self.showNotification) if not self._gcstyle & GC_PROMPT: self.cmdPrompt.Hide() if self._gcstyle & GC_SEARCH: self.infoCollapseLabelExp = _( "Click here to show search module engine") self.infoCollapseLabelCol = _( "Click here to hide search module engine") self.searchPane = wx.CollapsiblePane( parent=self.panelOutput, label=self.infoCollapseLabelExp, style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND) self.MakeSearchPaneContent(self.searchPane.GetPane(), self._menuModel) self.searchPane.Collapse(True) self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSearchPaneChanged, self.searchPane) self.search.moduleSelected.connect( lambda name: self.cmdPrompt.SetTextAndFocus(name + ' ')) else: self.search = None if self._gcstyle & GC_PROMPT: cmdLabel = _("Command prompt") self.outputBox = wx.StaticBox(parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % _("Output window")) self.cmdBox = wx.StaticBox(parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % cmdLabel) # buttons self.btnOutputClear = wx.Button(parent=self.panelOutput, id=wx.ID_CLEAR) self.btnOutputClear.SetToolTipString(_("Clear output window content")) self.btnCmdClear = wx.Button(parent=self.panelOutput, id=wx.ID_CLEAR) self.btnCmdClear.SetToolTipString(_("Clear command prompt content")) self.btnOutputSave = wx.Button(parent=self.panelOutput, id=wx.ID_SAVE) self.btnOutputSave.SetToolTipString( _("Save output window content to the file")) self.btnCmdAbort = wx.Button(parent=self.panelProgress, id=wx.ID_STOP) self.btnCmdAbort.SetToolTipString(_("Abort running command")) self.btnCmdProtocol = wx.ToggleButton(parent=self.panelOutput, id=wx.ID_ANY, label=_("&Log file"), size=self.btnCmdClear.GetSize()) self.btnCmdProtocol.SetToolTipString( _("Toggle to save list of executed commands into " "a file; content saved when switching off.")) if not self._gcstyle & GC_PROMPT: self.btnCmdClear.Hide() self.btnCmdProtocol.Hide() self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase) self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear) self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave) self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort) self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol) self._layout()
def __init__(self, parent, toolSwitcher, MapWindow, digitClass, giface, tools=[]): self.MapWindow = MapWindow self.Map = MapWindow.GetMap() # Map class instance self.tools = tools self.digitClass = digitClass BaseToolbar.__init__(self, parent, toolSwitcher) self.digit = None self._giface = giface self.fType = None # feature type for simple features editing self.editingStarted = Signal("VDigitToolbar.editingStarted") self.editingStopped = Signal("VDigitToolbar.editingStopped") self.editingBgMap = Signal("VDigitToolbar.editingBgMap") layerTree = self._giface.GetLayerTree() if layerTree: self.editingStarted.connect(layerTree.StartEditing) self.editingStopped.connect(layerTree.StopEditing) self.editingBgMap.connect(layerTree.SetBgMapForEditing) # currently selected map layer for editing (reference to MapLayer # instance) self.mapLayer = None # list of vector layers from Layer Manager (only in the current mapset) self.layers = [] self.comboid = self.combo = None self.undo = -1 self.redo = -1 # only one dialog can be open self.settingsDialog = None # create toolbars (two rows optionally) self.InitToolbar(self._toolbarData()) self._default = -1 # default action (digitize new point, line, etc.) self.action = {'desc': '', 'type': '', 'id': -1} self._currentAreaActionType = None # list of available vector maps self.UpdateListOfLayers(updateTool=True) for tool in ( 'addPoint', 'addLine', 'addBoundary', 'addCentroid', 'addArea', 'addVertex', 'deleteLine', 'deleteArea', 'displayAttr', 'displayCats', 'editLine', 'moveLine', 'moveVertex', 'removeVertex', 'additionalTools'): if hasattr(self, tool): tool = getattr(self, tool) self.toolSwitcher.AddToolToGroup( group='mouseUse', toolbar=self, tool=tool) else: Debug.msg(1, '%s skipped' % tool) # custom button for digitization of area/boundary/centroid # TODO: could this be somehow generalized? nAreaTools = 0 if self.tools and 'addBoundary' in self.tools: nAreaTools += 1 if self.tools and 'addCentroid' in self.tools: nAreaTools += 1 if self.tools and 'addArea' in self.tools: nAreaTools += 1 if nAreaTools != 1: self.areaButton = self.CreateSelectionButton( _("Select area/boundary/centroid tool")) self.areaButtonId = self.InsertControl(5, self.areaButton) self.areaButton.Bind(wx.EVT_BUTTON, self.OnAddAreaMenu) # realize toolbar self.Realize() # workaround for Mac bug. May be fixed by 2.8.8, but not before then. if self.combo: self.combo.Hide() self.combo.Show() # disable undo/redo if self.undo > 0: self.EnableTool(self.undo, False) if self.redo > 0: self.EnableTool(self.redo, False) self.FixSize(width=105)
class VDigitWindow(BufferedMapWindow): """A Buffered window extended for vector digitizer. """ def __init__(self, parent, giface, Map, properties, tree=None, id=wx.ID_ANY, lmgr=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): BufferedMapWindow.__init__(self, parent=parent, giface=giface, Map=Map, properties=properties, style=style, **kwargs) self.lmgr = lmgr self.tree = tree self.pdcVector = PseudoDC() self.toolbar = self.parent.GetToolbar('vdigit') self.digit = None # wxvdigit.IVDigit self._digitizingInfo = False # digitizing with info # Emitted when info about digitizing updated # Parameter text is a string with information # currently used only for coordinates of mouse cursor + segmnt and # total feature length self.digitizingInfo = Signal('VDigitWindow.digitizingInfo') # Emitted when some info about digitizing is or will be availbale self.digitizingInfoAvailable = Signal('VDigitWindow.digitizingInfo') # Emitted when some info about digitizing is or will be availbale # digitizingInfo signal is emmited only between digitizingInfoAvailable # and digitizingInfoUnavailable signals self.digitizingInfoUnavailable = Signal('VDigitWindow.digitizingInfo') self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.mouseMoving.connect(self._mouseMovingToDigitizingInfo) def GetDisplay(self): if self.digit: return self.digit.GetDisplay() return None def GetDigit(self): """Get digit class""" return self.digit def SetToolbar(self, toolbar): """Set up related toolbar """ self.toolbar = toolbar def _mouseMovingToDigitizingInfo(self, x, y): e, n = x, y precision = int(UserSettings.Get(group='projection', key='format', subkey='precision')) if self.toolbar.GetAction() != 'addLine' or \ self.toolbar.GetAction('type') not in ('line', 'boundary') or \ len(self.polycoords) == 0: # we cannot provide info, so find out if it is something new if self._digitizingInfo: self._digitizingInfo = False self.digitizingInfoUnavailable.emit() return # else, we can provide info, so find out if it is first time if not self._digitizingInfo: self._digitizingInfo = True self.digitizingInfoAvailable.emit() # for linear feature show segment and total length distance_seg = self.Distance(self.polycoords[-1], (e, n), screen=False)[0] distance_tot = distance_seg for idx in range(1, len(self.polycoords)): distance_tot += self.Distance(self.polycoords[idx - 1], self.polycoords[idx], screen=False)[0] text = "seg: %.*f; tot: %.*f" % (precision, distance_seg, precision, distance_tot) self.digitizingInfo.emit(text=text) def OnKeyDown(self, event): """Key pressed""" shift = event.ShiftDown() kc = event.GetKeyCode() event = None if not shift: if kc == ord('P'): event = wx.CommandEvent(winid=self.toolbar.addPoint) tool = self.toolbar.OnAddPoint elif kc == ord('L'): event = wx.CommandEvent(winid=self.toolbar.addLine) tool = self.toolbar.OnAddLine if event: self.toolbar.OnTool(event) tool(event) def _updateMap(self): if not self.toolbar or \ not self.toolbar.GetLayer(): return # set region self.digit.GetDisplay().UpdateRegion() # re-calculate threshold for digitization tool # self.parent.digit.GetDisplay().GetThreshold() # draw map # self.pdcVector.Clear() self.pdcVector.RemoveAll() item = None if self.tree: try: item = self.tree.FindItemByData( 'maplayer', self.toolbar.GetLayer()) except TypeError: pass if not self.tree or \ (self.tree and item and self.tree.IsItemChecked(item)): self.redrawAll = True self.digit.GetDisplay().DrawMap() # translate tmp objects (pointer position) if self.toolbar.GetAction() == 'moveLine' and \ hasattr(self, "moveInfo"): if 'beginDiff' in self.moveInfo: # move line for id in self.moveInfo['id']: self.pdcTmp.TranslateId(id, self.moveInfo['beginDiff'][0], self.moveInfo['beginDiff'][1]) del self.moveInfo['beginDiff'] def OnLeftDownAddLine(self, event): """Left mouse button pressed - add new feature """ try: mapLayer = self.toolbar.GetLayer().GetName() except: return if self.toolbar.GetAction('type') in ['point', 'centroid']: # add new point / centroiud east, north = self.Pixel2Cell(self.mouse['begin']) nfeat, fids = self.digit.AddFeature( self.toolbar.GetAction('type'), [(east, north)]) if nfeat < 1: return self.UpdateMap(render=False) # redraw map # add new record into atribute table if UserSettings.Get( group='vdigit', key="addRecord", subkey='enabled'): # select attributes based on layer and category cats = { fids[0]: { UserSettings.Get( group='vdigit', key="layer", subkey='value'): ( UserSettings.Get( group='vdigit', key="category", subkey='value'), )}} posWindow = self.ClientToScreen( (self.mouse['end'][0] + self.dialogOffset, self.mouse['end'][1] + self.dialogOffset)) addRecordDlg = DisplayAttributesDialog( parent=self, map=mapLayer, cats=cats, pos=posWindow, action="add", ignoreError=True) if self.toolbar.GetAction('type') == 'centroid': for fid in fids: self._geomAttrb(fid, addRecordDlg, 'area') self._geomAttrb(fid, addRecordDlg, 'perimeter') if addRecordDlg.IsFound(): addRecordDlg.ShowModal() addRecordDlg.Destroy() elif self.toolbar.GetAction('type') in ["line", "boundary", "area"]: # add new point to the line self.polycoords.append( self.Pixel2Cell( event.GetPosition())) self.DrawLines(pdc=self.pdcTmp) def _geomAttrb(self, fid, dialog, attrb): """Define geometry attributes """ mapLayer = self.toolbar.GetLayer() if self.tree: item = self.tree.FindItemByData('maplayer', mapLayer) vdigit = self.tree.GetLayerInfo(item, key='vdigit') else: item = vdigit = None if not vdigit or \ 'geomAttr' not in vdigit or \ attrb not in vdigit['geomAttr']: return val = -1 if attrb == 'length': val = self.digit.GetLineLength(fid) type = attrb elif attrb == 'area': val = self.digit.GetAreaSize(fid) type = attrb elif attrb == 'perimeter': val = self.digit.GetAreaPerimeter(fid) type = 'length' if val > 0: layer = int( UserSettings.Get( group='vdigit', key="layer", subkey='value')) column = vdigit['geomAttr'][attrb]['column'] val = UnitsConvertValue( val, type, vdigit['geomAttr'][attrb]['units']) dialog.SetColumnValue(layer, column, val) dialog.OnReset() def _geomAttrbUpdate(self, fids): """Update geometry atrributes of currently selected features :param fid: list feature id """ mapLayer = self.parent.toolbars['vdigit'].GetLayer() vectorName = mapLayer.GetName() if self.tree: item = self.tree.FindItemByData('maplayer', mapLayer) vdigit = self.tree.GetLayerInfo(item, key='vdigit') else: item = vdigit = None if not vdigit or 'geomAttr' not in vdigit: return dbInfo = gselect.VectorDBInfo(vectorName) sqlfile = tempfile.NamedTemporaryFile(mode="w") for fid in fids: for layer, cats in six.iteritems(self.digit.GetLineCats(fid)): table = dbInfo.GetTable(layer) for attrb, item in six.iteritems(vdigit['geomAttr']): val = -1 if attrb == 'length': val = self.digit.GetLineLength(fid) type = attrb elif attrb == 'area': val = self.digit.GetAreaSize(fid) type = attrb elif attrb == 'perimeter': val = self.digit.GetAreaPerimeter(fid) type = 'length' if val < 0: continue val = UnitsConvertValue(val, type, item['units']) for cat in cats: sqlfile.write( 'UPDATE %s SET %s = %f WHERE %s = %d;\n' % (table, item['column'], val, dbInfo.GetKeyColumn(layer), cat)) sqlfile.file.flush() RunCommand('db.execute', parent=True, quiet=True, input=sqlfile.name) def _updateATM(self): """Update open Attribute Table Manager .. todo:: use AddDataRow() instead """ if not self.lmgr: return # update ATM digitVector = self.toolbar.GetLayer().GetName() for atm in self.lmgr.dialogs['atm']: atmVector = atm.GetVectorName() if atmVector == digitVector: layer = UserSettings.Get( group='vdigit', key="layer", subkey='value') # TODO: use AddDataRow instead atm.LoadData(layer) def OnLeftDownEditLine(self, event): """Left mouse button pressed - edit linear feature - add new vertex. """ self.polycoords.append(self.Pixel2Cell(self.mouse['begin'])) self.moveInfo['id'].append(wx.NewId()) self.DrawLines(pdc=self.pdcTmp) def OnLeftDownMoveLine(self, event): """Left mouse button pressed - vector digitizer move feature/vertex, edit linear feature """ self.moveInfo = dict() # geographic coordinates of initial position (left-down) self.moveInfo['begin'] = None # list of ids to modify self.moveInfo['id'] = list() # set pen if self.toolbar.GetAction() in ["moveVertex", "editLine"]: pcolor = UserSettings.Get(group='vdigit', key="symbol", subkey=["highlight", "color"]) self.pen = self.polypen = wx.Pen(colour=pcolor, width=2, style=wx.SHORT_DASH) self.pdcTmp.SetPen(self.polypen) def OnLeftDownDisplayCA(self, event): """Left mouse button pressed - vector digitizer display categories or attributes action """ try: mapLayer = self.toolbar.GetLayer().GetName() except: return coords = self.Pixel2Cell(self.mouse['begin']) # unselect self.digit.GetDisplay().SetSelected([]) # select feature by point cats = {} self.digit.GetDisplay().SelectLineByPoint(coords) if not self.digit.GetDisplay().GetSelected(): for key in ('attributes', 'category'): if self.parent.dialogs[key] and \ self.parent.dialogs[key].IsShown(): self.parent.dialogs[key].Hide() self.UpdateMap(render=False, renderVector=True) return if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled'): lines = self.digit.GetDisplay().GetSelected() else: lines = ( self.digit.GetDisplay().GetSelected()[0], ) # only first found for line in lines: cats[line] = self.digit.GetLineCats(line) posWindow = self.ClientToScreen( (self.mouse['end'][0] + self.dialogOffset, self.mouse['end'][1] + self.dialogOffset)) if self.toolbar.GetAction() == "displayAttrs": # select attributes based on coordinates (all layers) if self.parent.dialogs['attributes'] is None: self.parent.dialogs['attributes'] = \ DisplayAttributesDialog(parent=self, map=mapLayer, cats=cats, action="update") else: # upgrade dialog self.parent.dialogs['attributes'].UpdateDialog(cats=cats) if self.parent.dialogs['attributes'] and \ self.parent.dialogs['attributes'].mapDBInfo: if len(cats.keys()) > 0: # highlight feature & re-draw map if not self.parent.dialogs['attributes'].IsShown(): self.parent.dialogs['attributes'].Show() else: if self.parent.dialogs['attributes'] and \ self.parent.dialogs['attributes'].IsShown(): self.parent.dialogs['attributes'].Hide() else: # displayCats if self.parent.dialogs['category'] is None: # open new dialog dlg = VDigitCategoryDialog(parent=self, vectorName=mapLayer, cats=cats, pos=posWindow, title=_("Update categories")) self.parent.dialogs['category'] = dlg else: # update currently open dialog self.parent.dialogs['category'].UpdateDialog(cats=cats) if self.parent.dialogs['category']: if len(cats.keys()) > 0: # highlight feature & re-draw map if not self.parent.dialogs['category'].IsShown(): self.parent.dialogs['category'].Show() else: if self.parent.dialogs['category'].IsShown(): self.parent.dialogs['category'].Hide() self.UpdateMap(render=False, renderVector=True) def OnLeftDownCopyCA(self, event): """Left mouse button pressed - vector digitizer copy categories or attributes action """ if not hasattr(self, "copyCatsList"): self.copyCatsList = [] else: self.copyCatsIds = [] self.mouse['box'] = 'box' def OnLeftDownCopyLine(self, event): """Left mouse button pressed - vector digitizer copy lines action """ if not hasattr(self, "copyIds"): self.copyIds = [] self.layerTmp = None def OnLeftDownBulkLine(self, event): """Left mouse button pressed - vector digitizer label 3D vector lines """ if len(self.polycoords) > 1: # start new line self.polycoords = [] self.ClearLines(pdc=self.pdcTmp) self.polycoords.append(self.Pixel2Cell(event.GetPosition())) if len(self.polycoords) == 1: begin = self.Pixel2Cell(self.polycoords[-1]) end = self.Pixel2Cell(self.mouse['end']) else: end = self.Pixel2Cell(self.polycoords[-1]) begin = self.Pixel2Cell(self.mouse['begin']) self.DrawLines(self.pdcTmp, polycoords=(begin, end)) def OnLeftDownUndo(self, event): """Left mouse button pressed with control key - vector digitizer undo functionality """ if self.mouse["use"] != "pointer" or not self.toolbar: return action = self.toolbar.GetAction() if (action == "addLine" and self.toolbar.GetAction('type') in ["line", "boundary", "area"]) or action == "editLine": # add line or boundary -> remove last point from the line try: removed = self.polycoords.pop() Debug.msg( 4, "VDigitWindow.OnMiddleDown(): polycoords_poped=%s" % [removed, ]) # self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1]) except: pass if action == "editLine": # remove last vertex & line if len(self.moveInfo['id']) > 1: self.moveInfo['id'].pop() self.UpdateMap(render=False, renderVector=False) elif action in ["deleteLine", "deleteArea", "moveLine", "splitLine", "addVertex", "removeVertex", "moveVertex", "copyCats", "flipLine", "mergeLine", "snapLine", "connectLine", "copyLine", "queryLine", "breakLine", "typeConv"]: # various tools -> unselected selected features self.digit.GetDisplay().SetSelected([]) if action in ["moveLine", "moveVertex", "editLine"] and \ hasattr(self, "moveInfo"): del self.moveInfo elif action == "copyCats": try: del self.copyCatsList del self.copyCatsIds except AttributeError: pass elif action == "copyLine": del self.copyIds if self.layerTmp: self.Map.DeleteLayer(self.layerTmp) self.UpdateMap(render=True, renderVector=False) del self.layerTmp self.polycoords = [] self.UpdateMap(render=False) # render vector elif action == "zbulkLine": # reset polyline self.polycoords = [] self.digit.GetDisplay().SetSelected([]) self.UpdateMap(render=False) self.redrawAll = True self.UpdateMap(render=False, renderVector=False) def _onLeftDown(self, event): """Left mouse button donw - vector digitizer various actions """ try: mapLayer = self.toolbar.GetLayer().GetName() except: GMessage(parent=self, message=_("No vector map selected for editing.")) event.Skip() return action = self.toolbar.GetAction() if not action: GMessage( parent=self, message=_( "Nothing to do. " "Choose appropriate tool from digitizer toolbar.")) event.Skip() return if action not in ("moveVertex", "addVertex", "removeVertex", "editLine"): # set pen self.pen = wx.Pen( colour=UserSettings.Get( group='vdigit', key='symbol', subkey=[ 'newSegment', 'color']), width=2, style=wx.SHORT_DASH) self.polypen = wx.Pen( colour=UserSettings.Get( group='vdigit', key='symbol', subkey=[ 'newLine', 'color']), width=2, style=wx.SOLID) if action in ("addVertex", "removeVertex", "splitLines"): # unselect self.digit.GetDisplay().SetSelected([]) if action == "addLine": self.OnLeftDownAddLine(event) elif action == "editLine" and \ hasattr(self, "moveInfo"): self.OnLeftDownEditLine(event) elif action in ("moveLine", "moveVertex", "editLine") and \ not hasattr(self, "moveInfo"): self.OnLeftDownMoveLine(event) elif action in ("displayAttrs" "displayCats"): self.OnLeftDownDisplayCA(event) elif action in ("copyCats", "copyAttrs"): self.OnLeftDownCopyCA(event) elif action == "copyLine": self.OnLeftDownCopyLine(event) elif action == "zbulkLine": self.OnLeftDownBulkLine(event) def OnLeftUpVarious(self, event): """Left mouse button released - vector digitizer various actions """ pos1 = self.Pixel2Cell(self.mouse['begin']) pos2 = self.Pixel2Cell(self.mouse['end']) nselected = 0 action = self.toolbar.GetAction() # -> delete line || move line || move vertex if action in ("moveVertex", "editLine"): if len(self.digit.GetDisplay().GetSelected()) == 0: nselected = int( self.digit.GetDisplay().SelectLineByPoint(pos1)['line'] != -1) if action == "editLine": try: selVertex = self.digit.GetDisplay( ).GetSelectedVertex(pos1)[0] except IndexError: selVertex = None if selVertex: # self.UpdateMap(render=False) ids = self.digit.GetDisplay().GetSelected(grassId=False) # move this line to tmp layer self.polycoords = [] for id in ids: if id % 2: # register only vertices e, n = self.Pixel2Cell( self.pdcVector.GetIdBounds(id)[0:2]) self.polycoords.append((e, n)) self.digit.GetDisplay().DrawSelected(False) if selVertex < ids[-1] / 2: # choose first or last node of line self.moveInfo['id'].reverse() self.polycoords.reverse() else: # unselect self.digit.GetDisplay().SetSelected([]) del self.moveInfo self.UpdateMap(render=False) elif action in ("copyCats", "copyAttrs"): if not hasattr(self, "copyCatsIds"): # 'from' -> select by point nselected = int( self.digit.GetDisplay().SelectLineByPoint(pos1)['line'] != -1) if nselected: self.copyCatsList = self.digit.GetDisplay().GetSelected() else: # -> 'to' -> select by bbox self.digit.GetDisplay().SetSelected([]) # return number of selected features (by box/point) nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2)) if nselected == 0: nselected = int( self.digit.GetDisplay().SelectLineByPoint(pos1)['line'] != -1) if nselected > 0: self.copyCatsIds = self.digit.GetDisplay().GetSelected() elif action == "queryLine": selected = self.digit.SelectLinesByQuery(bbox=(pos1, pos2)) nselected = len(selected) if nselected > 0: self.digit.GetDisplay().SetSelected(selected) else: # -> moveLine || deleteLine, etc. (select by point/box) if action == 'moveLine' and \ len(self.digit.GetDisplay().GetSelected()) > 0: nselected = 0 else: if action == 'deleteArea': nselected = int( self.digit.GetDisplay().SelectAreaByPoint(pos1)['area'] != -1) else: if action == 'moveLine': drawSeg = True else: drawSeg = False nselected = self.digit.GetDisplay().SelectLinesByBox( bbox=(pos1, pos2), drawSeg=drawSeg) if nselected == 0: nselected = int( self.digit.GetDisplay().SelectLineByPoint(pos1)['line'] != -1) if nselected > 0: if action in ("moveLine", "moveVertex") and \ hasattr(self, "moveInfo"): # get pseudoDC id of objects which should be redrawn if action == "moveLine": # -> move line self.moveInfo['id'] = self.digit.GetDisplay( ).GetSelected(grassId=False) else: # moveVertex self.moveInfo['id'] = self.digit.GetDisplay( ).GetSelectedVertex(pos1) if len(self.moveInfo['id']) == 0: # no vertex found self.digit.GetDisplay().SetSelected([]) # # check for duplicates # if UserSettings.Get( group='vdigit', key='checkForDupl', subkey='enabled'): dupl = self.digit.GetDisplay().GetDuplicates() self.UpdateMap(render=False) if dupl: posWindow = self.ClientToScreen( (self.mouse['end'][0] + self.dialogOffset, self.mouse['end'][1] + self.dialogOffset)) dlg = VDigitDuplicatesDialog( parent=self, data=dupl, pos=posWindow) if dlg.ShowModal() == wx.ID_OK: self.digit.GetDisplay().UnSelect(dlg.GetUnSelected()) # update selected self.UpdateMap(render=False) if action != "editLine": # -> move line || move vertex self.UpdateMap(render=False) else: # no vector object found if not (action in ("moveLine", "moveVertex") and hasattr(self, "moveInfo") and len(self.moveInfo['id']) > 0): # avoid left-click when features are already selected self.UpdateMap(render=False, renderVector=False) def OnLeftUpModifyLine(self, event): """Left mouse button released - vector digitizer split line, add/remove vertex action """ pos1 = self.Pixel2Cell(self.mouse['begin']) pointOnLine = self.digit.GetDisplay().SelectLineByPoint(pos1)['point'] if not pointOnLine: return if self.toolbar.GetAction() in ["splitLine", "addVertex"]: self.UpdateMap(render=False) # highlight object self.DrawCross( pdc=self.pdcTmp, coords=self.Cell2Pixel( (pointOnLine[0], pointOnLine[1])), size=5) else: # removeVertex # get only id of vertex try: id = self.digit.GetDisplay().GetSelectedVertex(pos1)[0] except IndexError: id = None if id: x, y = self.pdcVector.GetIdBounds(id)[0:2] self.pdcVector.RemoveId(id) self.UpdateMap(render=False) # highlight object self.DrawCross(pdc=self.pdcTmp, coords=(x, y), size=5) else: # unselect self.digit.GetDisplay().SetSelected([]) self.UpdateMap(render=False) def OnLeftUpCopyLine(self, event): """Left mouse button released - vector digitizer copy feature action """ pos1 = self.Pixel2Cell(self.mouse['begin']) pos2 = self.Pixel2Cell(self.mouse['end']) if UserSettings.Get(group='vdigit', key='bgmap', subkey='value', settings_type='internal') == '': # no background map -> copy from current vector map layer nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2)) if nselected > 0: # highlight selected features self.UpdateMap(render=False) else: self.UpdateMap(render=False, renderVector=False) else: # copy features from background map self.copyIds = self.digit.SelectLinesFromBackgroundMap( bbox=(pos1, pos2)) if len(self.copyIds) > 0: color = UserSettings.Get(group='vdigit', key='symbol', subkey=['highlight', 'color']) colorStr = str(color[0]) + ":" + str(color[1] ) + ":" + str(color[2]) dVectTmp = [ 'd.vect', 'map=%s' % UserSettings.Get( group='vdigit', key='bgmap', subkey='value', settings_type='internal'), 'cats=%s' % ListOfCatsToRange( self.copyIds), '-i', 'color=%s' % colorStr, 'fill_color=%s' % colorStr, 'type=point,line,boundary,centroid', 'width=2'] if not self.layerTmp: self.layerTmp = self.Map.AddLayer(ltype='vector', name=QUERYLAYER, command=dVectTmp) else: self.layerTmp.SetCmd(dVectTmp) else: if self.layerTmp: self.Map.DeleteLayer(self.layerTmp) self.layerTmp = None self.UpdateMap(render=True, renderVector=True) def OnLeftUpBulkLine(self, event): """Left mouse button released - vector digitizer z-bulk line action """ # select lines to be labeled pos1 = self.polycoords[0] pos2 = self.polycoords[1] nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2)) if nselected > 0: # highlight selected features self.UpdateMap(render=False) self.DrawLines(pdc=self.pdcTmp) # redraw temp line else: self.UpdateMap(render=False, renderVector=False) def OnLeftUpConnectLine(self, event): """Left mouse button released - vector digitizer connect line action """ if len(self.digit.GetDisplay().GetSelected()) > 0: self.UpdateMap(render=False) def _onLeftUp(self, event): """Left mouse button released""" if event.ControlDown(): return if hasattr(self, "moveInfo"): if len(self.digit.GetDisplay().GetSelected()) == 0: self.moveInfo['begin'] = self.Pixel2Cell( self.mouse['begin']) # left down # eliminate initial mouse moving efect self.mouse['begin'] = self.mouse['end'] action = self.toolbar.GetAction() if action in ("deleteLine", "deleteArea", "moveLine", "moveVertex", "copyCats", "copyAttrs", "editLine", "flipLine", "mergeLine", "snapLine", "queryLine", "breakLine", "typeConv", "connectLine"): self.OnLeftUpVarious(event) elif action in ("splitLine", "addVertex", "removeVertex"): self.OnLeftUpModifyLine(event) elif action == "copyLine": self.OnLeftUpCopyLine(event) elif action == "zbulkLine" and \ len(self.polycoords) == 2: self.OnLeftUpBulkLine(event) elif action == "connectLine": self.OnLeftUpConnectLine(event) if len(self.digit.GetDisplay().GetSelected()) > 0: self.redrawAll = None def _onRightDown(self, event): # digitization tool (confirm action) action = self.toolbar.GetAction() if action in ("moveLine", "moveVertex") and \ hasattr(self, "moveInfo"): pFrom = self.moveInfo['begin'] pTo = self.Pixel2Cell(event.GetPosition()) move = (pTo[0] - pFrom[0], pTo[1] - pFrom[1]) if action == "moveLine": # move line if self.digit.MoveSelectedLines(move) < 0: return elif action == "moveVertex": # move vertex fid = self.digit.MoveSelectedVertex(pFrom, move) if fid < 0: return self._geomAttrbUpdate([fid, ]) del self.moveInfo def _onRightUp(self, event): """Right mouse button released (confirm action) """ action = self.toolbar.GetAction() if action == "addLine" and \ self.toolbar.GetAction('type') in ["line", "boundary", "area"]: # -> add new line / boundary try: mapName = self.toolbar.GetLayer().GetName() except: mapName = None GError(parent=self, message=_("No vector map selected for editing.")) if mapName: if self.toolbar.GetAction('type') == 'line': line = True else: line = False if len(self.polycoords) < 2: # ignore 'one-point' lines return nfeat, fids = self.digit.AddFeature( self.toolbar.GetAction('type'), self.polycoords) if nfeat < 0: return position = self.Cell2Pixel(self.polycoords[-1]) self.polycoords = [] self.UpdateMap(render=False) self.redrawAll = True self.Refresh() # add new record into atribute table if self._addRecord() and (line is True or (not line and nfeat > 0)): posWindow = self.ClientToScreen( (position[0] + self.dialogOffset, position[1] + self.dialogOffset)) # select attributes based on layer and category cats = { fids[0]: { UserSettings.Get( group='vdigit', key="layer", subkey='value'): ( UserSettings.Get( group='vdigit', key="category", subkey='value'), )}} addRecordDlg = DisplayAttributesDialog( parent=self, map=mapName, cats=cats, pos=posWindow, action="add", ignoreError=True) for fid in fids: self._geomAttrb(fid, addRecordDlg, 'length') # auto-placing centroid self._geomAttrb(fid, addRecordDlg, 'area') self._geomAttrb(fid, addRecordDlg, 'perimeter') if addRecordDlg.IsFound(): addRecordDlg.ShowModal() addRecordDlg.Destroy() elif action == "deleteLine": # -> delete selected vector features if self.digit.DeleteSelectedLines() < 0: return self._updateATM() elif action == "deleteArea": # -> delete selected vector areas if self.digit.DeleteSelectedAreas() < 0: return self._updateATM() elif action == "splitLine": # split line if self.digit.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0: return elif action == "addVertex": # add vertex fid = self.digit.AddVertex(self.Pixel2Cell(self.mouse['begin'])) if fid < 0: return elif action == "removeVertex": # remove vertex fid = self.digit.RemoveVertex(self.Pixel2Cell(self.mouse['begin'])) if fid < 0: return self._geomAttrbUpdate([fid, ]) elif action in ("copyCats", "copyAttrs"): if action == 'copyCats': if self.digit.CopyCats(self.copyCatsList, self.copyCatsIds, copyAttrb=False) < 0: return else: if self.digit.CopyCats(self.copyCatsList, self.copyCatsIds, copyAttrb=True) < 0: return del self.copyCatsList del self.copyCatsIds self._updateATM() elif action == "editLine" and \ hasattr(self, "moveInfo"): line = self.digit.GetDisplay().GetSelected()[0] if self.digit.EditLine(line, self.polycoords) < 0: return del self.moveInfo elif action == "flipLine": if self.digit.FlipLine() < 0: return elif action == "mergeLine": if self.digit.MergeLine() < 0: return elif action == "breakLine": if self.digit.BreakLine() < 0: return elif action == "snapLine": if self.digit.SnapLine() < 0: return elif action == "connectLine": if len(self.digit.GetDisplay().GetSelected()) > 1: if self.digit.ConnectLine() < 0: return elif action == "copyLine": if self.digit.CopyLine(self.copyIds) < 0: return del self.copyIds if self.layerTmp: self.Map.DeleteLayer(self.layerTmp) self.UpdateMap(render=True, renderVector=False) del self.layerTmp elif action == "zbulkLine" and len(self.polycoords) == 2: pos1 = self.polycoords[0] pos2 = self.polycoords[1] selected = self.digit.GetDisplay().GetSelected() dlg = VDigitZBulkDialog( parent=self, title=_("Z bulk-labeling dialog"), nselected=len(selected)) if dlg.ShowModal() == wx.ID_OK: if self.digit.ZBulkLines(pos1, pos2, dlg.value.GetValue(), dlg.step.GetValue()) < 0: return self.UpdateMap(render=False) elif action == "typeConv": # -> feature type conversion # - point <-> centroid # - line <-> boundary if self.digit.TypeConvForSelectedLines() < 0: return if action != "addLine": # unselect and re-render self.digit.GetDisplay().SetSelected([]) self.polycoords = [] self.UpdateMap(render=False) def _onMouseMoving(self, event): self.mouse['end'] = event.GetPosition() Debug.msg(5, "VDigitWindow.OnMouseMoving(): coords=%f,%f" % (self.mouse['end'][0], self.mouse['end'][1])) action = self.toolbar.GetAction() if action == "addLine" and \ self.toolbar.GetAction('type') in ["line", "boundary", "area"]: if len(self.polycoords) > 0: self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1])) elif action in ["moveLine", "moveVertex", "editLine"] \ and hasattr(self, "moveInfo"): dx = self.mouse['end'][0] - self.mouse['begin'][0] dy = self.mouse['end'][1] - self.mouse['begin'][1] # draw lines on new position if action == "moveLine" and \ len(self.moveInfo['id']) > 0: # move line for id in self.moveInfo['id']: self.pdcTmp.TranslateId(id, dx, dy) elif action in ["moveVertex", "editLine"]: # move vertex -> # (vertex, left vertex, left line, # right vertex, right line) # do not draw static lines if action == "moveVertex" and \ len(self.moveInfo['id']) > 0: self.polycoords = [] self.pdcTmp.RemoveId(self.moveInfo['id'][0]) if self.moveInfo['id'][1] > 0: # previous vertex x, y = self.Pixel2Cell( self.pdcTmp.GetIdBounds( self.moveInfo['id'][1])[ 0:2]) self.pdcTmp.RemoveId(self.moveInfo['id'][1] + 1) self.polycoords.append((x, y)) self.polycoords.append(self.Pixel2Cell(self.mouse['end'])) if self.moveInfo['id'][2] > 0: # next vertex x, y = self.Pixel2Cell( self.pdcTmp.GetIdBounds( self.moveInfo['id'][2])[ 0:2]) self.pdcTmp.RemoveId(self.moveInfo['id'][2] - 1) self.polycoords.append((x, y)) self.ClearLines(pdc=self.pdcTmp) self.DrawLines(pdc=self.pdcTmp) if action == "editLine": self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1])) self.Refresh() # TODO: use RefreshRect() self.mouse['begin'] = self.mouse['end'] elif action == "zbulkLine": if len(self.polycoords) == 1: # draw mouse moving self.MouseDraw(self.pdcTmp) def _zoom(self, event): tmp1 = self.mouse['end'] tmp2 = self.Cell2Pixel(self.moveInfo['begin']) dx = tmp1[0] - tmp2[0] dy = tmp1[1] - tmp2[1] self.moveInfo['beginDiff'] = (dx, dy) for id in self.moveInfo['id']: self.pdcTmp.RemoveId(id) def _addRecord(self): return UserSettings.Get( group='vdigit', key="addRecord", subkey='enabled')
class VDigitToolbar(BaseToolbar): """Toolbar for digitization """ def __init__(self, parent, toolSwitcher, MapWindow, digitClass, giface, tools=[]): self.MapWindow = MapWindow self.Map = MapWindow.GetMap() # Map class instance self.tools = tools self.digitClass = digitClass BaseToolbar.__init__(self, parent, toolSwitcher) self.digit = None self._giface = giface self.fType = None # feature type for simple features editing self.editingStarted = Signal("VDigitToolbar.editingStarted") self.editingStopped = Signal("VDigitToolbar.editingStopped") self.editingBgMap = Signal("VDigitToolbar.editingBgMap") layerTree = self._giface.GetLayerTree() if layerTree: self.editingStarted.connect(layerTree.StartEditing) self.editingStopped.connect(layerTree.StopEditing) self.editingBgMap.connect(layerTree.SetBgMapForEditing) # currently selected map layer for editing (reference to MapLayer # instance) self.mapLayer = None # list of vector layers from Layer Manager (only in the current mapset) self.layers = [] self.comboid = self.combo = None self.undo = -1 self.redo = -1 # only one dialog can be open self.settingsDialog = None # create toolbars (two rows optionally) self.InitToolbar(self._toolbarData()) self._default = -1 # default action (digitize new point, line, etc.) self.action = {'desc': '', 'type': '', 'id': -1} self._currentAreaActionType = None # list of available vector maps self.UpdateListOfLayers(updateTool=True) for tool in ( 'addPoint', 'addLine', 'addBoundary', 'addCentroid', 'addArea', 'addVertex', 'deleteLine', 'deleteArea', 'displayAttr', 'displayCats', 'editLine', 'moveLine', 'moveVertex', 'removeVertex', 'additionalTools'): if hasattr(self, tool): tool = getattr(self, tool) self.toolSwitcher.AddToolToGroup( group='mouseUse', toolbar=self, tool=tool) else: Debug.msg(1, '%s skipped' % tool) # custom button for digitization of area/boundary/centroid # TODO: could this be somehow generalized? nAreaTools = 0 if self.tools and 'addBoundary' in self.tools: nAreaTools += 1 if self.tools and 'addCentroid' in self.tools: nAreaTools += 1 if self.tools and 'addArea' in self.tools: nAreaTools += 1 if nAreaTools != 1: self.areaButton = self.CreateSelectionButton( _("Select area/boundary/centroid tool")) self.areaButtonId = self.InsertControl(5, self.areaButton) self.areaButton.Bind(wx.EVT_BUTTON, self.OnAddAreaMenu) # realize toolbar self.Realize() # workaround for Mac bug. May be fixed by 2.8.8, but not before then. if self.combo: self.combo.Hide() self.combo.Show() # disable undo/redo if self.undo > 0: self.EnableTool(self.undo, False) if self.redo > 0: self.EnableTool(self.redo, False) self.FixSize(width=105) def _toolbarData(self): """Toolbar data """ data = [] self.icons = { 'addPoint': MetaIcon(img='point-create', label=_('Digitize new point'), desc=_('Left: new point')), 'addLine': MetaIcon(img='line-create', label=_('Digitize new line'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'addBoundary': MetaIcon(img='boundary-create', label=_('Digitize new boundary'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'addCentroid': MetaIcon(img='centroid-create', label=_('Digitize new centroid'), desc=_('Left: new point')), 'addArea': MetaIcon(img='polygon-create', label=_('Digitize new area (boundary without category)'), desc=_('Left: new point')), 'addVertex': MetaIcon(img='vertex-create', label=_('Add new vertex to line or boundary'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'deleteLine': MetaIcon(img='line-delete', label=_('Delete selected point(s), line(s), boundary(ies) or centroid(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'deleteArea': MetaIcon(img='polygon-delete', label=_('Delete selected area(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'displayAttr': MetaIcon(img='attributes-display', label=_('Display/update attributes'), desc=_('Left: Select')), 'displayCats': MetaIcon(img='cats-display', label=_('Display/update categories'), desc=_('Left: Select')), 'editLine': MetaIcon(img='line-edit', label=_('Edit selected line/boundary'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'moveLine': MetaIcon(img='line-move', label=_('Move selected point(s), line(s), boundary(ies) or centroid(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'moveVertex': MetaIcon(img='vertex-move', label=_('Move selected vertex'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'removeVertex': MetaIcon(img='vertex-delete', label=_('Remove selected vertex'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'settings': BaseIcons['settings'].SetLabel(_('Digitization settings')), 'quit': BaseIcons['quit'].SetLabel(label=_('Quit digitizer'), desc=_('Quit digitizer and save changes')), 'help': BaseIcons['help'].SetLabel(label=_('Vector Digitizer manual'), desc=_('Show Vector Digitizer manual')), 'additionalTools': MetaIcon(img='tools', label=_('Additional tools ' '(copy, flip, connect, etc.)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'undo': MetaIcon(img='undo', label=_('Undo'), desc=_('Undo previous changes')), 'redo': MetaIcon(img='redo', label=_('Redo'), desc=_('Redo previous changes')), } if not self.tools or 'selector' in self.tools: data.append((None, )) if not self.tools or 'addPoint' in self.tools: data.append(("addPoint", self.icons["addPoint"], self.OnAddPoint, wx.ITEM_CHECK)) if not self.tools or 'addLine' in self.tools: data.append(("addLine", self.icons["addLine"], self.OnAddLine, wx.ITEM_CHECK)) if not self.tools or 'addArea' in self.tools: data.append(("addArea", self.icons["addArea"], self.OnAddAreaTool, wx.ITEM_CHECK)) if not self.tools or 'deleteLine' in self.tools: data.append(("deleteLine", self.icons["deleteLine"], self.OnDeleteLine, wx.ITEM_CHECK)) if not self.tools or 'deleteArea' in self.tools: data.append(("deleteArea", self.icons["deleteArea"], self.OnDeleteArea, wx.ITEM_CHECK)) if not self.tools or 'moveVertex' in self.tools: data.append(("moveVertex", self.icons["moveVertex"], self.OnMoveVertex, wx.ITEM_CHECK)) if not self.tools or 'addVertex' in self.tools: data.append(("addVertex", self.icons["addVertex"], self.OnAddVertex, wx.ITEM_CHECK)) if not self.tools or 'removeVertex' in self.tools: data.append(("removeVertex", self.icons["removeVertex"], self.OnRemoveVertex, wx.ITEM_CHECK)) if not self.tools or 'editLine' in self.tools: data.append(("editLine", self.icons["editLine"], self.OnEditLine, wx.ITEM_CHECK)) if not self.tools or 'moveLine' in self.tools: data.append(("moveLine", self.icons["moveLine"], self.OnMoveLine, wx.ITEM_CHECK)) if not self.tools or 'displayCats' in self.tools: data.append(("displayCats", self.icons["displayCats"], self.OnDisplayCats, wx.ITEM_CHECK)) if not self.tools or 'displayAttr' in self.tools: data.append(("displayAttr", self.icons["displayAttr"], self.OnDisplayAttr, wx.ITEM_CHECK)) if not self.tools or 'additionalSelf.Tools' in self.tools: data.append(("additionalTools", self.icons["additionalTools"], self.OnAdditionalToolMenu, wx.ITEM_CHECK)) if not self.tools or 'undo' in self.tools or \ 'redo' in self.tools: data.append((None, )) if not self.tools or 'undo' in self.tools: data.append(("undo", self.icons["undo"], self.OnUndo)) if not self.tools or 'redo' in self.tools: data.append(("redo", self.icons["redo"], self.OnRedo)) if not self.tools or 'settings' in self.tools or \ 'help' in self.tools or \ 'quit' in self.tools: data.append((None, )) if not self.tools or 'settings' in self.tools: data.append(("settings", self.icons["settings"], self.OnSettings)) if not self.tools or 'help' in self.tools: data.append(("help", self.icons["help"], self.OnHelp)) if not self.tools or 'quit' in self.tools: data.append(("quit", self.icons["quit"], self.OnExit)) return self._getToolbarData(data) def OnTool(self, event): """Tool selected -> untoggles previusly selected tool in toolbar""" Debug.msg(3, "VDigitToolbar.OnTool(): id = %s" % event.GetId()) # set cursor self.MapWindow.SetNamedCursor('cross') self.MapWindow.mouse['box'] = 'point' self.MapWindow.mouse['use'] = 'pointer' aId = self.action.get('id', -1) BaseToolbar.OnTool(self, event) # clear tmp canvas if self.action['id'] != aId or aId == -1: self.MapWindow.polycoords = [] self.MapWindow.ClearLines(pdc=self.MapWindow.pdcTmp) if self.digit and \ len(self.MapWindow.digit.GetDisplay().GetSelected()) > 0: # cancel action self.MapWindow.OnMiddleDown(None) # set no action if self.action['id'] == -1: self.action = {'desc': '', 'type': '', 'id': -1} # set focus self.MapWindow.SetFocus() def OnAddPoint(self, event): """Add point to the vector map Laier""" Debug.msg(2, "VDigitToolbar.OnAddPoint()") self.action = {'desc': "addLine", 'type': "point", 'id': self.addPoint} self.MapWindow.mouse['box'] = 'point' def OnAddLine(self, event): """Add line to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddLine()") self.action = {'desc': "addLine", 'type': "line", 'id': self.addLine} self.MapWindow.mouse['box'] = 'line' # self.MapWindow.polycoords = [] # reset temp line def OnAddBoundary(self, event): """Add boundary to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddBoundary()") self._toggleAreaIfNeeded() # reset temp line if self.action['desc'] != 'addLine' or \ self.action['type'] != 'boundary': self.MapWindow.polycoords = [] # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons[ 'addBoundary'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addBoundary'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "boundary", 'id': self.addArea} self.MapWindow.mouse['box'] = 'line' self._currentAreaActionType = 'boundary' def OnAddCentroid(self, event): """Add centroid to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddCentroid()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons[ 'addCentroid'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addCentroid'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "centroid", 'id': self.addArea} self.MapWindow.mouse['box'] = 'point' self._currentAreaActionType = 'centroid' def OnAddArea(self, event): """Add area to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddArea()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap( self.addArea, self.icons['addArea'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addArea'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "area", 'id': self.addArea} self.MapWindow.mouse['box'] = 'line' self._currentAreaActionType = 'area' def _toggleAreaIfNeeded(self): """In some cases, the area tool is not toggled, we have to do it manually.""" if not self.GetToolState(self.addArea): self.ToggleTool(self.addArea, True) self.toolSwitcher.ToolChanged(self.addArea) def OnAddAreaTool(self, event): """Area tool activated.""" Debug.msg(2, "VDigitToolbar.OnAddAreaTool()") # we need the previous id if not self._currentAreaActionType or self._currentAreaActionType == 'area': # default action self.OnAddArea(event) elif self._currentAreaActionType == 'boundary': self.OnAddBoundary(event) elif self._currentAreaActionType == 'centroid': self.OnAddCentroid(event) def OnAddAreaMenu(self, event): """Digitize area menu (add area/boundary/centroid)""" menuItems = [] if not self.tools or 'addArea' in self.tools: menuItems.append((self.icons["addArea"], self.OnAddArea)) if not self.fType and not self.tools or 'addBoundary' in self.tools: menuItems.append((self.icons["addBoundary"], self.OnAddBoundary)) if not self.fType and not self.tools or 'addCentroid' in self.tools: menuItems.append((self.icons["addCentroid"], self.OnAddCentroid)) self._onMenu(menuItems) def OnExit(self, event=None): """Quit digitization tool""" # stop editing of the currently selected map layer if self.mapLayer: self.StopEditing() # close dialogs if still open if self.settingsDialog: self.settingsDialog.OnCancel(None) # set default mouse settings self.parent.GetMapToolbar().SelectDefault() self.MapWindow.polycoords = [] # TODO: replace this by binding wx event in parent (or use signals...) if not self.parent.IsStandalone(): # disable the toolbar self.parent.RemoveToolbar("vdigit") else: self.parent.Close() def OnMoveVertex(self, event): """Move line vertex""" Debug.msg(2, "Digittoolbar.OnMoveVertex():") self.action = {'desc': "moveVertex", 'id': self.moveVertex} self.MapWindow.mouse['box'] = 'point' def OnAddVertex(self, event): """Add line vertex""" Debug.msg(2, "Digittoolbar.OnAddVertex():") self.action = {'desc': "addVertex", 'id': self.addVertex} self.MapWindow.mouse['box'] = 'point' def OnRemoveVertex(self, event): """Remove line vertex""" Debug.msg(2, "Digittoolbar.OnRemoveVertex():") self.action = {'desc': "removeVertex", 'id': self.removeVertex} self.MapWindow.mouse['box'] = 'point' def OnEditLine(self, event): """Edit line""" Debug.msg(2, "Digittoolbar.OnEditLine():") self.action = {'desc': "editLine", 'id': self.editLine} self.MapWindow.mouse['box'] = 'line' def OnMoveLine(self, event): """Move line""" Debug.msg(2, "Digittoolbar.OnMoveLine():") self.action = {'desc': "moveLine", 'id': self.moveLine} self.MapWindow.mouse['box'] = 'box' def OnDeleteLine(self, event): """Delete line""" Debug.msg(2, "Digittoolbar.OnDeleteLine():") self.action = {'desc': "deleteLine", 'id': self.deleteLine} self.MapWindow.mouse['box'] = 'box' def OnDeleteArea(self, event): """Delete Area""" Debug.msg(2, "Digittoolbar.OnDeleteArea():") self.action = {'desc': "deleteArea", 'id': self.deleteArea} self.MapWindow.mouse['box'] = 'box' def OnDisplayCats(self, event): """Display/update categories""" Debug.msg(2, "Digittoolbar.OnDisplayCats():") self.action = {'desc': "displayCats", 'id': self.displayCats} self.MapWindow.mouse['box'] = 'point' def OnDisplayAttr(self, event): """Display/update attributes""" Debug.msg(2, "Digittoolbar.OnDisplayAttr():") self.action = {'desc': "displayAttrs", 'id': self.displayAttr} self.MapWindow.mouse['box'] = 'point' def OnUndo(self, event): """Undo previous changes""" self.digit.Undo() event.Skip() def OnRedo(self, event): """Undo previous changes""" self.digit.Undo(level=1) event.Skip() def EnableUndo(self, enable=True): """Enable 'Undo' in toolbar :param enable: False for disable """ self._enableTool(self.undo, enable) def EnableRedo(self, enable=True): """Enable 'Redo' in toolbar :param enable: False for disable """ self._enableTool(self.redo, enable) def _enableTool(self, tool, enable): if not self.FindById(tool): return if enable: if self.GetToolEnabled(tool) is False: self.EnableTool(tool, True) else: if self.GetToolEnabled(tool) is True: self.EnableTool(tool, False) def GetAction(self, type='desc'): """Get current action info""" return self.action.get(type, '') def OnSettings(self, event): """Show settings dialog""" if self.digit is None: try: self.digit = self.MapWindow.digit = self.digitClass( mapwindow=self.MapWindow) except SystemExit: self.digit = self.MapWindow.digit = None if not self.settingsDialog: self.settingsDialog = VDigitSettingsDialog( parent=self.parent, giface=self._giface) self.settingsDialog.Show() def OnHelp(self, event): """Show digitizer help page in web browser""" self._giface.Help('wxGUI.vdigit') def OnAdditionalToolMenu(self, event): """Menu for additional tools""" point = wx.GetMousePosition() toolMenu = wx.Menu() for label, itype, handler, desc in ( (_('Break selected lines/boundaries at intersection'), wx.ITEM_CHECK, self.OnBreak, "breakLine"), (_('Connect selected lines/boundaries'), wx.ITEM_CHECK, self.OnConnect, "connectLine"), (_('Copy categories'), wx.ITEM_CHECK, self.OnCopyCats, "copyCats"), (_('Copy features from (background) vector map'), wx.ITEM_CHECK, self.OnCopy, "copyLine"), (_('Copy attributes'), wx.ITEM_CHECK, self.OnCopyAttrb, "copyAttrs"), (_('Feature type conversion'), wx.ITEM_CHECK, self.OnTypeConversion, "typeConv"), (_('Flip selected lines/boundaries'), wx.ITEM_CHECK, self.OnFlip, "flipLine"), (_('Merge selected lines/boundaries'), wx.ITEM_CHECK, self.OnMerge, "mergeLine"), (_('Snap selected lines/boundaries (only to nodes)'), wx.ITEM_CHECK, self.OnSnap, "snapLine"), (_('Split line/boundary'), wx.ITEM_CHECK, self.OnSplitLine, "splitLine"), (_('Query features'), wx.ITEM_CHECK, self.OnQuery, "queryLine"), (_('Z bulk-labeling of 3D lines'), wx.ITEM_CHECK, self.OnZBulk, "zbulkLine")): # Add items to the menu item = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY, text=label, kind=itype) toolMenu.AppendItem(item) self.MapWindow.Bind(wx.EVT_MENU, handler, item) if self.action['desc'] == desc: item.Check(True) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.MapWindow.PopupMenu(toolMenu) toolMenu.Destroy() if self.action['desc'] == 'addPoint': self.ToggleTool(self.additionalTools, False) def OnCopy(self, event): """Copy selected features from (background) vector map""" if not self.digit: GError(_("No vector map open for editing."), self.parent) return # select background map dlg = VectorDialog(self.parent, title=_("Select background vector map"), layerTree=self._giface.GetLayerTree()) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return mapName = dlg.GetName(full=True) dlg.Destroy() # close open background map if any bgMap = UserSettings.Get(group='vdigit', key='bgmap', subkey='value', settings_type='internal') if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) # open background map for reading UserSettings.Set(group='vdigit', key='bgmap', subkey='value', value=str(mapName), settings_type='internal') self.digit.OpenBackgroundMap(mapName) self.editingBgMap.emit(mapName=mapName) if self.action['desc'] == 'copyLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopy():") self.action = {'desc': "copyLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSplitLine(self, event): """Split line""" if self.action['desc'] == 'splitLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSplitLine():") self.action = {'desc': "splitLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnCopyCats(self, event): """Copy categories""" if self.action['desc'] == 'copyCats': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyCats():") self.action = {'desc': "copyCats", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnCopyAttrb(self, event): """Copy attributes""" if self.action['desc'] == 'copyAttrs': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyAttrb():") self.action = {'desc': "copyAttrs", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnFlip(self, event): """Flip selected lines/boundaries""" if self.action['desc'] == 'flipLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnFlip():") self.action = {'desc': "flipLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnMerge(self, event): """Merge selected lines/boundaries""" if self.action['desc'] == 'mergeLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnMerge():") self.action = {'desc': "mergeLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnBreak(self, event): """Break selected lines/boundaries""" if self.action['desc'] == 'breakLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnBreak():") self.action = {'desc': "breakLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSnap(self, event): """Snap selected features""" if self.action['desc'] == 'snapLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSnap():") self.action = {'desc': "snapLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnConnect(self, event): """Connect selected lines/boundaries""" if self.action['desc'] == 'connectLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnConnect():") self.action = {'desc': "connectLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnQuery(self, event): """Query selected lines/boundaries""" if self.action['desc'] == 'queryLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg( 2, "Digittoolbar.OnQuery(): %s" % UserSettings.Get( group='vdigit', key='query', subkey='selection')) self.action = {'desc': "queryLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnZBulk(self, event): """Z bulk-labeling selected lines/boundaries""" if not self.digit.IsVector3D(): GError(parent=self.parent, message=_("Vector map is not 3D. Operation canceled.")) return if self.action['desc'] == 'zbulkLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnZBulk():") self.action = {'desc': "zbulkLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'line' def OnTypeConversion(self, event): """Feature type conversion Supported conversions: - point <-> centroid - line <-> boundary """ if self.action['desc'] == 'typeConv': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnTypeConversion():") self.action = {'desc': "typeConv", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSelectMap(self, event): """Select vector map layer for editing If there is a vector map layer already edited, this action is firstly terminated. The map layer is closed. After this the selected map layer activated for editing. """ if event.GetSelection() == 0: # create new vector map layer if self.mapLayer: openVectorMap = self.mapLayer.GetName( fullyQualified=False)['name'] else: openVectorMap = None dlg = CreateNewVector(self.parent, exceptMap=openVectorMap, giface=self._giface, cmd=(('v.edit', {'tool': 'create'}, 'map')), disableAdd=True) if dlg and dlg.GetName(): # add layer to map layer tree/map display mapName = dlg.GetName() + '@' + grass.gisenv()['MAPSET'] self._giface.GetLayerList().AddLayer( ltype='vector', name=mapName, checked=True, cmd=['d.vect', 'map=%s' % mapName]) vectLayers = self.UpdateListOfLayers(updateTool=True) selection = vectLayers.index(mapName) # create table ? if dlg.IsChecked('table'): # TODO: replace this by signal # also note that starting of tools such as atm, iclass, # plots etc. should be handled in some better way # than starting randomly from mapdisp and lmgr lmgr = self.parent.GetLayerManager() if lmgr: lmgr.OnShowAttributeTable(None, selection='table') dlg.Destroy() else: self.combo.SetValue(_('Select vector map')) if dlg: dlg.Destroy() return else: selection = event.GetSelection() - 1 # first option is 'New vector map' # skip currently selected map if self.layers[selection] == self.mapLayer: return if self.mapLayer: # deactive map layer for editing self.StopEditing() # select the given map layer for editing self.StartEditing(self.layers[selection]) event.Skip() def StartEditing(self, mapLayer): """Start editing selected vector map layer. :param mapLayer: MapLayer to be edited """ # check if topology is available (skip for hidden - temporary # maps, see iclass for details) if not mapLayer.IsHidden() and grass.vector_info( mapLayer.GetName())['level'] != 2: dlg = wx.MessageDialog( parent=self.MapWindow, message=_( "Topology for vector map <%s> is not available. " "Topology is required by digitizer.\nDo you want to " "rebuild topology (takes some time) and open the vector map " "for editing?") % mapLayer.GetName(), caption=_("Digitizer error"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE) if dlg.ShowModal() == wx.ID_YES: RunCommand('v.build', map=mapLayer.GetName()) else: return # deactive layer self.Map.ChangeLayerActive(mapLayer, False) # clean map canvas self.MapWindow.EraseMap() # unset background map if needed if mapLayer: if UserSettings.Get( group='vdigit', key='bgmap', subkey='value', settings_type='internal') == mapLayer.GetName(): UserSettings.Set( group='vdigit', key='bgmap', subkey='value', value='', settings_type='internal') self.parent.SetStatusText(_("Please wait, " "opening vector map <%s> for editing...") % mapLayer.GetName(), 0) self.MapWindow.pdcVector = wx.PseudoDC() self.digit = self.MapWindow.digit = self.digitClass( mapwindow=self.MapWindow) self.mapLayer = mapLayer # open vector map (assume that 'hidden' map layer is temporary vector # map) if self.digit.OpenMap( mapLayer.GetName(), tmp=mapLayer.IsHidden()) is None: self.mapLayer = None self.StopEditing() return False # check feature type (only for OGR layers) self.fType = self.digit.GetFeatureType() self.EnableAll() self.EnableUndo(False) self.EnableRedo(False) if self.fType == 'point': for tool in (self.addLine, self.addArea, self.moveVertex, self.addVertex, self.removeVertex, self.editLine): self.EnableTool(tool, False) elif self.fType == 'linestring': for tool in (self.addPoint, self.addArea): self.EnableTool(tool, False) elif self.fType == 'polygon': for tool in (self.addPoint, self.addLine): self.EnableTool(tool, False) elif self.fType: GError( parent=self, message=_( "Unsupported feature type '%(type)s'. Unable to edit " "OGR layer <%(layer)s>.") % {'type': self.fType, 'layer': mapLayer.GetName()}) self.digit.CloseMap() self.mapLayer = None self.StopEditing() return False # update toolbar if self.combo: self.combo.SetValue(mapLayer.GetName()) if 'map' in self.parent.toolbars: self.parent.toolbars['map'].combo.SetValue(_('Vector digitizer')) # here was dead code to enable vdigit button in toolbar # with if to ignore iclass # some signal (DigitizerStarted) can be emitted here Debug.msg( 4, "VDigitToolbar.StartEditing(): layer=%s" % mapLayer.GetName()) # change cursor if self.MapWindow.mouse['use'] == 'pointer': self.MapWindow.SetNamedCursor('cross') if not self.MapWindow.resize: self.MapWindow.UpdateMap(render=True) # respect opacity opacity = mapLayer.GetOpacity() if opacity < 1.0: alpha = int(opacity * 255) self.digit.GetDisplay().UpdateSettings(alpha=alpha) # emit signal layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData('maplayer', self.mapLayer) else: item = None self.editingStarted.emit( vectMap=mapLayer.GetName(), digit=self.digit, layerItem=item) return True def StopEditing(self): """Stop editing of selected vector map layer. :return: True on success :return: False on failure """ item = None if self.combo: self.combo.SetValue(_('Select vector map')) # save changes if self.mapLayer: Debug.msg( 4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName()) if UserSettings.Get(group='vdigit', key='saveOnExit', subkey='enabled') is False: if self.digit.GetUndoLevel() > -1: dlg = wx.MessageDialog( parent=self.parent, message=_( "Do you want to save changes " "in vector map <%s>?") % self.mapLayer.GetName(), caption=_("Save changes?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_NO: # revert changes self.digit.Undo(0) dlg.Destroy() self.parent.SetStatusText(_("Please wait, " "closing and rebuilding topology of " "vector map <%s>...") % self.mapLayer.GetName(), 0) self.digit.CloseMap() # close open background map if any bgMap = UserSettings.Get( group='vdigit', key='bgmap', subkey='value', settings_type='internal') if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) self._giface.GetProgress().SetValue(0) self._giface.WriteCmdLog( _("Editing of vector map <%s> successfully finished") % self.mapLayer.GetName(), notification=Notification.HIGHLIGHT) # re-active layer layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData('maplayer', self.mapLayer) if item and layerTree.IsItemChecked(item): self.Map.ChangeLayerActive(self.mapLayer, True) # change cursor self.MapWindow.SetNamedCursor('default') self.MapWindow.pdcVector = None # close dialogs for dialog in ('attributes', 'category'): if self.parent.dialogs[dialog]: self.parent.dialogs[dialog].Close() self.parent.dialogs[dialog] = None self.digit = None self.MapWindow.digit = None self.editingStopped.emit(layerItem=item) self.mapLayer = None self.MapWindow.redrawAll = True return True def UpdateListOfLayers(self, updateTool=False): """Update list of available vector map layers. This list consists only editable layers (in the current mapset) :param updateTool: True to update also toolbar :type updateTool: bool """ Debug.msg(4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % updateTool) layerNameSelected = None # name of currently selected layer if self.mapLayer: layerNameSelected = self.mapLayer.GetName() # select vector map layer in the current mapset layerNameList = [] self.layers = self.Map.GetListOfLayers(ltype="vector", mapset=grass.gisenv()['MAPSET']) for layer in self.layers: if not layer.name in layerNameList: # do not duplicate layer layerNameList.append(layer.GetName()) if updateTool: # update toolbar if not self.mapLayer: value = _('Select vector map') else: value = layerNameSelected if not self.comboid: if not self.tools or 'selector' in self.tools: self.combo = wx.ComboBox( self, id=wx.ID_ANY, value=value, choices=[_('New vector map'), ] + layerNameList, size=(80, -1), style=wx.CB_READONLY) self.comboid = self.InsertControl(0, self.combo) self.parent.Bind( wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid) else: self.combo.SetItems([_('New vector map'), ] + layerNameList) self.Realize() return layerNameList def GetLayer(self): """Get selected layer for editing -- MapLayer instance""" return self.mapLayer
class Statistics: """Statistis connected to one class (category). It is Python counterpart of similar C structure. But it adds some attributes or features used in wxIClass. It is not interface to C structure (it copies values). """ def __init__(self): self.category = -1 self.name = "" self.rasterName = "" self.color = "0:0:0" self.nbands = 0 self.ncells = 0 self.nstd = 1.5 self.bands = [] self.ready = False self.statisticsSet = Signal("Statistics.statisticsSet") def SetReady(self, ready=True): self.ready = ready def IsReady(self): return self.ready def SetBaseStatistics(self, cat, name, color): """Sets basic (non-statistical) values. .. todo:: Later self.name is changed but self.rasterName is not. self.rasterName should not be set by user. It can remains the same. But it should be done more explicitly. Currently it looks like unintentional feature or bug. """ self.category = cat self.name = name self.color = color rasterPath = grass.tempfile(create=False) name = name.replace(' ', '_') self.rasterName = name + '_' + os.path.basename(rasterPath) def SetFromcStatistics(self, cStatistics): """Sets all statistical values. Copies all statistic values from \a cStattistics. :param cStatistics: pointer to C statistics structure """ cat = c_int() set_stats = {} I_iclass_statistics_get_cat(cStatistics, byref(cat)) if self.category != cat.value: set_stats["category"] = cat.value name = c_char_p() I_iclass_statistics_get_name(cStatistics, byref(name)) if self.name != name.value: set_stats["name"] = grass.decode(name.value) color = c_char_p() I_iclass_statistics_get_color(cStatistics, byref(color)) if self.color != color.value: set_stats["color"] = grass.decode(color.value) nbands = c_int() I_iclass_statistics_get_nbands(cStatistics, byref(nbands)) if self.nbands != nbands.value: set_stats["nbands"] = nbands.value ncells = c_int() I_iclass_statistics_get_ncells(cStatistics, byref(ncells)) if self.ncells != ncells.value: set_stats["ncells"] = ncells.value nstd = c_float() I_iclass_statistics_get_nstd(cStatistics, byref(nstd)) if self.nstd != nstd.value: set_stats["nstd"] = nstd.value self.SetStatistics(set_stats) self.SetBandStatistics(cStatistics) def SetBandStatistics(self, cStatistics): """Sets all band statistics. :param cStatistics: pointer to C statistics structure """ self.bands = [] for i in range(self.nbands): band = BandStatistics() band.SetFromcStatistics(cStatistics, index=i) self.bands.append(band) def SetStatistics(self, stats): for st, val in six.iteritems(stats): setattr(self, st, val) self.statisticsSet.emit(stats=stats)
class WSPanel(wx.Panel): def __init__(self, parent, web_service, **kwargs): """Show data from capabilities file. Signal: capParsed - this signal is emitted when capabilities file is downloaded (after ConnectToServer method was called) :param parent: parent widget :param web_service: web service to be panel generated for """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.ws = web_service self.capParsed = Signal("WSPanel.capParsed") # stores widgets, which represents parameters/flags of d.wms self.params = {} self.flags = {} self.o_layer_name = "" # stores err output from r.in.wms during getting capabilities self.cmd_err_str = "" # stores selected layer from layer list self.sel_layers = [] # downloaded and parsed data from server successfully? self.is_connected = False # common part of command for r.in.wms -c and d.wms self.ws_cmdl = None # provides information about driver parameters self.drv_info = WMSDriversInfo() self.drv_props = self.drv_info.GetDrvProperties(self.ws) self.ws_drvs = { "WMS_1.1.1": { "cmd": ["wms_version=1.1.1", "driver=WMS_GRASS"], "cap_parser": lambda temp_file: WMSCapabilities(temp_file, "1.1.1"), }, "WMS_1.3.0": { "cmd": ["wms_version=1.3.0", "driver=WMS_GRASS"], "cap_parser": lambda temp_file: WMSCapabilities(temp_file, "1.3.0"), }, "WMTS": { "cmd": ["driver=WMTS_GRASS"], "cap_parser": WMTSCapabilities, }, "OnEarth": { "cmd": ["driver=OnEarth_GRASS"], "cap_parser": OnEarthCapabilities, }, } self.cmdStdErr = GStderr(self) self.cmd_thread = CmdThread(self) self.cap_file = grass.tempfile() reqDataBox = StaticBox(parent=self, label=_(" Requested data settings ")) self._nb_sizer = wx.StaticBoxSizer(reqDataBox, wx.VERTICAL) self.notebook = GNotebook( parent=self, style=FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON | FN.FNB_NODRAG ) self._requestPage() self._advancedSettsPage() self._layout() self.layerSelected = self.list.layerSelected self.Bind(EVT_CMD_DONE, self.OnCapDownloadDone) self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) self.SetMinSize((-1, 300)) def __del__(self): self.cmd_thread.abort(abortall=True) grass.try_remove(self.cap_file) def _layout(self): self._nb_sizer.Add(self.notebook, proportion=1, flag=wx.EXPAND) self.SetSizer(self._nb_sizer) def _requestPage(self): """Create request page""" self.req_page_panel = wx.Panel(parent=self, id=wx.ID_ANY) self.notebook.AddPage( page=self.req_page_panel, text=_("Request"), name="request" ) # list of layers self.layersBox = StaticBox( parent=self.req_page_panel, id=wx.ID_ANY, label=_("List of layers ") ) style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT if self.drv_props["req_multiple_layers"]: style = style | wx.TR_MULTIPLE if "WMS" not in self.ws: style = style | wx.TR_HIDE_ROOT self.list = LayersList( parent=self.req_page_panel, web_service=self.ws, style=style ) self.params["format"] = None self.params["srs"] = None if "srs" not in self.drv_props["ignored_params"]: projText = StaticText( parent=self.req_page_panel, id=wx.ID_ANY, label=_("Source projection:") ) self.params["srs"] = wx.Choice(parent=self.req_page_panel, id=wx.ID_ANY) self.list.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnListSelChanged) # layout self.req_page_sizer = wx.BoxSizer(wx.VERTICAL) layersSizer = wx.StaticBoxSizer(self.layersBox, wx.HORIZONTAL) layersSizer.Add( self.list, proportion=1, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5, ) self.req_page_sizer.Add( layersSizer, proportion=1, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5, ) self.source_sizer = wx.BoxSizer(wx.HORIZONTAL) if self.params["format"] is not None: self.source_sizer.Add( self.params["format"], flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5 ) if self.params["srs"] is not None: self.source_sizer.Add( projText, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5 ) self.source_sizer.Add( self.params["srs"], flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP | wx.BOTTOM, border=5, ) self.req_page_sizer.Add( self.source_sizer, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5 ) self.req_page_panel.SetSizer(self.req_page_sizer) def enableButtons(self, enable=True): """Enable/disable up, down, buttons""" self.btnUp.Enable(enable) self.btnDown.Enable(enable) def _advancedSettsPage(self): """Create advanced settings page""" # TODO parse maxcol, maxrow, settings from d.wms module? # TODO OnEarth driver - add selection of time adv_setts_panel = ScrolledPanel( parent=self, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER ) self.notebook.AddPage( page=adv_setts_panel, text=_("Advanced request settings"), name="adv_req_setts", ) labels = {} self.l_odrder_list = None if "WMS" in self.ws: labels["l_order"] = StaticBox( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Order of layers in raster"), ) self.l_odrder_list = wx.ListBox( adv_setts_panel, id=wx.ID_ANY, choices=[], style=wx.LB_SINGLE | wx.LB_NEEDED_SB, ) self.btnUp = Button(adv_setts_panel, id=wx.ID_ANY, label=_("Up")) self.btnDown = Button(adv_setts_panel, id=wx.ID_ANY, label=_("Down")) self.btnUp.Bind(wx.EVT_BUTTON, self.OnUp) self.btnDown.Bind(wx.EVT_BUTTON, self.OnDown) labels["method"] = StaticText( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Reprojection method:") ) self.reproj_methods = ["nearest", "linear", "cubic", "cubicspline"] self.params["method"] = wx.Choice( parent=adv_setts_panel, id=wx.ID_ANY, choices=[ _("Nearest neighbor"), _("Linear interpolation"), _("Cubic interpolation"), _("Cubic spline interpolation"), ], ) labels["maxcols"] = StaticText( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Maximum columns to request from server at time:"), ) self.params["maxcols"] = SpinCtrl( parent=adv_setts_panel, id=wx.ID_ANY, size=(100, -1) ) labels["maxrows"] = StaticText( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Maximum rows to request from server at time:"), ) self.params["maxrows"] = SpinCtrl( parent=adv_setts_panel, id=wx.ID_ANY, size=(100, -1) ) min = 100 max = 10000 self.params["maxcols"].SetRange(min, max) self.params["maxrows"].SetRange(min, max) val = 500 self.params["maxcols"].SetValue(val) self.params["maxrows"].SetValue(val) self.flags["o"] = self.params["bgcolor"] = None if "o" not in self.drv_props["ignored_flags"]: self.flags["o"] = wx.CheckBox( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Do not request transparent data"), ) self.flags["o"].Bind(wx.EVT_CHECKBOX, self.OnTransparent) labels["bgcolor"] = StaticText( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Background color:") ) self.params["bgcolor"] = csel.ColourSelect( parent=adv_setts_panel, id=wx.ID_ANY, colour=(255, 255, 255), size=globalvar.DIALOG_COLOR_SIZE, ) self.params["bgcolor"].Enable(False) self.params["urlparams"] = None if self.params["urlparams"] not in self.drv_props["ignored_params"]: labels["urlparams"] = StaticText( parent=adv_setts_panel, id=wx.ID_ANY, label=_("Additional query parameters for server:"), ) self.params["urlparams"] = TextCtrl(parent=adv_setts_panel, id=wx.ID_ANY) # layout border = wx.BoxSizer(wx.VERTICAL) if "WMS" in self.ws: boxSizer = wx.StaticBoxSizer(labels["l_order"], wx.VERTICAL) gridSizer = wx.GridBagSizer(hgap=3, vgap=3) gridSizer.Add( self.l_odrder_list, pos=(0, 0), span=(4, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0, ) gridSizer.Add( self.btnUp, pos=(0, 1), flag=wx.ALIGN_CENTER_VERTICAL, border=0 ) gridSizer.Add( self.btnDown, pos=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL, border=0 ) gridSizer.AddGrowableCol(0) boxSizer.Add(gridSizer, flag=wx.EXPAND | wx.ALL, border=5) border.Add(boxSizer, flag=wx.LEFT | wx.RIGHT | wx.UP | wx.EXPAND, border=5) gridSizer = wx.GridBagSizer(hgap=3, vgap=3) row = 0 for k in ["method", "maxcols", "maxrows", "o", "bgcolor"]: if k in self.params: param = self.params[k] elif k in self.flags: param = self.flags[k] if param is None: continue if k in labels or k == "o": if k != "o": label = labels[k] else: label = param gridSizer.Add( label, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0) ) if k != "o": gridSizer.Add( param, flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 1) ) row += 1 gridSizer.AddGrowableCol(0) border.Add(gridSizer, flag=wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, border=5) if self.params["urlparams"]: gridSizer = wx.GridBagSizer(hgap=3, vgap=3) row = 0 gridSizer.Add( labels["urlparams"], flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0), ) gridSizer.Add( self.params["urlparams"], flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos=(row, 1), ) gridSizer.AddGrowableCol(1) border.Add( gridSizer, flag=wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, border=5 ) adv_setts_panel.SetSizer(border) adv_setts_panel.SetAutoLayout(True) adv_setts_panel.SetupScrolling() def OnUp(self, event): """Move selected layer up""" if self.l_odrder_list.GetSelections(): pos = self.l_odrder_list.GetSelection() if pos: self.sel_layers.insert(pos - 1, self.sel_layers.pop(pos)) if pos > 0: self._updateLayerOrderList(selected=(pos - 1)) else: self._updateLayerOrderList(selected=0) def OnDown(self, event): """Move selected to down""" if self.l_odrder_list.GetSelections(): pos = self.l_odrder_list.GetSelection() if pos != len(self.sel_layers) - 1: self.sel_layers.insert(pos + 1, self.sel_layers.pop(pos)) if pos < len(self.sel_layers) - 1: self._updateLayerOrderList(selected=(pos + 1)) else: self._updateLayerOrderList(selected=len(self.sel_layers) - 1) def _updateLayerOrderList(self, selected=None): """Update order in list.""" def getlayercaption(l): if l["title"]: cap = l["title"] else: cap = l["name"] if l["style"]: if l["style"]["title"]: cap += " / " + l["style"]["title"] else: cap += " / " + l["style"]["name"] return cap layer_capts = [getlayercaption(l) for l in self.sel_layers] self.l_odrder_list.Set(layer_capts) if self.l_odrder_list.IsEmpty(): self.enableButtons(False) else: self.enableButtons(True) if selected is not None: self.l_odrder_list.SetSelection(selected) self.l_odrder_list.EnsureVisible(selected) def OnTransparent(self, event): checked = event.IsChecked() if checked: self.params["bgcolor"].Enable(True) else: self.params["bgcolor"].Enable(False) def ConnectToServer(self, url, username, password): """Download and parse data from capabilities file. :param url: server url :type url: str :param username: username for connection :type username: str :param password: password for connection :type password: str """ self._prepareForNewConn(url, username, password) cap_cmd = [ "r.in.wms", "-c", ("capfile_output=%s" % self.cap_file), "--overwrite", ] + self.ws_cmdl self.currentPid = self.cmd_thread.GetId() self.cmd_thread.RunCmd(cap_cmd, stderr=self.cmdStdErr) def OnCmdOutput(self, event): """Manage cmd output.""" if Debug.GetLevel() != 0: Debug.msg(1, event.text) elif event.type != "message" and event.type != "warning": self.cmd_err_str += event.text + os.linesep def _prepareForNewConn(self, url, username, password): """Prepare panel for new connection""" self.is_connected = False self.sel_layers = [] self.formats_list = [] self.projs_list = [] self.conn = {"url": url, "password": password, "username": username} conn_cmd = [] for k, v in six.iteritems(self.conn): if v: conn_cmd.append("%s=%s" % (k, v)) self.ws_cmdl = self.ws_drvs[self.ws]["cmd"] + conn_cmd def OnCapDownloadDone(self, event): """Process downloaded capabilities file and emits capParsed signal (see class constructor). """ if event.pid != self.currentPid: return if event.returncode != 0: if self.cmd_err_str: self.cmd_err_str = ( _( "Unable to download %s capabilities file\nfrom <%s>:\n" % (self.ws.replace("_", " "), self.conn["url"]) ) + self.cmd_err_str ) self._postCapParsedEvt(error_msg=self.cmd_err_str) self.cmd_err_str = "" return self._parseCapFile(self.cap_file) def _parseCapFile(self, cap_file): """Parse capabilities data and emits capParsed signal (see class constructor). """ try: self.cap = self.ws_drvs[self.ws]["cap_parser"](cap_file) except (IOError, ParseError) as error: error_msg = _( "%s web service was not found in fetched capabilities file from <%s>:\n%s\n" % (self.ws, self.conn["url"], str(error)) ) if Debug.GetLevel() != 0: Debug.msg(1, error_msg) self._postCapParsedEvt(None) else: self._postCapParsedEvt(error_msg=error_msg) return self.is_connected = True # WMS standard has formats defined for all layers if "WMS" in self.ws: self.formats_list = sorted(self._getFormats()) self._updateFormatRadioBox(self.formats_list) self._setDefaultFormatVal() self.list.LoadData(self.cap) self.OnListSelChanged(event=None) self._postCapParsedEvt(None) def ParseCapFile( self, url, username, password, cap_file=None, ): """Parse capabilities data and emits capParsed signal (see class constructor). """ self._prepareForNewConn(url, username, password) if cap_file is None or not url: self._postCapParsedEvt(None) return shutil.copyfile(cap_file, self.cap_file) self._parseCapFile(self.cap_file) def UpdateWidgetsByCmd(self, cmd): """Update panel widgets accordnig to passed cmd tuple :param cmd: cmd in tuple """ dcmd = cmd[1] layers = [] if "layers" in dcmd: layers = dcmd["layers"] styles = [] if "styles" in dcmd: styles = dcmd["styles"] if "WMS" in self.ws: layers = layers.split(",") styles = styles.split(",") else: layers = [layers] styles = [styles] if len(layers) != len(styles): styles = [""] * len(layers) l_st_list = [] for i in range(len(layers)): l_st_list.append({"style": styles[i], "layer": layers[i]}) # WMS standard - first layer in params is most bottom... # therefore layers order need to be reversed l_st_list = [l for l in reversed(l_st_list)] self.list.SelectLayers(l_st_list) params = {} if "format" in dcmd: params["format"] = dcmd["format"] if "srs" in dcmd: params["srs"] = "EPSG:" + dcmd["srs"] if "method" in dcmd: params["method"] = dcmd["method"] for p, v in six.iteritems(params): if self.params[p]: self.params[p].SetStringSelection(v) for p, conv_f in [("urlparams", None), ("maxcols", int), ("maxrows", int)]: if p in dcmd: v = dcmd[p] if conv_f: v = conv_f(v) self.params[p].SetValue(v) if "flags" in dcmd and "o" in dcmd["flags"]: self.flags["o"].SetValue(1) self.params["bgcolor"].Enable(True) if "bgcolor" in dcmd and self.params["bgcolor"]: bgcolor = dcmd["bgcolor"].strip().lower() if len(bgcolor) == 8 and "0x" == bgcolor[:2]: colour = "#" + bgcolor[2:] self.params["bgcolor"].SetColour(colour) def IsConnected(self): """Was successful in downloading and parsing capabilities data?""" return self.is_connected def _postCapParsedEvt(self, error_msg): """Helper function""" self.capParsed.emit(error_msg=error_msg) def CreateCmd(self): """Create d.wms cmd from values of panels widgets :return: cmd list :return: None if required widgets do not have selected/filled values. """ # check required widgets if not self._checkImportValues(): return None # create d.wms command lcmd = self.ws_cmdl lcmd = ["d.wms"] + lcmd layers = "layers=" styles = "styles=" first = True # WMS standard - first layer in params is most bottom... # therefore layers order need to be reversed for layer in reversed(self.sel_layers): if not first: layers += "," styles += "," first = False layers += layer["name"] if layer["style"] is not None: styles += layer["style"]["name"] lcmd.append(layers) lcmd.append(styles) if "format" not in self.drv_props["ignored_params"]: i_format = self.params["format"].GetSelection() lcmd.append("format=%s" % self.formats_list[i_format]) if "srs" not in self.drv_props["ignored_params"]: i_srs = self.params["srs"].GetSelection() srs = self.projs_list[i_srs].split(":")[-1] epsg_num = int("".join(re.findall(r"\d+", srs))) lcmd.append("srs=%s" % epsg_num) for k in ["maxcols", "maxrows", "urlparams"]: lcmd.append(k + "=" + str(self.params[k].GetValue())) i_method = self.params["method"].GetSelection() lcmd.append("method=" + self.reproj_methods[i_method]) if "o" not in self.drv_props["ignored_flags"] and self.flags["o"].IsChecked(): lcmd.append("-o") c = self.params["bgcolor"].GetColour() hex_color = wx.Colour(c[0], c[1], c[2]).GetAsString(wx.C2S_HTML_SYNTAX) lcmd.append("bgcolor=" + "0x" + hex_color[1:]) lcmd.append("map=" + self.o_layer_name) return lcmd def OnListSelChanged(self, event): """Update widgets according to selected layer in list.""" curr_sel_ls = self.list.GetSelectedLayers() # update self.sel_layers (selected layer list) if "WMS" in self.ws: for sel_l in self.sel_layers[:]: if sel_l not in curr_sel_ls: self.sel_layers.remove(sel_l) for l in curr_sel_ls: if l not in self.sel_layers: self.sel_layers.append(l) self._updateLayerOrderList() else: self.sel_layers = curr_sel_ls # update projection self.projs_list = [] projs_list = [] intersect_proj = [] first = True for l in curr_sel_ls: layer_projs = l["cap_intf_l"].GetLayerData("srs") if first: projs_list = layer_projs first = False continue projs_list = set(projs_list).intersection(layer_projs) if "srs" not in self.drv_props["ignored_params"]: for proj in projs_list: proj_code = Srs(proj.strip()).getcode() proj_spl = proj_code.split(":") if proj_spl[0].strip().lower() in self.drv_info.GetSrs(): # accept ogc:crs code self.projs_list.append(proj_code) cur_sel = self.params["srs"].GetStringSelection() self.projs_list = sorted(self.projs_list) self.params["srs"].SetItems(self.projs_list) if cur_sel: self.params["srs"].SetStringSelection(cur_sel) else: try: i = self.projs_list.index("EPSG:4326") self.params["srs"].SetSelection(i) except ValueError: if len(self.projs_list) > 0: self.params["srs"].SetSelection(0) # update format if "WMS" not in self.ws and "format" not in self.drv_props["ignored_params"]: self.formats_list = [] cur_sel = None if self.params["format"]: cur_sel = self.params["format"].GetStringSelection() if len(curr_sel_ls) > 0: self.formats_list = sorted( self._getFormats(curr_sel_ls[0]["cap_intf_l"]) ) self._updateFormatRadioBox(self.formats_list) if cur_sel: if self.params["format"]: self.params["format"].SetStringSelection(cur_sel) else: self._setDefaultFormatVal() self.Layout() def _setDefaultFormatVal(self): """Set default format value.""" try: i = self.formats_list.index("png") self.params["format"].SetSelection(i) except ValueError: pass def _updateFormatRadioBox(self, formats_list): """Helper function""" if self.params["format"]: self.req_page_sizer.Detach(self.params["format"]) self.params["format"].Destroy() if len(self.formats_list) > 0: self.params["format"] = wx.RadioBox( parent=self.req_page_panel, id=wx.ID_ANY, label=_("Source image format"), pos=wx.DefaultPosition, choices=formats_list, majorDimension=4, style=wx.RA_SPECIFY_COLS, ) self.source_sizer.Insert( 2, window=self.params["format"], flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5, ) def _getFormats(self, layer=None): """Get formats WMS has formats defined generally for whole cap. In WMTS and NASA OnEarh formats are defined for layer. """ formats_label = [] if layer is None: formats_list = self.cap.GetFormats() else: formats_list = layer.GetLayerData("format") for frmt in formats_list: frmt = frmt.strip() label = self.drv_info.GetFormatLabel(frmt) if label: formats_label.append(label) return formats_label def _checkImportValues( self, ): """Check if required widgets are selected/filled""" warning_str = "" show_war = False if not self.list or not self.list.GetSelectedLayers(): warning_str += _("Select layer in layer list.\n") show_war = True if ( self.params["format"] is not None and self.params["format"].GetSelection() == -1 ): warning_str += _("Select source image format.\n") show_war = True if self.params["srs"] is not None and self.params["srs"].GetSelection() == -1: warning_str += _("Select source projection.\n") show_war = True if not self.o_layer_name: warning_str += _("Choose output layer name.\n") show_war = True if show_war: GMessage(parent=self.parent, message=warning_str) return False return True def SetOutputLayerName(self, name): """Set name of layer to be added to layer tree""" self.o_layer_name = name def GetOutputLayerName(self): return self.o_layer_name def GetCapFile(self): """Get path to file where capabilities are saved""" return self.cap_file def GetWebService(self): """Get web service""" return self.ws
class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl): """Styled wxGUI prompt with autocomplete and calltips""" def __init__(self, parent, giface, menuModel, margin=False): GPrompt.__init__(self, parent=parent, giface=giface, menuModel=menuModel) wx.stc.StyledTextCtrl.__init__(self, self.panel, id=wx.ID_ANY) # # styles # self.SetWrapMode(True) self.SetUndoCollection(True) # # create command and map lists for autocompletion # self.AutoCompSetIgnoreCase(False) # # line margins # # TODO print number only from cmdlog self.SetMarginWidth(1, 0) self.SetMarginWidth(2, 0) if margin: self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER) self.SetMarginWidth(0, 30) else: self.SetMarginWidth(0, 0) # # miscellaneous # self.SetViewWhiteSpace(False) self.SetUseTabs(False) self.UsePopUp(True) self.SetUseHorizontalScrollBar(True) # support light and dark mode bg_color = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW) fg_color = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOWTEXT) selection_color = wx.SystemSettings().GetColour( wx.SYS_COLOUR_HIGHLIGHT) self.StyleSetBackground(wx.stc.STC_STYLE_DEFAULT, bg_color) self.StyleSetForeground(wx.stc.STC_STYLE_DEFAULT, fg_color) self.SetCaretForeground(fg_color) self.SetSelBackground(True, selection_color) self.StyleClearAll() # # bindings # self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) self.Bind(wx.EVT_CHAR, self.OnChar) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged) if sys.platform != "darwin": # unstable on Mac with wxPython 3 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) # signal which requests showing of a notification self.showNotification = Signal("GPromptSTC.showNotification") # signal to notify selected command self.commandSelected = Signal("GPromptSTC.commandSelected") def OnTextSelectionChanged(self, event): """Copy selected text to clipboard and skip event. The same function is in GStc class (goutput.py). """ wx.CallAfter(self.Copy) event.Skip() def OnItemChanged(self, event): """Change text in statusbar if the item selection in the auto-completion list is changed""" # list of commands if self.toComplete["entity"] == "command": item = (self.toComplete["cmd"].rpartition(".")[0] + "." + self.autoCompList[event.GetIndex()]) try: nodes = self._menuModel.SearchNodes(key="command", value=item) desc = "" if nodes: self.commandSelected.emit(command=item) desc = nodes[0].data["description"] except KeyError: desc = "" self.ShowStatusText(desc) # list of flags elif self.toComplete["entity"] == "flags": desc = self.cmdDesc.get_flag( self.autoCompList[event.GetIndex()])["description"] self.ShowStatusText(desc) # list of parameters elif self.toComplete["entity"] == "params": item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()]) desc = item["name"] + "=" + item["type"] if not item["required"]: desc = "[" + desc + "]" desc += ": " + item["description"] self.ShowStatusText(desc) # list of flags and commands elif self.toComplete["entity"] == "params+flags": if self.autoCompList[event.GetIndex()][0] == "-": desc = self.cmdDesc.get_flag(self.autoCompList[ event.GetIndex()].strip("-"))["description"] else: item = self.cmdDesc.get_param( self.autoCompList[event.GetIndex()]) desc = item["name"] + "=" + item["type"] if not item["required"]: desc = "[" + desc + "]" desc += ": " + item["description"] self.ShowStatusText(desc) else: self.ShowStatusText("") def OnItemSelected(self, event): """Item selected from the list""" lastWord = self.GetWordLeft() # to insert selection correctly if selected word partly matches written # text match = difflib.SequenceMatcher(None, event.GetText(), lastWord) matchTuple = match.find_longest_match(0, len(event.GetText()), 0, len(lastWord)) compl = event.GetText()[matchTuple[2]:] text = self.GetTextLeft() + compl # add space or '=' at the end end = "=" for char in (".", "-", "="): if text.split(" ")[-1].find(char) >= 0: end = " " compl += end text += end self.AddText(compl) pos = len(text) self.SetCurrentPos(pos) cmd = text.strip().split(" ")[0] if not self.cmdDesc or cmd != self.cmdDesc.get_name(): try: self.cmdDesc = gtask.parse_interface(cmd) except IOError: self.cmdDesc = None def OnKillFocus(self, event): """Hides autocomplete""" # hide autocomplete if self.AutoCompActive(): self.AutoCompCancel() event.Skip() def SetTextAndFocus(self, text): pos = len(text) self.commandSelected.emit(command=text) self.SetText(text) self.SetSelectionStart(pos) self.SetCurrentPos(pos) self.SetFocus() def UpdateCmdHistory(self, cmd): """Update command history :param cmd: command given as a string """ # add command to history self.cmdbuffer.append(cmd) # update also traced commands self.commands.append(cmd) # keep command history to a manageable size if len(self.cmdbuffer) > 200: del self.cmdbuffer[0] self.cmdindex = len(self.cmdbuffer) def EntityToComplete(self): """Determines which part of command (flags, parameters) should be completed at current cursor position""" entry = self.GetTextLeft() toComplete = dict(cmd=None, entity=None) try: cmd = entry.split()[0].strip() except IndexError: return toComplete try: splitted = utils.split(str(entry)) except ValueError: # No closing quotation error return toComplete if len(splitted) > 0 and cmd in globalvar.grassCmd: toComplete["cmd"] = cmd if entry[-1] == " ": words = entry.split(" ") if any(word.startswith("-") for word in words): toComplete["entity"] = "params" else: toComplete["entity"] = "params+flags" else: # get word left from current position word = self.GetWordLeft(withDelimiter=True) if word[0] == "=" and word[-1] == "@": toComplete["entity"] = "mapsets" elif word[0] == "=": # get name of parameter paramName = self.GetWordLeft( withDelimiter=False, ignoredDelimiter="=").strip("=") if paramName: try: param = self.cmdDesc.get_param(paramName) except (ValueError, AttributeError): return toComplete else: return toComplete if param["values"]: toComplete["entity"] = "param values" elif param["prompt"] == "raster" and param[ "element"] == "cell": toComplete["entity"] = "raster map" elif param["prompt"] == "vector" and param[ "element"] == "vector": toComplete["entity"] = "vector map" elif word[0] == "-": toComplete["entity"] = "flags" elif word[0] == " ": toComplete["entity"] = "params" else: toComplete["entity"] = "command" toComplete["cmd"] = cmd return toComplete def GetWordLeft(self, withDelimiter=False, ignoredDelimiter=None): """Get word left from current cursor position. The beginning of the word is given by space or chars: .,-= :param withDelimiter: returns the word with the initial delimeter :param ignoredDelimiter: finds the word ignoring certain delimeter """ textLeft = self.GetTextLeft() parts = list() if ignoredDelimiter is None: ignoredDelimiter = "" for char in set(" .,-=") - set(ignoredDelimiter): if not withDelimiter: delimiter = "" else: delimiter = char parts.append(delimiter + textLeft.rpartition(char)[2]) return min(parts, key=lambda x: len(x)) def ShowList(self): """Show sorted auto-completion list if it is not empty""" if len(self.autoCompList) > 0: self.autoCompList.sort() self.AutoCompShow(0, itemList=" ".join(self.autoCompList)) def OnKeyPressed(self, event): """Key pressed capture special treatment for tabulator to show help""" pos = self.GetCurrentPos() if event.GetKeyCode() == wx.WXK_TAB: # show GRASS command calltips (to hide press 'ESC') entry = self.GetTextLeft() try: cmd = entry.split()[0].strip() except IndexError: cmd = "" if cmd not in globalvar.grassCmd: return info = gtask.command_info(cmd) self.CallTipSetBackground("#f4f4d1") self.CallTipSetForeground("BLACK") self.CallTipShow(pos, info["usage"] + "\n\n" + info["description"]) elif (event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER) and not self.AutoCompActive()): # run command on line when <return> is pressed self._runCmd(self.GetCurLine()[0].strip()) elif (event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and not self.AutoCompActive()): # Command history using up and down if len(self.cmdbuffer) < 1: return self.DocumentEnd() # move through command history list index values if event.GetKeyCode() == wx.WXK_UP: self.cmdindex = self.cmdindex - 1 if event.GetKeyCode() == wx.WXK_DOWN: self.cmdindex = self.cmdindex + 1 if self.cmdindex < 0: self.cmdindex = 0 if self.cmdindex > len(self.cmdbuffer) - 1: self.cmdindex = len(self.cmdbuffer) - 1 try: # without strip causes problem on windows txt = self.cmdbuffer[self.cmdindex].strip() except KeyError: txt = "" # clear current line and insert command history self.DelLineLeft() self.DelLineRight() pos = self.GetCurrentPos() self.InsertText(pos, txt) self.LineEnd() self.ShowStatusText("") else: event.Skip() def OnChar(self, event): """Key char capture for autocompletion, calltips, and command history .. todo:: event.ControlDown() for manual autocomplete """ # keycodes used: "." = 46, "=" = 61, "-" = 45 pos = self.GetCurrentPos() # complete command after pressing '.' if event.GetKeyCode() == 46: self.autoCompList = list() entry = self.GetTextLeft() self.InsertText(pos, ".") self.CharRight() self.toComplete = self.EntityToComplete() try: if self.toComplete["entity"] == "command": for command in globalvar.grassCmd: try: if command.find(self.toComplete["cmd"]) == 0: dotNumber = list( self.toComplete["cmd"]).count(".") self.autoCompList.append( command.split(".", dotNumber)[-1]) except UnicodeDecodeError as error: sys.stderr.write( DecodeString(command) + ": " + str(error)) except (KeyError, TypeError): return self.ShowList() # complete flags after pressing '-' elif ((event.GetKeyCode() == 45) or event.GetKeyCode() == wx.WXK_NUMPAD_SUBTRACT or event.GetKeyCode() == wx.WXK_SUBTRACT): self.autoCompList = list() entry = self.GetTextLeft() self.InsertText(pos, "-") self.CharRight() self.toComplete = self.EntityToComplete() if self.toComplete["entity"] == "flags" and self.cmdDesc: if self.GetTextLeft()[-2:] == " -": # complete e.g. --quite for flag in self.cmdDesc.get_options()["flags"]: if len(flag["name"]) == 1: self.autoCompList.append(flag["name"]) else: for flag in self.cmdDesc.get_options()["flags"]: if len(flag["name"]) > 1: self.autoCompList.append(flag["name"]) self.ShowList() # complete map or values after parameter elif event.GetKeyCode() == 61: self.autoCompList = list() self.InsertText(pos, "=") self.CharRight() self.toComplete = self.EntityToComplete() if self.toComplete["entity"] == "raster map": self.autoCompList = self.mapList["raster"] elif self.toComplete["entity"] == "vector map": self.autoCompList = self.mapList["vector"] elif self.toComplete["entity"] == "param values": param = self.GetWordLeft(withDelimiter=False, ignoredDelimiter="=").strip(" =") self.autoCompList = self.cmdDesc.get_param(param)["values"] self.ShowList() # complete mapset ('@') elif event.GetKeyCode() == 64: self.autoCompList = list() self.InsertText(pos, "@") self.CharRight() self.toComplete = self.EntityToComplete() if self.toComplete["entity"] == "mapsets": self.autoCompList = self.mapsetList self.ShowList() # complete after pressing CTRL + Space elif event.GetKeyCode() == wx.WXK_SPACE and event.ControlDown(): self.autoCompList = list() self.toComplete = self.EntityToComplete() # complete command if self.toComplete["entity"] == "command": for command in globalvar.grassCmd: if command.find(self.toComplete["cmd"]) == 0: dotNumber = list(self.toComplete["cmd"]).count(".") self.autoCompList.append( command.split(".", dotNumber)[-1]) # complete flags in such situations (| is cursor): # r.colors -| ...w, q, l # r.colors -w| ...w, q, l elif self.toComplete["entity"] == "flags" and self.cmdDesc: for flag in self.cmdDesc.get_options()["flags"]: if len(flag["name"]) == 1: self.autoCompList.append(flag["name"]) # complete parameters in such situations (| is cursor): # r.colors -w | ...color, map, rast, rules # r.colors col| ...color elif self.toComplete["entity"] == "params" and self.cmdDesc: for param in self.cmdDesc.get_options()["params"]: if param["name"].find( self.GetWordLeft(withDelimiter=False)) == 0: self.autoCompList.append(param["name"]) # complete flags or parameters in such situations (| is cursor): # r.colors | ...-w, -q, -l, color, map, rast, rules # r.colors color=grey | ...-w, -q, -l, color, map, rast, rules elif self.toComplete["entity"] == "params+flags" and self.cmdDesc: self.autoCompList = list() for param in self.cmdDesc.get_options()["params"]: self.autoCompList.append(param["name"]) for flag in self.cmdDesc.get_options()["flags"]: if len(flag["name"]) == 1: self.autoCompList.append("-" + flag["name"]) else: self.autoCompList.append("--" + flag["name"]) self.ShowList() # complete map or values after parameter # r.buffer input=| ...list of raster maps # r.buffer units=| ... feet, kilometers, ... elif self.toComplete["entity"] == "raster map": self.autoCompList = list() self.autoCompList = self.mapList["raster"] elif self.toComplete["entity"] == "vector map": self.autoCompList = list() self.autoCompList = self.mapList["vector"] elif self.toComplete["entity"] == "param values": self.autoCompList = list() param = self.GetWordLeft(withDelimiter=False, ignoredDelimiter="=").strip(" =") self.autoCompList = self.cmdDesc.get_param(param)["values"] self.ShowList() elif event.GetKeyCode() == wx.WXK_SPACE: items = self.GetTextLeft().split() if len(items) == 1: cmd = items[0].strip() if cmd in globalvar.grassCmd and ( not self.cmdDesc or cmd != self.cmdDesc.get_name()): try: self.cmdDesc = gtask.parse_interface(cmd) except IOError: self.cmdDesc = None event.Skip() else: event.Skip() def ShowStatusText(self, text): """Requests showing of notification, e.g. showing in a statusbar.""" self.showNotification.emit(message=text) def GetTextLeft(self): """Returns all text left of the caret""" pos = self.GetCurrentPos() self.HomeExtend() entry = self.GetSelectedText() self.SetCurrentPos(pos) return entry def OnDestroy(self, event): """The clipboard contents can be preserved after the app has exited""" if wx.TheClipboard.IsOpened(): wx.TheClipboard.Flush() event.Skip() def OnCmdErase(self, event): """Erase command prompt""" self.Home() self.DelLineRight()
def __init__(self, parent, web_service, **kwargs): """Show data from capabilities file. Signal: capParsed - this signal is emitted when capabilities file is downloaded (after ConnectToServer method was called) :param parent: parent widget :param web_service: web service to be panel generated for """ wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY) self.parent = parent self.ws = web_service self.capParsed = Signal("WSPanel.capParsed") # stores widgets, which represents parameters/flags of d.wms self.params = {} self.flags = {} self.o_layer_name = "" # stores err output from r.in.wms during getting capabilities self.cmd_err_str = "" # stores selected layer from layer list self.sel_layers = [] # downloaded and parsed data from server successfully? self.is_connected = False # common part of command for r.in.wms -c and d.wms self.ws_cmdl = None # provides information about driver parameters self.drv_info = WMSDriversInfo() self.drv_props = self.drv_info.GetDrvProperties(self.ws) self.ws_drvs = { "WMS_1.1.1": { "cmd": ["wms_version=1.1.1", "driver=WMS_GRASS"], "cap_parser": lambda temp_file: WMSCapabilities(temp_file, "1.1.1"), }, "WMS_1.3.0": { "cmd": ["wms_version=1.3.0", "driver=WMS_GRASS"], "cap_parser": lambda temp_file: WMSCapabilities(temp_file, "1.3.0"), }, "WMTS": { "cmd": ["driver=WMTS_GRASS"], "cap_parser": WMTSCapabilities, }, "OnEarth": { "cmd": ["driver=OnEarth_GRASS"], "cap_parser": OnEarthCapabilities, }, } self.cmdStdErr = GStderr(self) self.cmd_thread = CmdThread(self) self.cap_file = grass.tempfile() reqDataBox = StaticBox(parent=self, label=_(" Requested data settings ")) self._nb_sizer = wx.StaticBoxSizer(reqDataBox, wx.VERTICAL) self.notebook = GNotebook( parent=self, style=FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON | FN.FNB_NODRAG ) self._requestPage() self._advancedSettsPage() self._layout() self.layerSelected = self.list.layerSelected self.Bind(EVT_CMD_DONE, self.OnCapDownloadDone) self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) self.SetMinSize((-1, 300))
class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin): def __init__(self, parent, scatt_id, scatt_mgr, transpose, id=wx.ID_ANY): # TODO should not be transpose and scatt_id but x, y wx.Panel.__init__(self, parent, id) # bacause of aui (if floatable it can not take cursor from parent) ManageBusyCursorMixin.__init__(self, window=self) self.parent = parent self.full_extend = None self.mode = None self._createWidgets() self._doLayout() self.scatt_id = scatt_id self.scatt_mgr = scatt_mgr self.cidpress = None self.cidrelease = None self.rend_dt = {} self.transpose = transpose self.inverse = False self.SetSize((200, 100)) self.Layout() self.base_scale = 1.2 self.Bind(wx.EVT_CLOSE, lambda event: self.CleanUp()) self.plotClosed = Signal("ScatterPlotWidget.plotClosed") self.cursorMove = Signal("ScatterPlotWidget.cursorMove") self.contex_menu = ScatterPlotContextMenu(plot=self) self.ciddscroll = None self.canvas.mpl_connect("motion_notify_event", self.Motion) self.canvas.mpl_connect("button_press_event", self.OnPress) self.canvas.mpl_connect("button_release_event", self.OnRelease) self.canvas.mpl_connect("draw_event", self.DrawCallback) self.canvas.mpl_connect("figure_leave_event", self.OnCanvasLeave) def DrawCallback(self, event): self.polygon_drawer.DrawCallback(event) self.axes.draw_artist(self.zoom_rect) def _createWidgets(self): # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((1.0, 1.0), dpi=self.dpi) self.fig.autolayout = True self.canvas = FigCanvas(self, -1, self.fig) self.axes = self.fig.add_axes([0.0, 0.0, 1, 1]) pol = Polygon(list(zip([0], [0])), animated=True) self.axes.add_patch(pol) self.polygon_drawer = PolygonDrawer(self.axes, pol=pol, empty_pol=True) self.zoom_wheel_coords = None self.zoom_rect_coords = None self.zoom_rect = Polygon(list(zip([0], [0])), facecolor="none") self.zoom_rect.set_visible(False) self.axes.add_patch(self.zoom_rect) def ZoomToExtend(self): if self.full_extend: self.axes.axis(self.full_extend) self.canvas.draw() def SetMode(self, mode): self._deactivateMode() if mode == "zoom": self.ciddscroll = self.canvas.mpl_connect("scroll_event", self.ZoomWheel) self.mode = "zoom" elif mode == "zoom_extend": self.mode = "zoom_extend" elif mode == "pan": self.mode = "pan" elif mode: self.polygon_drawer.SetMode(mode) def SetSelectionPolygonMode(self, activate): self.polygon_drawer.SetSelectionPolygonMode(activate) def _deactivateMode(self): self.mode = None self.polygon_drawer.SetMode(None) if self.ciddscroll: self.canvas.mpl_disconnect(self.ciddscroll) self.zoom_rect.set_visible(False) self._stopCategoryEdit() def GetCoords(self): coords = self.polygon_drawer.GetCoords() if coords is None: return if self.transpose: for c in coords: tmp = c[0] c[0] = c[1] c[1] = tmp return coords def SetEmpty(self): return self.polygon_drawer.SetEmpty() def OnRelease(self, event): if not self.mode == "zoom": return self.zoom_rect.set_visible(False) self.ZoomRectangle(event) self.canvas.draw() def OnPress(self, event): "on button press we will see if the mouse is over us and store some data" if not event.inaxes: return if self.mode == "zoom_extend": self.ZoomToExtend() if event.xdata and event.ydata: self.zoom_wheel_coords = {"x": event.xdata, "y": event.ydata} self.zoom_rect_coords = {"x": event.xdata, "y": event.ydata} else: self.zoom_wheel_coords = None self.zoom_rect_coords = None def _stopCategoryEdit(self): "disconnect all the stored connection ids" if self.cidpress: self.canvas.mpl_disconnect(self.cidpress) if self.cidrelease: self.canvas.mpl_disconnect(self.cidrelease) # self.canvas.mpl_disconnect(self.cidmotion) def _doLayout(self): self.main_sizer = wx.BoxSizer(wx.VERTICAL) self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.SetSizer(self.main_sizer) self.main_sizer.Fit(self) def Plot(self, cats_order, scatts, ellipses, styles): """Redraws the figure""" callafter_list = [] if self.full_extend: cx = self.axes.get_xlim() cy = self.axes.get_ylim() c = cx + cy else: c = None q = Queue() _rendDtMemmapsToFiles(self.rend_dt) p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.rend_dt, q)) p.start() merged_img, self.full_extend, self.rend_dt = q.get() p.join() _rendDtFilesToMemmaps(self.rend_dt) merged_img = np.memmap(filename=merged_img["dt"], shape=merged_img["sh"]) # merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None) self.axes.clear() self.axes.axis("equal") if self.transpose: merged_img = np.transpose(merged_img, (1, 0, 2)) img = imshow( self.axes, merged_img, extent=[int(ceil(x)) for x in self.full_extend], origin="lower", interpolation="nearest", aspect="equal", ) callafter_list.append([self.axes.draw_artist, [img]]) callafter_list.append([grass.try_remove, [merged_img.filename]]) for cat_id in cats_order: if cat_id == 0: continue if cat_id not in ellipses: continue e = ellipses[cat_id] if not e: continue colors = styles[cat_id]["color"].split(":") if self.transpose: e["theta"] = 360 - e["theta"] + 90 if e["theta"] >= 360: e["theta"] = abs(360 - e["theta"]) e["pos"] = [e["pos"][1], e["pos"][0]] ellip = Ellipse( xy=e["pos"], width=e["width"], height=e["height"], angle=e["theta"], edgecolor="w", linewidth=1.5, facecolor="None", ) self.axes.add_artist(ellip) callafter_list.append([self.axes.draw_artist, [ellip]]) color = [ int(v) / 255.0 for v in styles[cat_id]["color"].split(":")[:3] ] ellip = Ellipse( xy=e["pos"], width=e["width"], height=e["height"], angle=e["theta"], edgecolor=color, linewidth=1, facecolor="None", ) self.axes.add_artist(ellip) callafter_list.append([self.axes.draw_artist, [ellip]]) center = Line2D( [e["pos"][0]], [e["pos"][1]], marker="x", markeredgecolor="w", # markerfacecolor=color, markersize=2, ) self.axes.add_artist(center) callafter_list.append([self.axes.draw_artist, [center]]) callafter_list.append([self.fig.canvas.blit, []]) if c: self.axes.axis(c) wx.CallAfter(lambda: self.CallAfter(callafter_list)) def CallAfter(self, funcs_list): while funcs_list: fcn, args = funcs_list.pop(0) fcn(*args) self.canvas.draw() def CleanUp(self): self.plotClosed.emit(scatt_id=self.scatt_id) self.Destroy() def ZoomWheel(self, event): # get the current x and y limits if not event.inaxes: return # tcaswell # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel cur_xlim = self.axes.get_xlim() cur_ylim = self.axes.get_ylim() xdata = event.xdata ydata = event.ydata if event.button == "up": scale_factor = 1 / self.base_scale elif event.button == "down": scale_factor = self.base_scale else: scale_factor = 1 extend = ( xdata - (xdata - cur_xlim[0]) * scale_factor, xdata + (cur_xlim[1] - xdata) * scale_factor, ydata - (ydata - cur_ylim[0]) * scale_factor, ydata + (cur_ylim[1] - ydata) * scale_factor, ) self.axes.axis(extend) self.canvas.draw() def ZoomRectangle(self, event): # get the current x and y limits if not self.mode == "zoom": return if event.inaxes is None: return if event.button != 1: return cur_xlim = self.axes.get_xlim() cur_ylim = self.axes.get_ylim() x1, y1 = event.xdata, event.ydata x2 = deepcopy(self.zoom_rect_coords["x"]) y2 = deepcopy(self.zoom_rect_coords["y"]) if x1 == x2 or y1 == y2: return if x1 > x2: tmp = x1 x1 = x2 x2 = tmp if y1 > y2: tmp = y1 y1 = y2 y2 = tmp self.axes.axis((x1, x2, y1, y2)) # self.axes.set_xlim(x1, x2)#, auto = True) # self.axes.set_ylim(y1, y2)#, auto = True) self.canvas.draw() def Motion(self, event): self.PanMotion(event) self.ZoomRectMotion(event) if event.inaxes is None: return self.cursorMove.emit(x=event.xdata, y=event.ydata, scatt_id=self.scatt_id) def OnCanvasLeave(self, event): self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id) def PanMotion(self, event): "on mouse movement" if not self.mode == "pan": return if event.inaxes is None: return if event.button != 1: return cur_xlim = self.axes.get_xlim() cur_ylim = self.axes.get_ylim() x, y = event.xdata, event.ydata mx = (x - self.zoom_wheel_coords["x"]) * 0.6 my = (y - self.zoom_wheel_coords["y"]) * 0.6 extend = ( cur_xlim[0] - mx, cur_xlim[1] - mx, cur_ylim[0] - my, cur_ylim[1] - my, ) self.zoom_wheel_coords["x"] = x self.zoom_wheel_coords["y"] = y self.axes.axis(extend) # self.canvas.copy_from_bbox(self.axes.bbox) # self.canvas.restore_region(self.background) self.canvas.draw() def ZoomRectMotion(self, event): if not self.mode == "zoom": return if event.inaxes is None: return if event.button != 1: return x1, y1 = event.xdata, event.ydata self.zoom_rect.set_visible(True) x2 = self.zoom_rect_coords["x"] y2 = self.zoom_rect_coords["y"] self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1)) # self.axes.draw_artist(self.zoom_rect) self.canvas.draw()