def __action(self): """This method is the guts of the ``DiagnosticReportAction``. It does the following: 1. Prompts the user to select a location to save the report file. 2. Generates the report. 3. Formats the report as JSON. 4. Saves the report to the specified location. """ import wx dlg = wx.FileDialog(self.__frame, message=strings.titles[self, 'saveReport'], defaultDir=os.getcwd(), defaultFile='report.txt', style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() != wx.ID_OK: return path = dlg.GetPath() status.update('Writing diagnostic report to {}...'.format(path)) report = self.__generateReport() report = self.__formatReport(report) log.debug('Diagnostic report:\n{}'.format(report)) with open(path, 'wt') as f: f.write(report)
def addLabel(startIdx): # If the user closes this panel while the # label list is being created, wx will # complain when we try to append things # to a widget that has been destroyed. try: for labelIdx in range(startIdx, min(startIdx + blockSize, nlabels)): # A new request to re-create the list has # been made - cancel this creation chain. if self.__labelListCreateKey != myCreateKey: return label = lut[labelIdx] widget = LabelWidget(self, lut, label) self.__labelList.Append(str(label.value), clientData=label.value, extraWidget=widget) if labelIdx == nlabels - 1: status.update('Lookup table label list created.') self.Enable() self.__labelList.Enable() else: idle.idle(addLabel, labelIdx + 1) except RuntimeError: pass
def changeAtlasList(): # See comment above about # suppressing wx complaints try: filterStr = self.__regionFilter.GetValue().lower().strip() regionList.ApplyFilter(filterStr, ignoreCase=True) self.__updateAtlasState(atlasDesc) status.update(strings.messages[self, 'regionsLoaded'].format( atlasDesc.name)) log.debug('Showing region list for {} ({})'.format( atlasDesc.atlasID, id(regionList))) # Hide the currently # shown region list old = self.__regionSizer.GetItem(1).GetWindow() if old is not None: old.Show(False) regionList.Show(True) self.__regionSizer.Remove(1) self.__regionSizer.Insert(1, regionList, flag=wx.EXPAND, proportion=1) self.__regionSizer.Layout() if atlasPanelDisabled: self.__atlasPanel.enableAtlasPanel() except wx.PyDeadObjectError: pass
def onFinish(): if not fwidgets.isalive(grid): return status.update('All clusters loaded.') self.Enable() grid.Show() grid.Refresh()
def test_clearStatus(): target = MockTarget() status.setTarget(target) status.update('Status1', None) assert target.msg == 'Status1' status.clearStatus() assert target.msg == '' status.update('Status1', 0.25) assert target.msg == 'Status1' status.clearStatus() assert target.msg == ''
def load(): atlas = atlases.loadAtlas(atlasID, summary, resolution=res) # The atlas panel may be destroyed # before the atlas is loaded. if not self or self.destroyed(): return self.__loadedAtlases[atlasID, summary, res] = atlas status.update('Atlas {} loaded.'.format(atlasID)) if onLoad is not None: idle.idle(onLoad, atlas)
def test_ClearThread_die(): target = MockTarget() status.setTarget(target) # make sure the clearthread is running status.update('Status1', 1.0) time.sleep(1.1) # and can be killed status._clearThread.die() status._clearThread.clear(0.1) status._clearThread.join() status._clearThread = None # and then gets restarted again status.update('Status1', 0.25) assert target.msg == 'Status1' time.sleep(0.5) assert target.msg == ''
def test_update(): # Test without a target status.setTarget(None) status.update('Status') target = MockTarget() status.setTarget(target) assert target.msg is None status.update('Status1', None) assert target.msg == 'Status1' status.update('Status2', None) assert target.msg == 'Status2'
def test_update_timeout(): status.setTarget(None) status.clearStatus() target = MockTarget() status.setTarget(target) # Test that the message gets cleared status.update('Status1', 0.25) assert target.msg == 'Status1' time.sleep(0.5) assert target.msg == '' # If a timed update is followed by an untimed # update, the untimed one should persist status.update('Status2', 0.25) status.update('Status3', None) time.sleep(0.5) assert target.msg == 'Status3'
def __runCorrelateAction(self): """Called when this :class:`.Action` is invoked. Calculates correlation values from the voxel at the current :attr:`.DisplayContext.location` (relative to the currently selected overlay) to all other voxels, and updates the correlate overlay. The correlation calculation and overlay update is performed on a separate thread (via :meth:`.idle.run`), with a call to :meth:`calculateCorrelation`. """ # Because of the multi-threaded/asynchronous # way that this function does its job, # allowing it to be called multiple times # before prior calls have completed would be # very dangerous indeed. if self.__correlateFlag.is_set(): log.debug('Correlate action is already ' 'running - ignoring request') return # See if a correlate overlay already exists # for the currently selected overlay ovl = self.displayCtx.getSelectedOverlay() corrOvl = self.__correlateOverlays.get(ovl, None) # If not, check to see if it is a correlate # overlay that is selected and, if it is, # look up the corresponding source overlay. if corrOvl is None: if ovl in self.__overlayCorrelates: corrOvl = ovl ovl = self.__overlayCorrelates[corrOvl] # If corrOvl is still None, it means that # there is no correlate overlay for the # currently selected overlay. In this case, # we'll create a new correlate overlay and # add it to the overlay list after the # correlation values have been calculated. opts = self.displayCtx.getOpts(ovl) xyz = opts.getVoxel(vround=True) if xyz is None: return data = ovl.data[opts.index(atVolume=False)] # The correlation calculation is performed # on a separate thread. This thread then # schedules a function on idle.idle to # update the correlation overlay back on the # main thread. def calcCorr(): correlations = self.calculateCorrelation(xyz, data) # The correlation overlay is updated/ # created on the main thread. def update(): try: # A correlation overlay already # exists for the source overlay # - update its data if corrOvl is not None: corrOvl[:] = correlations # The correlation overlay hasn't # been created yet - create a # new overlay with the correlation # values. else: self.__createCorrelateOverlay(ovl, correlations) finally: fslstatus.clearStatus() self.__correlateFlag.clear() idle.idle(update) # Protect against more calls # while this job is running. self.__correlateFlag.set() fslstatus.update(strings.messages[self, 'calculating'].format(*xyz)) idle.run(calcCorr)
def __createLabelList(self): """Refreshes the contents of the class:`.LookupTable` label list, from the currently selected ``LookupTable``. """ # The label list is created asynchronously on # the wx.Idle loop, because it can take some # time for big lookup tables. In the event # that the list needs to be re-created (e.g. # the current lookup table is changed), this # attribute is used so that scheduled creation # routines (the addLabel function defined # below) can tell whether they should cancel. myCreateKey = (self.__labelListCreateKey + 1) % 65536 self.__labelListCreateKey = myCreateKey lut = self.__selectedLut nlabels = len(lut) self.__labelList.Clear() # If this is a new lut, it # won't have any labels if nlabels == 0: return # The label widgets are created via consecutive # calls to addLabel, which is scheduled on the # idle.idle loop. We create blockSize labels # in each asynchronous call. blockSize = 100 def addLabel(startIdx): # If the user closes this panel while the # label list is being created, wx will # complain when we try to append things # to a widget that has been destroyed. try: for labelIdx in range(startIdx, min(startIdx + blockSize, nlabels)): # A new request to re-create the list has # been made - cancel this creation chain. if self.__labelListCreateKey != myCreateKey: return label = lut[labelIdx] widget = LabelWidget(self, lut, label) self.__labelList.Append(str(label.value), clientData=label.value, extraWidget=widget) if labelIdx == nlabels - 1: status.update('Lookup table label list created.') self.Enable() self.__labelList.Enable() else: idle.idle(addLabel, labelIdx + 1) except RuntimeError: pass log.debug('Creating lookup table label list') status.update('Creating lookup table label list...', timeout=None) self.__labelList.Disable() self.Disable() idle.idle(addLabel, 0)
def defaultLoadFunc(s): msg = strings.messages['loadOverlays.loading'].format(s) status.update(msg)
def __reloadOverlay(self): """Reloads the currently selected overlay from disk. """ ovl = self.displayCtx.getSelectedOverlay() if ovl is None or type(ovl) != fslimage.Image: raise RuntimeError('Only Image overlays can be reloaded') index = self.overlayList.index(ovl) order = self.displayCtx.overlayOrder[:] dataSource = ovl.dataSource status.update('Reloading {}...'.format(dataSource)) # Get refs to all DisplayContexts - # the master one, and the one for # every view panel. displayCtxs = [self.displayCtx] viewPanels = self.__frame.viewPanels displayCtxs += [vp.displayCtx for vp in viewPanels] # Now get refs to all Display and # DisplayOpts instances for this # overlay. displays = [] opts = [] for dctx in displayCtxs: displays.append(self.displayCtx.getDisplay(ovl)) opts .append(self.displayCtx.getOpts( ovl)) # Turn those references into # {prop : value} dictionaries for i in range(len(displays)): d = displays[i] o = opts[ i] displayProps = d.getAllProperties()[0] optProps = o.getAllProperties()[0] displays[i] = {p : getattr(d, p) for p in displayProps} opts[ i] = {p : getattr(o, p) for p in optProps} # Now that we've got all the settings # for this overlay, we'll remove it # from the list. If removeOverlay # returns False, it probably means # the user cancelled the action. if not removeoverlay.removeOverlay(self.overlayList, self.displayCtx, ovl, 'reloadoverlay.unsaved'): return # Now we re-load the overlay, and add it # back in to the list at the same location ovl = fslimage.Image(dataSource) self.overlayList.insert(index, ovl) # Make sure the overlay is selected, # and the display order is preserved self.displayCtx.selectOverlay(ovl) self.displayCtx.overlayOrder = order # The last step is to re-apply all of the # Display/DisplayOpts settings to the # newly created Display/DisplayOpts # instances. for i, dctx in enumerate(displayCtxs): displayProps = displays[i] optProps = opts[ i] d = dctx.getDisplay(ovl) for prop, val in displayProps.items(): if not d.propertyIsEnabled(prop): continue try: setattr(d, prop, val) except props.DisabledError: continue # Get a ref to the DisplayOpts instance # after we have configured the Display, # as its overlay type may have changed # (and hence the DisplayOpts instance # may have been re-created). o = dctx.getOpts(ovl) for prop, val in optProps.items(): if not o.propertyIsEnabled(prop): continue setattr(o, prop, val) status.update('{} reloaded.'.format(dataSource))
def __onAtlasSelect(self, ev=None, atlasDesc=None): """Called when the user selects an atlas in the atlas list, or the :meth:`selectAtlas` method is called. If a region list (a list of :class:`OverlayListWidget` items for every region in the atlas, to be displayed in the region list) has not yet been created, it is created - this is done asynchronously (via the :func:`idle.idle` function), as it can take quite a long time for some of the atlases (e.g. the Talairach and Juelich). Then the region list is updated to show the regions for the newly selected atlas. """ if ev is not None: atlasDesc = ev.data regionList = self.__regionLists.get(atlasDesc.atlasID, None) atlasPanelDisabled = regionList is None # This function changes the displayed region # list. We schedule it on the wx idle loop, # so it will get called after the region list # has been populated (if it has not been # displayed before). def changeAtlasList(): # See comment above about # suppressing wx complaints try: filterStr = self.__regionFilter.GetValue().lower().strip() regionList.ApplyFilter(filterStr, ignoreCase=True) self.__updateAtlasState(atlasDesc) status.update(strings.messages[self, 'regionsLoaded'].format( atlasDesc.name)) log.debug('Showing region list for {} ({})'.format( atlasDesc.atlasID, id(regionList))) # Hide the currently # shown region list old = self.__regionSizer.GetItem(1).GetWindow() if old is not None: old.Show(False) regionList.Show(True) self.__regionSizer.Remove(1) self.__regionSizer.Insert(1, regionList, flag=wx.EXPAND, proportion=1) self.__regionSizer.Layout() if atlasPanelDisabled: self.__atlasPanel.enableAtlasPanel() except wx.PyDeadObjectError: pass if regionList is None: # The region list for this atlas has not yet been # created. So we create the list, and then create # a widget for every region in the atlas. Some of # the atlases (Juelich and Talairach in particular) # have a large number of regions, so we create the # widgets asynchronously on the wx idle loop. regionList = elistbox.EditableListBox( self.__regionPanel, style=(elistbox.ELB_NO_ADD | elistbox.ELB_NO_REMOVE | elistbox.ELB_NO_MOVE)) regionList.Show(False) self.__regionLists[atlasDesc.atlasID] = regionList # Add blockSize labels, starting from label[i], # to the region list. Then, if necessary, # schedule more labels be added, starting from # label[i + blockSize]. blockSize = 20 nlabels = len(atlasDesc.labels) def addToRegionList(start): # If the user kills this panel while # the region list is being updated, # suppress wx complaints. try: for i in range(start, min(start + blockSize, nlabels)): label = atlasDesc.labels[i] widget = OverlayListWidget(regionList, atlasDesc.atlasID, self.__atlasPanel, self, label.index) regionList.Append(label.name, extraWidget=widget) if i < nlabels - 1: idle.idle(addToRegionList, i + 1) else: idle.idle(changeAtlasList) except wx.PyDeadObjectError: pass log.debug('Creating region list for {} ({})'.format( atlasDesc.atlasID, id(regionList))) status.update(strings.messages[self, 'loadRegions'].format( atlasDesc.name), timeout=None) # Schedule addToRegionList on the # wx idle loop for the first region. # The function will recursively # schedule itself to run for subsequent # regions. # # Disable the panel while this is # occurring. atlasPanelDisabled = True self.__atlasPanel.enableAtlasPanel(False) idle.idle(addToRegionList, 0) else: idle.idle(changeAtlasList)
def __doScreenshot(self): """Capture a screenshot. Prompts the user to select a file to save the screenshot to, and then calls the :func:`screenshot` function. """ lastDirSetting = 'fsleyes.actions.screenshot.lastDir' # Ask the user where they want # the screenshot to be saved fromDir = fslsettings.read(lastDirSetting, os.getcwd()) # We can get a list of supported output # types via a matplotlib figure object fig = plt.figure() fmts = fig.canvas.get_supported_filetypes() # Default to png if # it is available if 'png' in fmts: fmts = [('png', fmts['png'])] + \ [(k, v) for k, v in fmts.items() if k != 'png'] else: fmts = list(fmts.items()) wildcard = [ '[*.{}] {}|*.{}'.format(fmt, desc, fmt) for fmt, desc in fmts ] wildcard = '|'.join(wildcard) filename = 'screenshot' dlg = wx.FileDialog(self.__panel, message=strings.messages[self, 'screenshot'], wildcard=wildcard, defaultDir=fromDir, defaultFile=filename, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() != wx.ID_OK: return filename = dlg.GetPath() # Make the dialog go away before # the screenshot gets taken dlg.Close() dlg.Destroy() wx.GetApp().Yield() # Show an error if the screenshot # function raises an error doScreenshot = status.reportErrorDecorator( strings.titles[self, 'error'], strings.messages[self, 'error'])(screenshot) # We do the screenshot asynchronously, # to make sure it is performed on # the main thread, during idle time idle.idle(doScreenshot, self.__panel, filename) status.update(strings.messages[self, 'pleaseWait'].format(filename)) fslsettings.write(lastDirSetting, op.dirname(filename))
def makeFrame(namespace, displayCtx, overlayList, splash): """Creates the *FSLeyes* interface. This function does the following: 1. Creates the :class:`.FSLeyesFrame` the top-level frame for ``fsleyes``. 2. Configures the frame according to the command line arguments (e.g. ortho or lightbox view). 3. Destroys the splash screen that was created by the :func:`context` function. :arg namespace: Parsed command line arguments, as returned by :func:`parseArgs`. :arg displayCtx: The :class:`.DisplayContext`, as created and returned by :func:`makeDisplayContext`. :arg overlayList: The :class:`.OverlayList`, as created and returned by :func:`makeDisplayContext`. :arg splash: The :class:`.FSLeyesSplash` frame. :returns: the :class:`.FSLeyesFrame` that was created. """ import fsl.utils.idle as idle import fsleyes_widgets.utils.status as status import fsleyes.parseargs as parseargs import fsleyes.frame as fsleyesframe import fsleyes.displaycontext as fsldisplay import fsleyes.layouts as layouts import fsleyes.views.canvaspanel as canvaspanel # Set up the frame scene (a.k.a. layout) # The scene argument can be: # # - The name of a saved (or built-in) layout # # - None, in which case the default or previous # layout is restored, unless a custom script # has been provided. script = namespace.runscript scene = namespace.scene # If a scene/layout or custom script # has not been specified, the default # behaviour is to restore the previous # frame layout. restore = (scene is None) and (script is None) status.update('Creating FSLeyes interface...') frame = fsleyesframe.FSLeyesFrame(None, overlayList, displayCtx, restore, True, fontSize=namespace.fontSize) # Allow files to be dropped # onto FSLeyes to open them dt = fsleyesframe.OverlayDropTarget(overlayList, displayCtx) frame.SetDropTarget(dt) # Make sure the new frame is shown # before destroying the splash screen frame.Show(True) frame.Refresh() frame.Update() # In certain instances under Linux/GTK, # closing the splash screen will crash # the application. No idea why. So we # leave the splash screen hidden, but # not closed, and close it when the main # frame is closed. This also works under # OSX. splash.Hide() splash.Refresh() splash.Update() def onFrameDestroy(ev): ev.Skip() # splash screen may already # have been destroyed try: splash.Close() except Exception: pass frame.Bind(wx.EVT_WINDOW_DESTROY, onFrameDestroy) status.update('Setting up scene...') # Set the default SceneOpts.performance # level so that all created SceneOpts # instances will default to it if namespace.performance is not None: fsldisplay.SceneOpts.performance.setAttribute(None, 'default', namespace.performance) # If a layout has been specified, # we load the layout if namespace.scene is not None: layouts.loadLayout(frame, namespace.scene) # Apply any view-panel specific arguments viewPanels = frame.viewPanels for viewPanel in viewPanels: if not isinstance(viewPanel, canvaspanel.CanvasPanel): continue displayCtx = viewPanel.displayCtx viewOpts = viewPanel.sceneOpts parseargs.applySceneArgs(namespace, overlayList, displayCtx, viewOpts) # If a script has been specified, we run # the script. This has to be done on the # idle loop, because overlays specified # on the command line are loaded on the # idle loop. Therefore, if we schedule the # script on idle (which is a queue), the # script can assume that all overlays have # already been loaded. from fsleyes.actions.runscript import RunScriptAction if script is not None: idle.idle(frame.menuActions[RunScriptAction], script) return frame
def loadAtlas(self, atlasID, summary, onLoad=None, onError=None, matchResolution=True): """Loads the atlas image with the specified ID. The atlas is loaded asynchronously (via the :mod:`.idle` module), as it can take some time. Use the `onLoad` argument if you need to do something when the atlas has been loaded. :arg onLoad: Optional. A function which is called when the atlas has been loaded, and which is passed the loaded :class:`.Atlas` image. :arg onError: Optional. A function which is called if the atlas loading job raises an error. Passed the ``Exception`` that was raised. :arg matchResolution: If ``True`` (the default), the version of the atlas with the most suitable resolution, based on the current contents of the :class:`.OverlayList`, is loaded. See the :func:`.atlases.loadAtlas` function for details on the other arguments. """ # Get the atlas description, and the # most suitable resolution to load. desc = atlases.getAtlasDescription(atlasID) res = self.__getSuitableResolution(desc, matchResolution) if desc.atlasType == 'label': summary = True atlas = self.__loadedAtlases.get((atlasID, summary, res), None) if atlas is None: log.debug('Loading atlas {}/{}'.format( atlasID, 'label' if summary else 'prob')) status.update('Loading atlas {}...'.format(atlasID), timeout=None) def load(): # the panel might get destroyed # before this function is called if self.destroyed: return atlas = atlases.loadAtlas(atlasID, summary, resolution=res) # The atlas panel may be destroyed # before the atlas is loaded. if not self or self.destroyed: return self.__loadedAtlases[atlasID, summary, res] = atlas status.update('Atlas {} loaded.'.format(atlasID)) if onLoad is not None: idle.idle(onLoad, atlas) idle.run(load, onError=onError) # If the atlas has already been loaded, # pass it straight to the onload function elif onLoad is not None: onLoad(atlas)
def __genClusterGrid(self, overlay, featImage, contrast, clusters): """Creates and returns a :class:`.WidgetGrid` which contains the given list of clusters, which are related to the given contrast. .. note:: This method assumes that the given ``overlay`` is an :class:`.Image` which has the same voxel dimensions as, and shares the the same world coordinate system as the ``featImage``. :arg overlay: The overlay for which clusters are currently being displayed. :arg featImage: The :class:`.FEATImage` to which the clusters are related. :arg contrast: The (0-indexed) number of the contrast to which the clusters are related. :arg clusters: A sequence of objects, each representing one cluster. See the :meth:`.FEATImage.clusterResults` method. """ cols = { 'index': 0, 'nvoxels': 1, 'p': 2, 'logp': 3, 'zmax': 4, 'zmaxcoords': 5, 'zcogcoords': 6, 'copemax': 7, 'copemaxcoords': 8, 'copemean': 9 } grid = widgetgrid.WidgetGrid(self) conName = featImage.contrastNames()[contrast] opts = self.displayCtx.getOpts(overlay) # We hide the grid and disable # this panle while the grid is # being created. grid.Hide() self.Disable() grid.SetGridSize(len(clusters), 10) grid.ShowRowLabels(False) grid.ShowColLabels(True) for col, i in cols.items(): grid.SetColLabel(i, strings.labels[self, col]) def makeCoordButton(coords): label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords)) btn = wx.Button(grid, label=six.u('\u2192'), style=wx.BU_EXACTFIT) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(label, flag=wx.EXPAND, proportion=1) sizer.Add(btn) def onClick(ev): dloc = opts.transformCoords([coords], 'voxel', 'display')[0] self.displayCtx.location = dloc btn.Bind(wx.EVT_BUTTON, onClick) return sizer # Creating all of the widgets could # take a bit of time, so we'll # do it asynchronously via idle.idle # display a message while doing so. status.update(strings.messages[self, 'loadingCluster'].format( contrast + 1, conName), timeout=None) def addCluster(i, clust): if not fwidgets.isalive(grid): return zmaxbtn = makeCoordButton((clust.zmaxx, clust.zmaxy, clust.zmaxz)) zcogbtn = makeCoordButton((clust.zcogx, clust.zcogy, clust.zcogz)) copemaxbtn = makeCoordButton( (clust.copemaxx, clust.copemaxy, clust.copemaxz)) def fmt(v): return '{}'.format(v) grid.SetText(i, cols['index'], fmt(clust.index)) grid.SetText(i, cols['nvoxels'], fmt(clust.nvoxels)) grid.SetText(i, cols['p'], fmt(clust.p)) grid.SetText(i, cols['logp'], fmt(clust.logp)) grid.SetText(i, cols['zmax'], fmt(clust.zmax)) grid.SetWidget(i, cols['zmaxcoords'], zmaxbtn) grid.SetWidget(i, cols['zcogcoords'], zcogbtn) grid.SetText(i, cols['copemax'], fmt(clust.copemax)) grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn) grid.SetText(i, cols['copemean'], fmt(clust.copemean)) # Refresh the grid widget when all # clusters have been added. def onFinish(): if not fwidgets.isalive(grid): return status.update('All clusters loaded.') self.Enable() grid.Show() grid.Refresh() for i, clust in enumerate(clusters): idle.idle(addCluster, i, clust) idle.idle(onFinish) return grid
def applyLayout(frame, name, layout, message=None): """Applies the given serialised layout string to the given :class:`.FSLeyesFrame`. :arg frame: The :class:`.FSLeyesFrame` instance. :arg name: The layout name. :arg layout: The serialised layout string. :arg message: A message to display (using the :mod:`.status` module). """ import fsleyes.views.canvaspanel as canvaspanel layout = deserialiseLayout(layout) frameChildren = layout[0] frameLayout = layout[1] vpChildrens = layout[2] vpLayouts = layout[3] vpPanelProps = layout[4] vpSceneProps = layout[5] # Show a message while re-configuring the frame if message is None: message = strings.messages['layout.applyingLayout'].format( strings.layouts.get(name, name)) status.update(message) # Clear all existing view # panels from the frame frame.removeAllViewPanels() # Add all of the view panels # specified in the layout for vp in frameChildren: log.debug('Adding view panel {} to frame'.format(vp.__name__)) frame.addViewPanel(vp) # Apply the layout to those view panels frame.auiManager.LoadPerspective(frameLayout) # For each view panel, add all of the # control panels, and lay them out viewPanels = frame.viewPanels for i in range(len(viewPanels)): vp = viewPanels[i] children = vpChildrens[i] vpLayout = vpLayouts[i] panelProps = vpPanelProps[i] sceneProps = vpSceneProps[i] for child in children: log.debug('Adding control panel {} to {}'.format( child.__name__, type(vp).__name__)) _addControlPanel(vp, child) vp.auiManager.LoadPerspective(vpLayout) # Apply saved property values # to the view panel. for name, val in panelProps.items(): log.debug('Setting {}.{} = {}'.format( type(vp).__name__, name, val)) vp.deserialise(name, val) # And, if it is a CanvasPanel, # to its SceneOpts instance. if isinstance(vp, canvaspanel.CanvasPanel): opts = vp.sceneOpts for name, val in sceneProps.items(): log.debug('Setting {}.{} = {}'.format( type(opts).__name__, name, val)) opts.deserialise(name, val)
def __histPropsChanged(self, *a): """Called internally, and when any histogram settings change. Re-calculates the histogram data. """ log.debug('Calculating histogram for ' 'overlay {}'.format(self.overlay.name)) status.update('Calculating histogram for ' 'overlay {}'.format(self.overlay.name)) if np.isclose(self.dataRange.xhi, self.dataRange.xlo): self.__xdata = np.array([]) self.__ydata = np.array([]) self.__nvals = 0 return if self.ignoreZeros: if self.includeOutliers: data = self.__nonZeroData else: data = self.__clippedNonZeroData else: if self.includeOutliers: data = self.__finiteData else: data = self.__clippedFiniteData # Figure out the number of bins to use if self.autoBin: nbins = autoBin(data, self.dataRange.x) else: nbins = self.nbins # nbins is unclamped, but # we don't allow < 10 if nbins < 10: nbins = 10 # Update the nbins property with props.skip(self, 'nbins', self.__name): self.nbins = nbins # We cache calculated bins and counts # for each combination of parameters, # as histogram calculation can take # time. hrange = (self.dataRange.xlo, self.dataRange.xhi) drange = (self.dataRange.xmin, self.dataRange.xmax) histkey = ((self.__opts.volumeDim, self.__opts.volume), self.includeOutliers, hrange, drange, self.nbins) cached = self.__histCache.get(histkey, None) if cached is not None: histX, histY, nvals = cached else: histX, histY, nvals = histogram(data, self.nbins, hrange, drange, self.includeOutliers, True) self.__histCache.put(histkey, (histX, histY, nvals)) self.__xdata = histX self.__ydata = histY self.__nvals = nvals status.update('Histogram for {} calculated.'.format(self.overlay.name)) log.debug('Calculated histogram for overlay ' '{} (number of values: {}, number ' 'of bins: {})'.format(self.overlay.name, self.__nvals, self.nbins))