# -*- coding: utf-8 -*- """ @brief activities This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author: Anna Petrasova (akratoc@ncsu.edu) """ import os import imp import datetime import json import time import traceback import wx import wx.lib.newevent import wx.lib.filebrowsebutton as filebrowse from wx.lib.wordwrap import wordwrap from grass.exceptions import CalledModuleError, ScriptError from grass.pydispatch.signal import Signal from tangible_utils import get_environment try: from activities_profile import ProfileFrame except ImportError: ProfileFrame = None from activities_dashboard import MultipleDashboardFrame # lazy importing activities_slides updateProfile, EVT_UPDATE_PROFILE = wx.lib.newevent.NewEvent() updateDisplay, EVT_UPDATE_DISPLAY = wx.lib.newevent.NewEvent() class ActivitiesPanel(wx.Panel): def __init__(self, parent, giface, settings, scaniface): wx.Panel.__init__(self, parent) self.group = None self.env = None self.giface = giface self.parent = parent self.settings = settings self.scaniface = scaniface self.current = 0 self.currentSubtask = 0 self.startTime = 0 self.endTime = 0 self.settingsChanged = Signal('ActivitiesPanel.settingsChanged') self.configFile = '' self.tasks = [] self.configuration = {} self.profileFrame = None self.dashboardFrame = None self.handsoff = None self.slides = None self.scaniface.additionalParams4Analyses = {'subTask': self.currentSubtask} self._processingSubTask = False # we want to start in pause mode to not capture any data self.scaniface.pause = True if 'activities' not in self.settings: self.settings['activities'] = {} self.settings['activities']['config'] = '' else: self.configFile = self.settings['activities']['config'] self.configPath = filebrowse.FileBrowseButton(self, labelText='Configuration:', fileMask = "JSON file (*.json)|*.json", changeCallback=self._loadConfiguration) self.configPath.SetValue(self.configFile, 0) self.title = wx.StaticText(self, label='') self.title.SetFont(wx.Font(18, wx.DEFAULT, wx.NORMAL, wx.BOLD)) self.buttonBack = wx.Button(self, label=' < ') self.buttonForward = wx.Button(self, label=' > ') self.buttonBack.Bind(wx.EVT_BUTTON, self.OnBack) self.buttonForward.Bind(wx.EVT_BUTTON, self.OnForward) self.buttonStart = wx.Button(self, label='Start activity') self.buttonCalibrate = wx.Button(self, size=(150, -1), label='Calibrate') self.buttonStop = wx.Button(self, label='End activity') self.buttonStart.Bind(wx.EVT_BUTTON, self.OnStart) self.buttonCalibrate.Bind(wx.EVT_BUTTON, lambda evt: self.Calibrate(startTask=False)) self.buttonStop.Bind(wx.EVT_BUTTON, self.OnStop) self.timeText = wx.StaticText(self, label='00:00', style=wx.ALIGN_CENTRE) self.timeText.SetFont(wx.Font(15, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.FONTWEIGHT_BOLD)) self.slidesStatus = wx.StaticText(self, label='Slides off', style=wx.ALIGN_CENTRE | wx.ALIGN_CENTRE_HORIZONTAL) self.slidesStatus.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) self.buttonNext = wx.Button(self, label='Next') self.buttonNext.Bind(wx.EVT_BUTTON, self.OnSubtask) self.instructions = wx.StaticText(self) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnTimer) self.mainSizer = wx.BoxSizer(wx.VERTICAL) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.configPath, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.title, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.buttonBack, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.buttonForward, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.buttonCalibrate, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.buttonStart, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) # sizer.Add(self.buttonPause, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.buttonStop, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.slidesStatus, proportion=0, flag=wx.EXPAND |wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border=10) sizer.Add(self.timeText, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER | wx.LEFT, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.buttonNext, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL, border=5) self.mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.instructions, proportion=1, flag=wx.EXPAND | 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.buttonNext.Hide() self.Layout() self.Bind(EVT_UPDATE_PROFILE, self.OnProfileUpdate) self.Bind(EVT_UPDATE_DISPLAY, self.OnDisplayUpdate) self._init() def _init(self): if self.configFile: try: with open(self.configFile, 'r') as f: try: self.configuration = json.load(f) self.tasks = self.configuration['tasks'] # this should reset the analysis file only when configuration is successfully loaded self.settings['analyses']['file'] = '' except ValueError: self.configFile = None except IOError: self.configFile = None self.current = 0 if self.tasks: self.title.SetLabel(self.tasks[self.current]['title']) self.buttonBack.Enable(False) self.buttonForward.Enable(True) self.timeText.SetLabel('00:00') self.slidesStatus.Show(bool('slides' in self.configuration and self.configuration['slides'])) self.instructions.SetLabel(self._getInstructions()) else: self._enableGUI(False) if self.configFile: self.buttonNext.Show('sublayers' in self.tasks[self.current]) self.buttonCalibrate.Show('calibrate' in self.tasks[self.current]) self._bindUserStop() self.Layout() def _enableGUI(self, enable): self.buttonBack.Enable(enable) self.buttonForward.Enable(enable) self.buttonStart.Enable(enable) self.buttonCalibrate.Enable(enable) self.buttonStop.Enable(enable) self.slidesStatus.Enable(enable) self.timeText.Enable(enable) self.title.Enable(enable) self.instructions.Show(enable) def _bindUserStop(self): windows = [mapw for mapw in self.giface.GetAllMapDisplays()] windows.append(wx.GetTopLevelParent(self)) windows.append(self.giface.lmgr) bindings = {"stopTask": self.OnUserStop, 'scanOnce': self.OnScanOnce, 'taskNext': self.OnNextTask, 'taskPrevious': self.OnPreviousTask, 'startTask': self.StartAutomated} if "keyboard_events" in self.configuration: items = [] for eventName in self.configuration['keyboard_events']: eventId = wx.NewId() items.append((wx.ACCEL_NORMAL, self.configuration['keyboard_events'][eventName], eventId)) for win in windows: win.Bind(wx.EVT_MENU, bindings.get(eventName, lambda evt: self.CustomAction(eventName)), id=eventId) accel_tbl = wx.AcceleratorTable(items) for win in windows: win.SetAcceleratorTable(accel_tbl) def CustomAction(self, eventName): env = get_environment(rast=self.settings['output']['scan']) myanalyses, functions = self._reloadAnalysisFile(funcPrefix=eventName) for func in functions: try: exec('myanalyses.' + func + "(eventHandler=wx.GetTopLevelParent(self), env=env)") except (CalledModuleError, Exception, ScriptError): print(traceback.print_exc()) def OnNextTask(self, event): if self.timer.IsRunning(): self.OnStop(event=None) self.OnForward(None) def OnPreviousTask(self, event): if self.timer.IsRunning(): self.OnStop(event=None) self.OnBack(None) def _checkChangeTask(self): if self.timer.IsRunning(): dlg = wx.MessageDialog(self, 'Stop currently running task before changing task', 'Stop task', wx.OK | wx.ICON_WARNING) dlg.ShowModal() dlg.Destroy() return False return True def _loadConfiguration(self, event): self.configFile = self.configPath.GetValue().strip() if self.configFile: self.settings['activities']['config'] = self.configFile self._enableGUI(True) with open(self.configFile, 'r') as f: try: self.configuration = json.load(f) self.tasks = self.configuration['tasks'] self.title.SetLabel(self.tasks[self.current]['title']) self.instructions.SetLabel(self._getInstructions()) except ValueError: self.configuration = {} self.settings['activities']['config'] = '' self._enableGUI(False) wx.MessageBox(parent=self, message='Parsing error while reading JSON file, please correct it and try again.', caption="Can't read JSON file", style=wx.OK | wx.ICON_ERROR) self._bindUserStop() else: self.settings['activities']['config'] = '' self._enableGUI(False) self.slidesStatus.Show(bool('slides' in self.configuration and self.configuration['slides'])) self.Layout() def _loadScanningParams(self, key): if key in self.tasks[self.current]: for each in self.tasks[self.current][key].keys(): self.settings['scan'][each] = self.tasks[self.current][key][each] def Calibrate(self, startTask): self._loadConfiguration(None) if 'base' in self.tasks[self.current]: self.settings['scan']['elevation'] = self.tasks[self.current]['base'] elif 'base_region' in self.tasks[self.current]: self.settings['scan']['region'] = self.tasks[self.current]['base_region'] self.settings['output']['scan'] = 'scan_saved' self.settings['analyses']['file'] = '' self._loadScanningParams(key='scanning_params') # just update whatever was not set with 'scanning_params' self._loadScanningParams(key='calibration_scanning_params') # resume scanning self.buttonCalibrate.SetLabel("Calibrating...") self.scaniface.filter['filter'] = False self._startScanning() wx.CallLater(2000, lambda: self.CalibrationDone(startTask)) def CalibrationDone(self, startTask): self._stopScanning() wx.CallLater(4000, lambda: self.buttonCalibrate.SetLabel("Calibrate")) if startTask: wx.CallLater(4000, lambda: self.OnStart(None)) def OnBack(self, event): if not self._checkChangeTask(): return # check if possible to go back if self.current <= 0: # just for sure self.buttonBack.Enable(False) return # move back self.current -= 1 if self.current <= 0: self.buttonBack.Enable(False) self.buttonForward.Enable(True) if self.tasks: self.title.SetLabel(self.tasks[self.current]['title']) self.timeText.SetLabel('00:00') self.buttonNext.Show('sublayers' in self.tasks[self.current]) self.buttonCalibrate.Show('calibrate' in self.tasks[self.current] and self.tasks[self.current]['calibrate']) self.instructions.SetLabel(self._getInstructions()) self.Layout() def OnForward(self, event): if not self._checkChangeTask(): return # check if possible to go forward if self.current >= len(self.tasks) - 1: # just for sure self.buttonForward.Enable(False) return self.current += 1 if self.current >= len(self.tasks) - 1: self.buttonForward.Enable(False) self.buttonBack.Enable(True) if self.tasks: self.title.SetLabel(self.tasks[self.current]['title']) self.timeText.SetLabel('00:00') self.buttonNext.Show('sublayers' in self.tasks[self.current]) self.buttonCalibrate.Show('calibrate' in self.tasks[self.current] and self.tasks[self.current]['calibrate']) self.instructions.SetLabel(self._getInstructions()) self.Layout() def StartAutomated(self, event): # Doesn't implement slides self._loadConfiguration(None) if 'calibrate' in self.tasks[self.current] and self.tasks[self.current]['calibrate']: self.Calibrate(startTask=True) else: self._startTask() def OnStart(self, event): self._loadConfiguration(None) if 'slides' in self.configuration and self.configuration['slides']: self._startSlides() else: # if no slides, start right away self._startTask() def _startSlides(self): # lazy import from activities_slides import Slides self.slides = Slides(self) self.slides.SetPosition(self.configuration['slides']['position']) self.slides.LoadURL('file://' + os.path.join(self.configuration['slides']['dir'], self.tasks[self.current]['slides']['file'])) self.slides.Maximize(True) self.slides.Show() slidenum = 1 self.slidesStatus.SetLabel("Slide {}".format(slidenum)) for t in self.tasks[self.current]['slides']['switch']: slidenum += 1 wx.CallLater(t * 1000, self._switchSlide, slidenum) wx.CallLater(self.tasks[self.current]['slides']['switch'][-1] * 1000, self._startTask) def _switchSlide(self, slidenum): if self.slides: # in case it's closed prematurely self.slides.Next() self.slidesStatus.SetLabel("Slide {}".format(slidenum)) def _getTaskDir(self): return self.configuration['taskDir'] if 'taskDir' in self.configuration else os.path.dirname(self.settings['activities']['config']) def _getInstructions(self): instr = self.configuration['tasks'][self.current]['instructions'] if 'instructions' in self.configuration['tasks'][self.current] else '' return wordwrap(instr, self.GetClientSize()[0] - 10, wx.ClientDC(self)) def _hideToolbarStatusbar(self): """Hide toolbar and statusbar of active Map Display""" self.giface.ShowAllToolbars(False) self.giface.ShowStatusbar(False) wx.CallLater(1000, self.giface.GetMapDisplay().PostSizeEvent) def _startTask(self): if self.timer.IsRunning(): return self._hideToolbarStatusbar() self.currentSubtask = 0 self._processingSubTask = False self.scaniface.additionalParams4Analyses = {'subTask': self.currentSubtask} self.LoadLayers() if 'base' in self.tasks[self.current]: self.settings['scan']['elevation'] = self.tasks[self.current]['base'] elif 'base_region' in self.tasks[self.current]: self.settings['scan']['region'] = self.tasks[self.current]['base_region'] self.settings['analyses']['file'] = os.path.join(self._getTaskDir(), self.tasks[self.current]['analyses']) self.settings['output']['scan'] = 'scan' self._loadScanningParams(key='scanning_params') # resume scanning if 'filter' in self.tasks[self.current]: self.scaniface.filter['filter'] = True self.scaniface.filter['counter'] = 0 self.scaniface.filter['threshold'] = self.tasks[self.current]['filter']['threshold'] self.scaniface.filter['debug'] = self.tasks[self.current]['filter']['debug'] if 'single_scan' in self.tasks[self.current] and self.tasks[self.current]['single_scan']: self._stopScanning() else: self._startScanning() # profile if 'profile' in self.tasks[self.current]: self.StartProfile() # display if 'display' in self.tasks[self.current]: self.StartDisplay() wx.CallLater(1000, self._setFocus) self.startTime = datetime.datetime.now() self.endTime = 0 self.timer.Start(100) def _setFocus(self): topParent = wx.GetTopLevelParent(self) topParent.Raise() topParent.SetFocus() def _closeAdditionalWindows(self): if self.slides: self.slides.Close() self.slidesStatus.SetLabel('Slides off') if self.profileFrame: self.profileFrame.Close() self.profileFrame = None if self.dashboardFrame: self.dashboardFrame.Destroy() self.dashboardFrame = None def _stopScanning(self): self.scaniface.pause = True self.scaniface.changedInput = True def _startScanning(self): self.scaniface.pause = False self.scaniface.changedInput = True def _removeAllLayers(self): ll = self.giface.GetLayerList() for l in reversed(ll): ll.DeleteLayer(l) def OnStop(self, event): self.timer.Stop() self._closeAdditionalWindows() self._removeAllLayers() self.settings['analyses']['file'] = '' if 'handsoff' in self.configuration: self.LoadHandsOff() # scan after hands off if 'duration_handsoff' in self.configuration: t = self.configuration['duration_handsoff'] else: t = 1 wx.CallLater(t, self._stop) else: self._stop() def _stop(self): # pause scanning self._stopScanning() if 'duration_handsoff_after' in self.configuration: t = self.configuration['duration_handsoff_after'] wx.CallLater(t, self.PostProcessing) else: self.PostProcessing() self._setFocus() def OnTimer(self, event): diff = datetime.datetime.now() - self.startTime minutes = diff.seconds // 60 seconds = diff.seconds - (minutes * 60) self.timeText.SetLabel('{:02d}:{:02d}'.format(minutes, seconds)) self.endTime = diff.seconds if 'time_limit' in self.tasks[self.current] and \ diff > datetime.timedelta(seconds=self.tasks[self.current]['time_limit']): self.timer.Stop() # sleep for several seconds now? self.OnStop(event=None) def LoadLayers(self): ll = self.giface.GetLayerList() for i, cmd in enumerate(self.configuration['tasks'][self.current]['layers']): opacity = 1.0 checked = True if "layers_opacity" in self.configuration['tasks'][self.current]: opacity_list = self.configuration['tasks'][self.current]['layers_opacity'] if i < len(opacity_list): opacity = float(opacity_list[i]) else: self.giface.WriteWarning("Number of layers is larger than the number of opacity values in config file") if "layers_checked" in self.configuration['tasks'][self.current]: checked_list = self.configuration['tasks'][self.current]['layers_checked'] if i < len(checked_list): checked = checked_list[i] else: self.giface.WriteWarning("Number of layers is larger than the number of checked values in config file") if cmd[0] == 'd.rast': l = ll.AddLayer('raster', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.vect': l = ll.AddLayer('vector', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.labels': l = ll.AddLayer('labels', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.shade': l = ll.AddLayer('shaded', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.rgb': l = ll.AddLayer('rgb', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.legend': l = ll.AddLayer('rastleg', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.northarrow': l = ll.AddLayer('northarrow', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) elif cmd[0] == 'd.barscale': l = ll.AddLayer('barscale', name=cmd[1].split('=')[1], checked=checked, opacity=opacity, cmd=cmd) else: l = ll.AddLayer('command', name=' '.join(cmd), checked=checked, opacity=opacity, cmd=[]) if not checked: # workaround: in not checked the order of layers is wrong try: for each in ll: ll.SelectLayer(each, False) ll.SelectLayer(l, True) except AttributeError: # SelectLayer introduced in r73097, for cases before: ll._tree.Unselect() ll._tree.SelectItem(l._layer, True) if 'sublayers' in self.tasks[self.current]: cmd = self.tasks[self.current]['sublayers'][0] if cmd[0] == 'd.rast': ll.AddLayer('raster', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) elif cmd[0] == 'd.vect': ll.AddLayer('vector', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) # zoom to base map self.ZoomToBase() def ZoomToBase(self): if 'base' in self.configuration['tasks'][self.current]: base = self.configuration['tasks'][self.current]['base'] self.giface.GetMapWindow().Map.GetRegion(rast=[base], update=True) elif 'base_region' in self.configuration['tasks'][self.current]: region = self.configuration['tasks'][self.current]['base_region'] self.giface.GetMapWindow().Map.GetRegion(regionName=region, update=True) self.giface.GetMapWindow().UpdateMap() def LoadHandsOff(self): ll = self.giface.GetLayerList() cmd = self.configuration['handsoff'] self.handsoff = ll.AddLayer('command', name=' '.join(cmd), checked=True, opacity=1.0, cmd=[]) def PostProcessing(self, onDone=None): wx.BeginBusyCursor() wx.SafeYield() env = get_environment(rast=self.settings['output']['scan']) try: postprocess = imp.load_source('postprocess', os.path.join(self._getTaskDir(), self.tasks[self.current]['analyses'])) except Exception as e: print(e) return functions = [func for func in dir(postprocess) if func.startswith('post_')] for func in functions: exec('del postprocess.' + func) try: postprocess = imp.load_source('postprocess', os.path.join(self._getTaskDir(), self.tasks[self.current]['analyses'])) except Exception as e: print(e) return functions = [func for func in dir(postprocess) if func.startswith('post_')] for func in functions: try: exec('postprocess.' + func + "(real_elev=self.settings['scan']['elevation']," " scanned_elev=self.settings['output']['scan']," " filterResults=self.scaniface.filter['counter']," " timeToFinish=self.endTime," " subTask=self.currentSubtask," " logDir=self.configuration['logDir']," " env=env)") except (CalledModuleError, Exception, ScriptError) as e: traceback.print_exc() wx.EndBusyCursor() if self.handsoff: ll = self.giface.GetLayerList() ll.DeleteLayer(self.handsoff) self.handsoff = None if onDone: onDone() def StartProfile(self): if not ProfileFrame: print('WARNING: DEM profile is not available, requires matplotlib library') return self.profileFrame = ProfileFrame(self) pos = self._getDashboardPosition(key='profile') size = self._getDashboardSize(key='profile') self.profileFrame.SetPosition(pos) self.profileFrame.SetSize(size) self.profileFrame.set_ticks(self.tasks[self.current]['profile']['ticks']) self.profileFrame.set_xlim(self.tasks[self.current]['profile']['limitx']) self.profileFrame.set_ylim(self.tasks[self.current]['profile']['limity']) self.profileFrame.Show() def OnProfileUpdate(self, event): # event can be received after frame is destroyed if not self.profileFrame: return env = get_environment(raster=self.tasks[self.current]['profile']['raster']) self.profileFrame.compute_profile(points=event.points, raster=self.tasks[self.current]['profile']['raster'], env=env) def StartDisplay(self): title = None if 'title' not in self.tasks[self.current]['display'] else self.tasks[self.current]['display']['title'] vertical = False if 'vertical' not in self.tasks[self.current]['display'] else self.tasks[self.current]['display']['vertical'] fontsize = self.tasks[self.current]['display']['fontsize'] maximum = self.tasks[self.current]['display']['maximum'] formatting_string = self.tasks[self.current]['display']['formatting_string'] self.dashboardFrame = MultipleDashboardFrame(self, fontsize=fontsize, maximum=maximum, title=title, formatting_string=formatting_string, vertical=vertical) pos = self._getDashboardPosition(key='display') size = self._getDashboardSize(key='display') self.dashboardFrame.SetSize(size) self.dashboardFrame.Show() self.dashboardFrame.SetPosition(pos) def _getDashboardPosition(self, key): if 'position' in self.tasks[self.current][key]: pos = self.tasks[self.current][key]['position'] elif 'relative_position' in self.tasks[self.current][key]: relPos = self.tasks[self.current][key]['relative_position'] pos = self._getPosFromRelative(relPos) else: pos = self._getPosFromRelative((1.01, 0.5)) return pos def _getDashboardSize(self, key): if 'size' in self.tasks[self.current][key]: size = self.tasks[self.current][key]['size'] elif 'relative_size' in self.tasks[self.current][key]: relSize = self.tasks[self.current][key]['relative_size'] size = self._getSizeFromRelative(relSize) else: size = self._getSizeFromRelative((0.3, 0.3)) return size def _getPosFromRelative(self, pos): md = self.giface.GetMapDisplay() mdSize = md.GetSize() mdPos = md.GetPosition() return (mdPos[0] + pos[0] * mdSize[0], mdPos[1] + pos[1] * mdSize[1]) def _getSizeFromRelative(self, size): md = self.giface.GetMapDisplay() mdSize = md.GetSize() return (size[0] * mdSize[0], size[1] * mdSize[1]) def OnDisplayUpdate(self, event): if not self.dashboardFrame: return self.dashboardFrame.show_value(event.value) def OnSubtask(self, event): self._processingSubTask = True self.LoadHandsOff() # keep scanning without hands if 'duration_handsoff' in self.configuration: t = self.configuration['duration_handsoff'] else: t = 0 wx.CallLater(t, self._subtaskStop) def _subtaskStop(self): # pause scanning self._stopScanning() if 'duration_handsoff_after' in self.configuration: t = self.configuration['duration_handsoff_after'] else: t = 0 if 'solutions' in self.tasks[self.current]: wx.CallLater(t, self.PostProcessing, onDone=self._showSolutions) else: wx.CallLater(t, self.PostProcessing, onDone=self._subtaskDone) def _showSolutions(self): ll = self.giface.GetLayerList() if self.handsoff: ll.DeleteLayer(self.handsoff) cmd = self.tasks[self.current]['solutions'][self.currentSubtask] if cmd[0] == 'd.rast': ll.AddLayer('raster', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) elif cmd[0] == 'd.vect': ll.AddLayer('vector', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) wx.CallLater(6000, self._subtaskDone) def _subtaskDone(self): # check if it was the last subTask if self.currentSubtask + 1 >= len(self.tasks[self.current]['sublayers']): # that was the last one self.timer.Stop() self._closeAdditionalWindows() self._removeAllLayers() self.settings['analyses']['file'] = '' else: # load new layers ll = self.giface.GetLayerList() for l in ll: if 'solutions' in self.tasks[self.current] and l.cmd == self.tasks[self.current]["solutions"][self.currentSubtask]: ll.DeleteLayer(l) break ll = self.giface.GetLayerList() for l in ll: if l.cmd == self.tasks[self.current]["sublayers"][self.currentSubtask]: ll.DeleteLayer(l) cmd = self.tasks[self.current]["sublayers"][self.currentSubtask + 1] if cmd[0] == 'd.rast': ll.AddLayer('raster', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) elif cmd[0] == 'd.vect': ll.AddLayer('vector', name=cmd[1].split('=')[1], checked=True, opacity=1.0, cmd=cmd) break self.currentSubtask += 1 self.scaniface.additionalParams4Analyses = {'subTask': self.currentSubtask} # update self.scaniface.filter['counter'] = 0 for each in self.giface.GetAllMapDisplays(): each.GetMapWindow().UpdateMap() self._startScanning() self.Raise() self.buttonStop.SetFocus() # now user can proceed to next task self._processingSubTask = False def OnUserStop(self, event): if self._processingSubTask: return if 'sublayers' in self.tasks[self.current]: self.OnSubtask(None) else: self.OnStop(None) def OnScanOnce(self, event): self.scaniface.resume_once = True self.scaniface.changedInput = True def _reloadAnalysisFile(self, funcPrefix): analysesFile = os.path.join(self._getTaskDir(), self.configuration['tasks'][self.current]['analyses']) try: myanalyses = imp.load_source('myanalyses', analysesFile) except Exception: return None functions = [func for func in dir(myanalyses) if func.startswith(funcPrefix)] for func in functions: exec('del myanalyses.' + func) try: myanalyses = imp.load_source('myanalyses', analysesFile) except Exception: return None functions = [func for func in dir(myanalyses) if func.startswith(funcPrefix)] return myanalyses, functions