From fb12da26adeb5848741c301bc9a2c5b7cad13d28 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Mon, 23 Feb 2015 21:33:36 +0000 Subject: [PATCH] In process of replacing ImageDisplayPanel with a nicer-looking toolbar which does the same thing. --- fsl/data/strings.py | 4 +- fsl/fslview/controls/__init__.py | 14 +- fsl/fslview/controls/imagedisplaytoolbar.py | 195 ++++++++++++++++++++ fsl/fslview/panel.py | 63 ++++--- fsl/fslview/views/canvaspanel.py | 45 +++-- 5 files changed, 274 insertions(+), 47 deletions(-) create mode 100644 fsl/fslview/controls/imagedisplaytoolbar.py diff --git a/fsl/data/strings.py b/fsl/data/strings.py index c96ba7042..d8247cbf7 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -55,7 +55,7 @@ titles = TypeDict({ 'ImageListPanel' : 'Image list', 'AtlasPanel' : 'Atlases', 'LocationPanel' : 'Location', - 'ImageDisplayPanel' : 'Display', + 'ImageDisplayToolBar' : 'Display', }) @@ -96,6 +96,8 @@ labels = TypeDict({ 'LocationPanel.volumeLabel' : 'Volume', 'LocationPanel.spaceLabel' : 'Space', 'LocationPanel.outOfBounds' : 'Out of bounds', + + 'ImageDisplayToolBar.more' : 'More settings', }) diff --git a/fsl/fslview/controls/__init__.py b/fsl/fslview/controls/__init__.py index bc0cfb1e8..6f03570cd 100644 --- a/fsl/fslview/controls/__init__.py +++ b/fsl/fslview/controls/__init__.py @@ -19,14 +19,16 @@ dynamic lookup of all :class:`~fsl.fslview.panel.ControlPanel` types. import fsl.fslview.panel as fslpanel import locationpanel import imagelistpanel -import imagedisplaypanel +# import imagedisplaypanel +import imagedisplaytoolbar import atlaspanel -FSLViewPanel = fslpanel .FSLViewPanel -LocationPanel = locationpanel .LocationPanel -ImageListPanel = imagelistpanel .ImageListPanel -ImageDisplayPanel = imagedisplaypanel.ImageDisplayPanel -AtlasPanel = atlaspanel. AtlasPanel +FSLViewPanel = fslpanel .FSLViewPanel +LocationPanel = locationpanel .LocationPanel +ImageListPanel = imagelistpanel .ImageListPanel +# ImageDisplayPanel = imagedisplaypanel.ImageDisplayPanel +ImageDisplayToolBar = imagedisplaytoolbar.ImageDisplayToolBar +AtlasPanel = atlaspanel. AtlasPanel def listControlPanels(): diff --git a/fsl/fslview/controls/imagedisplaytoolbar.py b/fsl/fslview/controls/imagedisplaytoolbar.py new file mode 100644 index 000000000..234b8efe9 --- /dev/null +++ b/fsl/fslview/controls/imagedisplaytoolbar.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# imagedisplaytoolbar.py - A toolbar which shows display control options for +# the currently selected image. +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> + +"""A :class:`wx.panel` which shows display control optionns for the currently +selected image - see :attr:`fsl.data.image.ImageList.selectedImage`. +""" + +import logging +log = logging.getLogger(__name__) + + +import wx + +import props + +import fsl.fslview.panel as fslpanel +import fsl.data.strings as strings +import imageselectpanel as imageselect + + +class ImageDisplayToolBar(fslpanel.FSLViewToolBar): + + def __init__(self, parent, imageList, displayCtx): + fslpanel.FSLViewToolBar.__init__(self, parent, imageList, displayCtx) + + self._imageSelect = imageselect.ImageSelectPanel( + self, imageList, displayCtx, False) + + self._moreButton = wx.Button( + self, label=strings.labels['ImageDisplayToolBar.more']) + + self._sepLine = wx.StaticLine( + self, size=(-1, 25), style=wx.LI_VERTICAL) + + self._displayPanels = {} + self._optsPanels = {} + + self._currentDisplayPanel = None + self._currentOptsPanel = None + + self._sizer = wx.BoxSizer(wx.HORIZONTAL) + self._sizer.Add(self._imageSelect) + self._sizer.Add((0, 0)) + self._sizer.Add(self._sepLine) + self._sizer.Add((0, 0)) + self._sizer.Add(self._moreButton) + + self.SetSizer(self._sizer) + + self._displayCtx.addListener( + 'selectedImage', + self._name, + self._selectedImageChanged) + + self._selectedImageChanged() + + + def destroy(self): + """Deregisters property listeners. """ + fslpanel.FSLViewToolBar.destroy(self) + + self._imageSelect.destroy() + + self._displayCtx.removeListener('selectedImage', self._name) + + for image in self._imageList: + image.removeListener('imageType', self._name) + + + def _imageTypeChanged(self, value, valid, image, name): + + oldOptsPanel = self._optsPanels.get(image, None) + newOptsPanel = self._makeOptsPanel(image) + + self._optsPanels[image] = newOptsPanel + + if oldOptsPanel is not None: + current = self._sizer.GetItem(3).GetWindow() + if current is oldOptsPanel: + self._sizer.Detach(3) + self._sizer.Insert(3, newOptsPanel, flag=wx.EXPAND) + oldOptsPanel.Destroy() + + + def _selectedImageChanged(self, *a): + """Called when the :attr:`~fsl.data.image.ImageList.selectedImage` + index changes. Ensures that the correct display panel is visible. + """ + + currDispPanel = self._sizer.GetItem(1).GetWindow() + currOptsPanel = self._sizer.GetItem(3).GetWindow() + + self._sizer.Detach(3) + self._sizer.Detach(1) + + if currDispPanel is not None: currDispPanel.Show(False) + if currOptsPanel is not None: currOptsPanel.Show(False) + + image = self._displayCtx.getSelectedImage() + + if image is None: + self._sizer.Insert(1, (0, 0)) + self._sizer.Insert(3, (0, 0)) + else: + + displayPanel = self._displayPanels.get(image, None) + optsPanel = self._optsPanels .get(image, None) + + if displayPanel is None: + displayPanel = self._makeDisplayPanel(image) + self._displayPanels[image] = displayPanel + + if optsPanel is None: + optsPanel = self._makeOptsPanel(image) + self._optsPanels[image] = optsPanel + + image.addListener( + 'imageType', + self._name, + self._imageTypeChanged, + overwrite=True) + + self._sizer.Insert(1, displayPanel, flag=wx.EXPAND) + self._sizer.Insert(3, optsPanel, flag=wx.EXPAND) + + displayPanel.Show(True) + optsPanel .Show(True) + + self.Layout() + + + def _makeDisplayPanel(self, image): + """Creates and returns panel containing widgets allowing + the user to edit the display properties of the given + :class:`~fsl.data.image.Image` instance. + """ + + display = self._displayCtx.getDisplayProperties(image) + panel = wx.Panel(self) + + enabled = props.makeWidget(panel, display, 'enabled') + name = props.makeWidget(panel, display, 'name') + imageType = props.makeWidget(panel, display, 'imageType') + + alpha = wx.Slider(panel, value=100, minValue=0, maxValue=100) + brightness = wx.Slider(panel, value=50, minValue=0, maxValue=100) + contrast = wx.Slider(panel, value=50, minValue=0, maxValue=100) + + props.bindWidget(alpha, display, 'alpha', wx.EVT_SLIDER) + props.bindWidget(brightness, display, 'brightness', wx.EVT_SLIDER) + props.bindWidget(contrast, display, 'contrast', wx.EVT_SLIDER) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + panel.SetSizer(sizer) + + sizer.Add(enabled, flag=wx.EXPAND) + sizer.Add(name, flag=wx.EXPAND) + sizer.Add(imageType, flag=wx.EXPAND) + sizer.Add(alpha, flag=wx.EXPAND) + sizer.Add(brightness, flag=wx.EXPAND) + sizer.Add(contrast, flag=wx.EXPAND) + + panel.Layout() + + return panel + + + def _makeOptsPanel(self, image): + + display = self._displayCtx.getDisplayProperties(image) + opts = display.getDisplayOpts() + panel = wx.Panel(self) + widgets = [] + + if display.imageType == 'volume': + widgets.append(props.makeWidget(panel, opts, 'cmap')) + + elif display.imageType == 'mask': + widgets.append(props.makeWidget(panel, opts, 'colour')) + + elif display.imageType == 'vector': + widgets.append(props.makeWidget(panel, opts, 'displayMode')) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + panel.SetSizer(sizer) + + for w in widgets: + sizer.Add(w) + + panel.Layout() + return panel diff --git a/fsl/fslview/panel.py b/fsl/fslview/panel.py index 38e2bae22..9cba35b1d 100644 --- a/fsl/fslview/panel.py +++ b/fsl/fslview/panel.py @@ -4,22 +4,23 @@ # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""This module provides a single class - the :class:`FSLViewPanel`. - +"""This module provides two classes - the :class:`FSLViewPanel`, and the +:class:`FSLViewToolBar`. A :class:`FSLViewPanel` object is a :class:`wx.Panel` which provides some sort of view of a collection of :class:`~fsl.data.image.Image` objects, -contained within an :class:`~fsl.data.image.ImageList`. - +contained within an :class:`~fsl.data.image.ImageList`. Similarly, a +:class:`FSLViewToolBar` is a :class:`wx.lib.agw.aui.AuiToolBar` which +provides some sort of control over the view. -A :class:`ViewPanel` is also a :class:`~fsl.fslview.actions.ActionProvider` -instance - any actions which are specified during construction are exposed -to the user. Furthermore, any display configuration options which should be -made available available to the user should be added as -:class:`~props.PropertyBase` attributes of the :class:`FSLViewPanel` -subclass. +Instances of these classes are also +:class:`~fsl.fslview.actions.ActionProvider` instances - any actions which +are specified during construction may be exposed to the user. Furthermore, +any display configuration options which should be made available available +to the user should be added as :class:`~props.PropertyBase` attributes of +the :class:`FSLViewPanel` subclass. -See the following for examples of :class:`ViewPanel` subclasses: +See the following for examples of :class:`FSLViewPanel` subclasses: - :class:`~fsl.fslview.views.OrthoPanel` - :class:`~fsl.fslview.views.LightBoxPanel` @@ -44,8 +45,7 @@ import displaycontext log = logging.getLogger(__name__) - -class FSLViewPanel(wx.Panel, actions.ActionProvider): +class _FSLViewPanel(actions.ActionProvider): """Superclass for FSLView view panels. A :class:`ViewPanel` has the following attributes, intended to be @@ -64,14 +64,11 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider): def __init__(self, - parent, imageList, displayCtx, actionz=None): """Create a :class:`ViewPanel`. - :arg parent: The :mod:`wx` parent object of this panel. - :arg imageList: A :class:`~fsl.data.image.ImageList` instance. :arg displayCtx: A :class:`~fsl.fslview.displaycontext.DisplayContext` @@ -82,7 +79,6 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider): :class:`~fsl.fslview.actions.ActionProvider`). """ - wx.Panel.__init__(self, parent) actions.ActionProvider.__init__(self, imageList, displayCtx, actionz) if not isinstance(imageList, fslimage.ImageList): @@ -99,9 +95,6 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider): self._name = '{}_{}'.format(self.__class__.__name__, id(self)) self.__destroyed = False - import fsl.fslview.layouts as layouts - self.SetMinSize(layouts.minSizes.get(self, (-1, -1))) - def destroy(self): """This method must be called by whatever is managing this @@ -138,11 +131,37 @@ class FSLViewPanel(wx.Panel, actions.ActionProvider): log.warning('The {}.destroy() method has not been called ' '- unless the application is shutting down, ' 'this is probably a bug!'.format(type(self).__name__)) - - wx.Panel .__del__(self) + actions.ActionProvider.__del__(self) +class FSLViewPanel(_FSLViewPanel, wx.Panel): + def __init__(self, parent, imageList, displayCtx, actionz=None): + wx.Panel.__init__(self, parent) + _FSLViewPanel.__init__(self, imageList, displayCtx, actionz) + + import fsl.fslview.layouts as layouts + self.SetMinSize(layouts.minSizes.get(self, (-1, -1))) + + + def __del__(self): + wx.Panel .__del__(self) + _FSLViewPanel.__del__(self) + +class FSLViewToolBar(_FSLViewPanel, wx.Panel): + def __init__(self, parent, imageList, displayCtx, actionz=None): + wx.Panel.__init__(self, parent) + _FSLViewPanel.__init__(self, imageList, displayCtx, actionz) + + import fsl.fslview.layouts as layouts + self.SetMinSize(layouts.minSizes.get(self, (-1, -1))) + + + def __del__(self): + wx.Panel .__del__(self) + _FSLViewPanel.__del__(self) + + class ConfigPanel(wx.Panel): def __init__(self, parent, target, layout=None): diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py index 4c7a3fa21..b4d5c286b 100644 --- a/fsl/fslview/views/canvaspanel.py +++ b/fsl/fslview/views/canvaspanel.py @@ -24,15 +24,16 @@ import wx.lib.agw.aui as aui import props -import fsl.data.strings as strings -import fsl.fslview.panel as fslpanel -import fsl.fslview.profiles as profiles -import fsl.fslview.displaycontext as displayctx -import fsl.fslview.controls.imagelistpanel as imagelistpanel -import fsl.fslview.controls.imagedisplaypanel as imagedisplaypanel -import fsl.fslview.controls.locationpanel as locationpanel -import fsl.fslview.controls.atlaspanel as atlaspanel -import colourbarpanel +import fsl.data.strings as strings +import fsl.fslview.panel as fslpanel +import fsl.fslview.profiles as profiles +import fsl.fslview.displaycontext as displayctx +import fsl.fslview.controls.imagelistpanel as imagelistpanel +# import fsl.fslview.controls.imagedisplaypanel as imagedisplaypanel +import fsl.fslview.controls.imagedisplaytoolbar as imagedisplaytoolbar +import fsl.fslview.controls.locationpanel as locationpanel +import fsl.fslview.controls.atlaspanel as atlaspanel +import colourbarpanel def _takeScreenShot(imageList, displayCtx, canvas): @@ -189,7 +190,7 @@ class CanvasPanel(fslpanel.FSLViewPanel): 'toggleAtlasPanel' : lambda *a: self.toggleControlPanel( atlaspanel.AtlasPanel, *a), 'toggleDisplayProperties' : lambda *a: self.toggleControlPanel( - imagedisplaypanel.ImageDisplayPanel, *a), + imagedisplaytoolbar.ImageDisplayToolBar, *a), 'toggleLocationPanel' : lambda *a: self.toggleControlPanel( locationpanel.LocationPanel, *a), 'toggleCanvasProperties' : lambda *a: self.toggleConfigPanel( @@ -370,15 +371,23 @@ class CanvasPanel(fslpanel.FSLViewPanel): self.__auiMgr.DetachPane(window) self.__onPaneClose(None, window) else: - window = panelType(self, self._imageList, self._displayCtx) + window = panelType(self, self._imageList, self._displayCtx) + + if isinstance(window, fslpanel.FSLViewPanel): + paneInfo = aui.AuiPaneInfo() \ + .Top() \ + .MinSize(window.GetMinSize()) \ + .BestSize(window.GetBestSize()) \ + .Caption(strings.titles[window]) + + elif isinstance(window, fslpanel.FSLViewToolBar): + paneInfo = aui.AuiPaneInfo() \ + .Top() \ + .ToolbarPane() \ + .Caption(strings.titles[window]) + - self.__auiMgr.AddPane( - window, - aui.AuiPaneInfo() - .Top() - .MinSize(window.GetMinSize()) - .BestSize(window.GetBestSize()) - .Caption(strings.titles[window])) + self.__auiMgr.AddPane(window, paneInfo) self.__controlPanels[panelType] = window self.__auiMgr.Update() -- GitLab