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")
class ScattsManager: """Main controller """ 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 CleanUp(self): self.thread.Terminate() # there should be better way hot to clean up the thread # than calling the clean up function outside the thread, # which still may running self.core.CleanUp() def CleanUpDone(self): for scatt_id, scatt in self.plots.items(): if scatt['scatt']: scatt['scatt'].CleanUp() self.plots.clear() def _initSettings(self): """Initialization of settings (if not already defined) """ # initializes default settings initSettings = [ ['selection', 'sel_pol', (255, 255, 0)], ['selection', 'sel_pol_vertex', (255, 0, 0)], ['selection', 'sel_area', (0, 255, 19)], ['selection', "snap_tresh", 10], ['selection', 'sel_area_opacty', 50], ['ellipses', 'show_ellips', True], ] for init in initSettings: UserSettings.ReadSettingsFile() UserSettings.Append(dict=UserSettings.userSettings, group='scatt', key=init[0], subkey=init[1], value=init[2], overwrite=False) def SetData(self): self.iclass_conn.SetData() self.digit_conn.SetData() def SetBands(self, bands): self.busy = wx.BusyInfo(_("Loading data...")) self.data_set = False self.thread.Run(callable=self.core.CleanUp, ondone=lambda event: self.CleanUpDone()) if self.show_add_scatt_plot: show_add = True else: show_add = False self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands))) self.all_bands = bands self.region = GetRegion() ncells = self.region["rows"] * self.region["cols"] if ncells > MAX_NCELLS: del self.busy self.data_set = True return self.bands = bands[:] self.bands_info = {} valid_bands = [] for b in self.bands[:]: i = GetRasterInfo(b) self.bands_info[b] = i if i is not None: valid_bands.append(b) for i, b in enumerate(valid_bands): # name : index in core bands - # if not in core bands (not CELL type) -> index = -1 self.all_bands_to_bands[b] = i self.thread.Run(callable=self.core.SetData, bands=valid_bands, ondone=self.SetDataDone, userdata={"show_add": show_add}) def SetDataDone(self, event): del self.busy self.data_set = True todo = event.ret self.bad_bands = event.ret bands = self.core.GetBands() self.bad_rasts = event.ret self.cats_mgr.SetData() if event.userdata['show_add']: self.AddScattPlot() def GetBands(self): return self.core.GetBands() def AddScattPlot(self): if not self.data_set and self.iclass_conn: self.show_add_scatt_plot = True self.iclass_conn.SetData() self.show_add_scatt_plot = False return if not self.data_set: GError(_('No data set.')) return self.computingStarted.emit() bands = self.core.GetBands() #added_bands_ids = [] # for scatt_id in self.plots): # added_bands_ids.append[idBandsToidScatt(scatt_id)] self.digit_conn.Update() ncells = self.region["rows"] * self.region["cols"] if ncells > MAX_NCELLS: GError( _( parent=self.guiparent, mmessage=_( "Interactive Scatter Plot Tool can not be used.\n" "Number of cells (rows*cols) <%d> in current region" "is higher than maximum limit <%d>.\n\n" "You can reduce number of cells in current region using <g.region> command." % (ncells, MAX_NCELLS)))) return elif ncells > WARN_NCELLS: dlg = wx.MessageDialog( parent=self.guiparent, message=_("Number of cells (rows*cols) <%d> in current region is " "higher than recommended threshold <%d>.\n" "It is strongly advised to reduce number of cells " "in current region below recommend threshold.\n " "It can be done by <g.region> command.\n\n" "Do you want to continue using " "Interactive Scatter Plot Tool with this region?" % (ncells, WARN_NCELLS)), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING) ret = dlg.ShowModal() if ret != wx.ID_YES: return dlg = AddScattPlotDialog(parent=self.guiparent, bands=self.all_bands, check_bands_callback=self.CheckBands) if dlg.ShowModal() == wx.ID_OK: scatt_ids = [] sel_bands = dlg.GetBands() for b_1, b_2 in sel_bands: transpose = False if b_1 > b_2: transpose = True tmp_band = b_2 b_2 = b_1 b_1 = tmp_band b_1_id = self.all_bands_to_bands[self.all_bands[b_1]] b_2_id = self.all_bands_to_bands[self.all_bands[b_2]] scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands)) if scatt_id in self.plots: continue self.plots[scatt_id] = {'transpose': transpose, 'scatt': None} scatt_ids.append(scatt_id) self._addScattPlot(scatt_ids) dlg.Destroy() def CheckBands(self, b_1, b_2): bands = self.core.GetBands() added_scatts_ids = self.plots.keys() b_1_id = self.all_bands_to_bands[self.all_bands[b_1]] b_2_id = self.all_bands_to_bands[self.all_bands[b_1]] scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands)) if scatt_id in added_scatts_ids: GWarning( parent=self.guiparent, message=_( "Scatter plot with same band combination (regardless x y order) " "is already displayed.")) return False b_1_name = self.all_bands[b_1] b_2_name = self.all_bands[b_2] b_1_i = self.bands_info[b_1_name] b_2_i = self.bands_info[b_2_name] err = "" for b in [b_1_name, b_2_name]: if self.bands_info[b] is None: err += _("Band <%s> is not CELL (integer) type.\n" % b) if err: GMessage(parent=self.guiparent, message=_("Scatter plot cannot be added.\n" + err)) return False mrange = b_1_i['range'] * b_2_i['range'] if mrange > MAX_SCATT_SIZE: GWarning(parent=self.guiparent, message=_("Scatter plot cannot be added.\n" "Multiple of bands ranges <%s:%d * %s:%d = %d> " "is higher than maximum limit <%d>.\n" % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'], mrange, MAX_SCATT_SIZE))) return False elif mrange > WARN_SCATT_SIZE: dlg = wx.MessageDialog( parent=self.guiparent, message=_( "Multiple of bands ranges <%s:%d * %s:%d = %d> " "is higher than recommended limit <%d>.\n" "It is strongly advised to reduce range extend of bands" "(e. g. using r.rescale) below recommended threshold.\n\n" "Do you really want to add this scatter plot?" % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'], mrange, WARN_SCATT_SIZE)), style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING) ret = dlg.ShowModal() if ret != wx.ID_YES: return False return True def _addScattPlot(self, scatt_ids): self.render_mgr.NewRunningProcess() self.thread.Run(callable=self.core.AddScattPlots, scatt_ids=scatt_ids, ondone=self.AddScattPlotDone) def AddScattPlotDone(self, event): if not self.data_set: return scatt_ids = event.kwds['scatt_ids'] for s_id in scatt_ids: trans = self.plots[s_id]['transpose'] self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot( scatt_id=s_id, transpose=trans) self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed) self.plots[s_id]['scatt'].cursorMove.connect( lambda x, y, scatt_id: self.cursorPlotMove.emit(x=x, y=y, scatt_id=scatt_id)) if self.plot_mode: self.plots[s_id]['scatt'].SetMode(self.plot_mode) self.plots[s_id]['scatt'].ZoomToExtend() self.render_mgr.RunningProcessDone() def PlotClosed(self, scatt_id): del self.plots[scatt_id] def SetPlotsMode(self, mode): self.plot_mode = mode for scatt in six.itervalues(self.plots): if scatt['scatt']: scatt['scatt'].SetMode(mode) self.modeSet.emit(mode=mode) def ActivateSelectionPolygonMode(self, activate): self.pol_sel_mode[0] = activate for scatt in six.itervalues(self.plots): if not scatt['scatt']: continue scatt['scatt'].SetSelectionPolygonMode(activate) if not activate and self.plot_mode not in [ 'zoom', 'pan', 'zoom_extend']: self.SetPlotsMode(None) self.render_mgr.RunningProcessDone() return activate def ProcessSelectionPolygons(self, process_mode): scatts_polygons = {} for scatt_id, scatt in six.iteritems(self.plots): if not scatt['scatt']: continue coords = scatt['scatt'].GetCoords() if coords is not None: scatts_polygons[scatt_id] = coords if not scatts_polygons: return value = 1 if process_mode == 'remove': value = 0 sel_cat_id = self.cats_mgr.GetSelectedCat() if not sel_cat_id: dlg = wx.MessageDialog( parent=self.guiparent, message=_( "In order to select arrea in scatter plot, " "you have to select class first.\n\n" "There is no class yet, " "do you want to create one?"), caption=_("No class selected"), style=wx.YES_NO) if dlg.ShowModal() == wx.ID_YES: self.iclass_conn.EmptyCategories() sel_cat_id = self.cats_mgr.GetSelectedCat() if not sel_cat_id: return for scatt in six.itervalues(self.plots): if scatt['scatt']: scatt['scatt'].SetEmpty() self.computingStarted.emit() self.render_mgr.NewRunningProcess() self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id]) self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id]) self.thread.Run(callable=self.core.UpdateCategoryWithPolygons, cat_id=sel_cat_id, scatts_pols=scatts_polygons, value=value, ondone=self.SetEditCatDataDone) def SetEditCatDataDone(self, event): if not self.data_set: return self.render_mgr.RunningProcessDone() if event.exception: GError( _("Error occurred during computation of scatter plot category:\n%s"), parent=self.guiparent, showTraceback=False) cat_id = event.ret self.iclass_conn.RenderCatRast(cat_id) def SettingsUpdated(self, chanaged_setts): self.render_mgr.RenderRequest() #['ellipses', 'show_ellips'] def GetCategoriesManager(self): return self.cats_mgr