diff --git a/doc/images/lookuptablepanel.png b/doc/images/lookuptablepanel.png new file mode 100644 index 0000000000000000000000000000000000000000..524b9cf090674c2a71296c741468e5dd43f76165 Binary files /dev/null and b/doc/images/lookuptablepanel.png differ diff --git a/fsl/data/strings.py b/fsl/data/strings.py index ffd072a4f06782e657ba55f295f66586a79ea705..d15c7191a9dad291c633b5abe536d03fc636cbae 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -107,10 +107,6 @@ messages = TypeDict({ 'HistogramPanel.calcHist' : 'Calculating histogram for {} ...', - - 'LookupTablePanel.notLutOverlay' : 'Choose an overlay which ' - 'uses a lookup table', - 'LookupTablePanel.labelExists' : 'The {} LUT already contains a ' 'label with value {}', diff --git a/fsl/fsleyes/colourmaps.py b/fsl/fsleyes/colourmaps.py index e41127cb359d6fbc39c629818898958101ec06ca..4cae6337aa6f5f7539eaec38fc7c486cbeddf9c3 100644 --- a/fsl/fsleyes/colourmaps.py +++ b/fsl/fsleyes/colourmaps.py @@ -914,6 +914,16 @@ class LookupTable(props.HasProperties): if lutFile is not None: self._load(lutFile) + + + def __str__(self): + """Returns the name of this ``LookupTable``. """ + return self.name + + + def __repr__(self): + """Returns the name of this ``LookupTable``. """ + return self.name def __len__(self): diff --git a/fsl/fsleyes/controls/lookuptablepanel.py b/fsl/fsleyes/controls/lookuptablepanel.py index 3517986b77e5372cd0afebe72cc33b5864c3574f..6e66c63dea38d2b15698a5fb1ff71a2341439e01 100644 --- a/fsl/fsleyes/controls/lookuptablepanel.py +++ b/fsl/fsleyes/controls/lookuptablepanel.py @@ -1,20 +1,32 @@ #!/usr/bin/env python # -# lookuptablepanel.py - +# lookuptablepanel.py - The LookupTablePanel class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +"""This module provides the :class:`LookupTablePanel`, a *FSLeyes control* +panel which allows the user to manage lookup tables. See the +:mod:`.colourmaps` module for more details on lookup tables. + +A few other class are defined in this module, all for use by the +``LookupTablePanel``: + +.. autosummary:: + :nosignatures: + + LabelWidget + NewLutDialog + LutLabelDialog +""" + import os -import copy import logging import wx import numpy as np -import props - import pwidgets.elistbox as elistbox import fsl.fsleyes.panel as fslpanel @@ -26,82 +38,45 @@ import fsl.data.strings as strings log = logging.getLogger(__name__) +class LookupTablePanel(fslpanel.FSLEyesPanel): + """A ``LookupTablePanel`` is a :class:`.FLSEyesPanel` which allows users + to manage ``LookupTable`` instances. A ``LookupTablePanel`` looks + something like this: + .. image:: images/lookuptablepanel.png + :scale: 50% + :align: center -class LabelWidget(wx.Panel): + A ``LookupTablePanel`` allows the user to do the following: - def __init__(self, lutPanel, overlayOpts, lut, value): - wx.Panel.__init__(self, lutPanel) - - self.lutPanel = lutPanel - self.opts = overlayOpts - self.lut = lut - self.value = value - - # TODO Change the enable box to a toggle - # button with an eye icon - - self.valueLabel = wx.StaticText(self, - style=wx.ALIGN_CENTRE_VERTICAL | - wx.ALIGN_RIGHT) - self.enableBox = wx.CheckBox(self) - self.colourButton = wx.ColourPickerCtrl(self) - - self.sizer = wx.BoxSizer(wx.HORIZONTAL) - self.SetSizer(self.sizer) - self.sizer.Add(self.valueLabel, flag=wx.ALIGN_CENTRE, proportion=1) - self.sizer.Add(self.enableBox, flag=wx.ALIGN_CENTRE, proportion=1) - self.sizer.Add(self.colourButton, flag=wx.ALIGN_CENTRE, proportion=1) - - label = lut.get(value) - colour = [np.floor(c * 255.0) for c in label.colour()] - - self.valueLabel .SetLabel(str(value)) - self.colourButton.SetColour(colour) - self.enableBox .SetValue(label.enabled()) + - Add/remove labels to/from a :class:`LookupTable`. + + - Change the colour, name, and visibility of a label in a + ``LookupTable``. + + - Create a new ``LookupTable``, or copy an existing one. - self.enableBox .Bind(wx.EVT_CHECKBOX, self.__onEnable) - self.colourButton.Bind(wx.EVT_COLOURPICKER_CHANGED, self.__onColour) + - Save/load a ``LookupTable`` to/from a file. + """ + - - def __onEnable(self, ev): + def __init__(self, parent, overlayList, displayCtx): + """Create a ``LookupTablePanel``. - # Disable the LutPanel listener, otherwise - # it will recreate the label list (see - # LookupTablePanel._initLabelList) - self.lut.disableListener('labels', self.lutPanel._name) - self.lut.set(self.value, enabled=self.enableBox.GetValue()) - self.lut.enableListener('labels', self.lutPanel._name) + :arg parent: The :mod:`wx` parent object. + :arg overlayList: The :class:`.OverlayList` instance. + :arg displayCtx: The :class:`.DisplayContext` instance. + """ - def __onColour(self, ev): - - newColour = self.colourButton.GetColour() - newColour = [c / 255.0 for c in newColour] - - self.lut.disableListener('labels', self.lutPanel._name) - self.lut.set(self.value, colour=newColour) - self.lut.enableListener('labels', self.lutPanel._name) - - -class LookupTablePanel(fslpanel.FSLEyesPanel): - - def __init__(self, parent, overlayList, displayCtx): - fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) self.__controlRow = wx.Panel(self) - self.__disabledLabel = wx.StaticText(self, - style=wx.ALIGN_CENTER_VERTICAL | - wx.ALIGN_CENTER_HORIZONTAL) self.__labelList = elistbox.EditableListBox( self, style=elistbox.ELB_NO_MOVE | elistbox.ELB_EDITABLE) - self.__overlayNameLabel = wx.StaticText(self, - style=wx.ST_ELLIPSIZE_MIDDLE) - - self.__lutWidget = None + self.__lutChoice = wx.Choice(self.__controlRow) self.__newLutButton = wx.Button(self.__controlRow) self.__copyLutButton = wx.Button(self.__controlRow) self.__saveLutButton = wx.Button(self.__controlRow) @@ -113,6 +88,8 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): self.__controlRow.SetSizer(self.__controlRowSizer) self .SetSizer(self.__sizer) + self.__controlRowSizer.Add(self.__lutChoice, + flag=wx.EXPAND, proportion=1) self.__controlRowSizer.Add(self.__newLutButton, flag=wx.EXPAND, proportion=1) self.__controlRowSizer.Add(self.__copyLutButton, @@ -122,13 +99,10 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): self.__controlRowSizer.Add(self.__saveLutButton, flag=wx.EXPAND, proportion=1) - self.__sizer.Add(self.__overlayNameLabel, flag=wx.EXPAND) - self.__sizer.Add(self.__controlRow, flag=wx.EXPAND) - self.__sizer.Add(self.__disabledLabel, flag=wx.EXPAND, proportion=1) - self.__sizer.Add(self.__labelList, flag=wx.EXPAND, proportion=1) + self.__sizer.Add(self.__controlRow, flag=wx.EXPAND) + self.__sizer.Add(self.__labelList, flag=wx.EXPAND, proportion=1) # Label the labels and buttons - self.__disabledLabel.SetLabel(strings.messages[self, 'notLutOverlay']) self.__newLutButton .SetLabel(strings.labels[ self, 'newLut']) self.__copyLutButton.SetLabel(strings.labels[ self, 'copyLut']) self.__loadLutButton.SetLabel(strings.labels[ self, 'loadLut']) @@ -142,6 +116,7 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): self.__labelList.Bind(elistbox.EVT_ELB_EDIT_EVENT, self.__onLabelEdit) + self.__lutChoice .Bind(wx.EVT_CHOICE, self.__onLutChoice) self.__newLutButton .Bind(wx.EVT_BUTTON, self.__onNewLut) self.__copyLutButton.Bind(wx.EVT_BUTTON, self.__onCopyLut) self.__loadLutButton.Bind(wx.EVT_BUTTON, self.__onLoadLut) @@ -158,53 +133,24 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): self._name, self.__selectedOverlayChanged) - self.__disabledLabel.Show(False) - self.__controlRowSizer.SetMinSize(self.__calcControlRowMinSize()) - self.Layout() - self.SetMinSize(self.__sizer.GetMinSize()) - + self.__updateLutChoices() self.__selectedOverlayChanged() - - def __calcControlRowMinSize(self): - """This method calculates and returns a minimum width and height - for the control row. - - When the LookupTable is first created, there is no LUT widget - it is - created when an appropriate overlay is selected (see - :meth:`__overlayTypeChanged`). Here, we create a dummy LUT widget, and - use its best size, along with the control row button sizes, to - calculate the minimum size needed to lay out the control row. - """ - - class DummyLut(props.HasProperties): - lut = copy.copy(displayctx.LabelOpts.lut) - - dl = DummyLut() - dummyLutWidget = props.makeWidget( - self, - dl, - 'lut', - labels=lambda l: l.name) - width, height = dummyLutWidget.GetBestSize().Get() - - for btn in [self.__newLutButton, - self.__copyLutButton, - self.__saveLutButton, - self.__loadLutButton]: + # If the selected lut was not set + # via the selectedOverlayChanged + # call, we'll manually set it + if self.__selectedLut is None: + self.__setLut(fslcmaps.getLookupTables()[0]) - w, h = btn.GetBestSize().Get() - width += w - - if h > height: - height = h - - dummyLutWidget.Destroy() - - return width, height + self.Layout() + self.SetMinSize(self.__sizer.GetMinSize()) def destroy(self): + """Must be called when this ``LookupTablePanel`` is no longer needed. + Removes some property listeners, and calls the + :meth:`FSLEyesPanel.destroy` method. + """ self._overlayList.removeListener('overlays', self._name) self._displayCtx .removeListener('selectedOverlay', self._name) @@ -213,11 +159,9 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): opts = self.__selectedOpts lut = self.__selectedLut - if overlay is not None: + if overlay is not None and overlay in self._overlayList: display = self._displayCtx.getDisplay(overlay) - - display.removeListener('name', self._name) display.removeListener('overlayType', self._name) if opts is not None: @@ -227,141 +171,121 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): lut.removeListener('labels', self._name) lut.removeListener('saved', self._name) - fslpanel.FSLEyesPanel.destroy(self) - - - def __selectedOverlayChanged(self, *a): - - newOverlay = self._displayCtx.getSelectedOverlay() - - if self.__selectedOverlay == newOverlay: - return - - if self.__selectedOverlay is not None and \ - self.__selectedOverlay in self._overlayList: - - display = self._displayCtx.getDisplay(self.__selectedOverlay) - - display.removeListener('name', self._name) - display.removeListener('overlayType', self._name) + self.__selectedOverlay = None + self.__selectedOpts = None + self.__selectedLut = None - self.__selectedOverlay = newOverlay + fslpanel.FSLEyesPanel.destroy(self) - if newOverlay is not None: - display = self._displayCtx.getDisplay(newOverlay) - display.addListener('name', - self._name, - self.__overlayNameChanged) - display.addListener('overlayType', - self._name, - self.__overlayTypeChanged) + + def __updateLutChoices(self): + """Refreshes the contents of the lookup table drop down box. + See the :func:`.colourmaps.getLookupTables` function. + """ - self.__overlayNameChanged() - self.__overlayTypeChanged() + log.debug('Updating lookup table choices') + oldNames = self.__lutChoice.GetItems() + oldSelection = self.__lutChoice.GetSelection() - def __overlayNameChanged(self, *a): + luts = fslcmaps.getLookupTables() + newNames = [l.name for l in luts] - overlay = self.__selectedOverlay + try: newSelection = oldNames.index(oldNames[oldSelection]) + except: newSelection = 0 - if overlay is None: - self.__overlayNameLabel.SetLabel('') - return + self.__lutChoice.SetItems(newNames) + self.__lutChoice.SetSelection(newSelection) - display = self._displayCtx.getDisplay(overlay) + for i, lut in enumerate(luts): + self.__lutChoice.SetClientData(i, lut) - self.__overlayNameLabel.SetLabel(display.name) + def __createLabelList(self): + """Refreshes the contents of the list ``LookupTable` label list.""" - def __overlayTypeChanged(self, *a): - - if self.__lutWidget is not None: - self.__controlRowSizer.Detach(self.__lutWidget) - self.__lutWidget.Destroy() - self.__lutWidget = None - - if self.__selectedOpts is not None: - self.__selectedOpts.removeListener('lut', self._name) - self.__selectedOpts = None + log.debug('Creating lookup table label list') - overlay = self.__selectedOverlay - enabled = False - - if overlay is not None: - opts = self._displayCtx.getOpts(overlay) - - if isinstance(opts, displayctx.LabelOpts): - enabled = True - - self.__overlayNameLabel.Show( enabled) - self.__controlRow .Show( enabled) - self.__labelList .Show( enabled) - self.__disabledLabel .Show(not enabled) - - if not enabled: - self.Layout() - return + self.__labelList.Clear() - opts = self._displayCtx.getOpts(overlay) + lut = self.__selectedLut - opts.addListener('lut', self._name, self.__lutChanged) - - self.__selectedOpts = opts - self.__lutWidget = props.makeWidget( - self.__controlRow, opts, 'lut', labels=lambda l: l.name) + for i, label in enumerate(lut.labels): - self.__controlRowSizer.Insert( - 0, self.__lutWidget, flag=wx.EXPAND, proportion=1) + self.__labelList.Append(label.name()) - self.__lutChanged() + widget = LabelWidget(self, lut, label.value()) + self.__labelList.SetItemWidget(i, widget) - self.Layout() + def __setLut(self, lut): + """Updates this ``LookupTablePanel`` to display the labels for the + given ``lut`` (assumed to be a :class:`.LookupTable` instance). + + If the currently selected overlay is associated with a + :class:`.LabelOpts` instance, its :attr:`.LabelOpts.lut` property + is set to the new ``LookupTable``. + """ - def __lutChanged(self, *a): + log.debug('Selecting lut: {}'.format(lut)) if self.__selectedLut is not None: self.__selectedLut.removeListener('labels', self._name) self.__selectedLut.removeListener('saved', self._name) - self.__selecedLut = None - - opts = self.__selectedOpts + + self.__selectedLut = lut - if opts is not None: - self.__selectedLut = opts.lut + if lut is not None: + lut.addListener('labels', self._name, self.__lutLabelsChanged) + lut.addListener('saved', self._name, self.__lutSaveStateChanged) - self.__selectedLut.addListener( - 'labels', self._name, self.__initLabelList) - self.__selectedLut.addListener( - 'saved', self._name, self.__lutSaveStateChanged) + if lut is not None and self.__selectedOpts is not None: + self.__selectedOpts.disableListener('lut', self._name) + self.__selectedOpts.lut = lut + self.__selectedOpts.enableListener('lut', self._name) - self.__initLabelList() + allLuts = fslcmaps.getLookupTables() + + self.__lutChoice.SetSelection(allLuts.index(lut)) + self.__lutSaveStateChanged() + self.__createLabelList() def __lutSaveStateChanged(self, *a): + """Called when the :attr:`.LookupTable.saved` property of the + current :class:`LookupTable` instance changes. Sets the state + of the *save* button accordingly. + """ self.__saveLutButton.Enable(not self.__selectedLut.saved) - - def __initLabelList(self, *a): - self.__labelList.Clear() + def __lutLabelsChanged(self, *a): + """Called when the :attr:`.LookupTable.labels` property of + the current :class:`LookupTable` instance changes. Updates + the list of labels. + """ + self.__createLabelList() - if self.__selectedOpts is None: - return - opts = self.__selectedOpts - lut = opts.lut + def __onLutChoice(self, ev): + """Called when the user changes the selected :class:`.LookupTable` + via the lookup table drop down box. If a . + """ - for i, label in enumerate(lut.labels): + selection = self.__lutChoice.GetSelection() + lut = self.__lutChoice.GetClientData(selection) - self.__labelList.Append(label.name()) + log.debug('Lut choice: {}'.format(lut)) - widget = LabelWidget(self, opts, lut, label.value()) - self.__labelList.SetItemWidget(i, widget) + self.__setLut(lut) def __onNewLut(self, ev): + """Called when the user presses the *New LUT* button. Displays a + :class:`NewLutDialog`, prompting the user to enter a name, and then + creates and registers a new :class:`.LookupTable` instance. + """ dlg = NewLutDialog(self.GetTopLevelParent()) if dlg.ShowModal() != wx.ID_OK: @@ -373,11 +297,17 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): lut = fslcmaps.LookupTable(dlg.name) fslcmaps.registerLookupTable(lut, self._overlayList, self._displayCtx) - if self.__selectedOpts is not None: - self.__selectedOpts.lut = lut + self.__updateLutChoices() + self.__setLut(lut) def __onCopyLut(self, ev): + """Called when the user presses the *Copy LUT* button. Displays a + :class:`NewLutDialog`, prompting the user to enter a name, and then + creates and registers a new :class:`.LookupTable` instance which is + initialised with the same label as the previously selected + ``LookupTable``. + """ name = self.__selectedLut.name @@ -399,11 +329,20 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): fslcmaps.registerLookupTable(lut, self._overlayList, self._displayCtx) - if self.__selectedOpts is not None: - self.__selectedOpts.lut = lut + self.__updateLutChoices() + self.__setLut(lut) - + def __onLoadLut(self, ev): + """Called when the user presses the *Load LUT* button. Displays a + :class:`NewLutDialog`, prompting the user to enter a name, and + then a ``wx.FileDialog`, prompting the user to select a file + containing lookup table information (See the :mod:`.colourmaps` + module for more details on the file format). + + Then creates and registers a new :class:`.LookupTable` instance, + initialising it with the selected file. + """ nameDlg = NewLutDialog(self.GetTopLevelParent()) @@ -426,63 +365,193 @@ class LookupTablePanel(fslpanel.FSLEyesPanel): self._displayCtx, name) - if self.__selectedOpts is not None: - self.__selectedOpts.lut = lut + self.__updateLutChoices() + self.__setLut(lut) def __onSaveLut(self, ev): + """Called when the user presses the *Save LUT* button. Makes sure + that the current :class:`LookupTable` is saved (see the + :func:`.colourmaps.installLookupTable` function). + """ fslcmaps.installLookupTable(self.__selectedLut.name) def __onLabelAdd(self, ev): + """Called when the user pushes the *add* button on the lookup table + label list. Displays a :class:`LutLabelDialog`, prompting the user + to select a name, value and colour, and then adds a new label to the + current :class:`.LookupTable` instance. + """ dlg = LutLabelDialog(self.GetTopLevelParent()) if dlg.ShowModal() != wx.ID_OK: return - opts = self.__selectedOpts + lut = self.__selectedLut value = dlg.value name = dlg.name colour = dlg.colour[:3] colour = [c / 255.0 for c in colour] - if opts.lut.get(value) is not None: + if lut.get(value) is not None: wx.MessageBox( - strings.messages[self, 'labelExists'].format( - opts.lut.name, value), - strings.titles[ self, 'labelExists'], - wx.ICON_INFORMATION | wx.OK) + strings.messages[self, 'labelExists'].format(lut.name, value), + strings.titles[ self, 'labelExists'], (wx.ICON_INFORMATION | + wx.OK)) return log.debug('New lut label for {}: {}, {}, {}'.format( - opts.lut.name, + lut.name, value, name, colour)) - opts.lut.set(value, name=name, colour=colour) + lut.set(value, name=name, colour=colour) def __onLabelRemove(self, ev): + """Called when the user pushes the *remove* button on the lookup + table label list. Removes the selected label from the current + :class:`.LookupTable`. + """ - opts = self.__selectedOpts - value = opts.lut.labels[ev.idx].value() + lut = self.__selectedLut + value = lut.labels[ev.idx].value() - self.__selectedLut.disableListener('labels', self._name) - opts.lut.delete(value) - self.__selectedLut.enableListener('labels', self._name) + lut.disableListener('labels', self._name) + lut.delete(value) + lut.enableListener('labels', self._name) def __onLabelEdit(self, ev): + """Called when the user edits the name of a label in the lookup + table label list. + """ + + lut = self.__selectedLut + value = lut.labels[ev.idx].value() + + lut.disableListener('labels', self._name) + lut.set(value, name=ev.label) + lut.enableListener('labels', self._name) + + + def __selectedOverlayChanged(self, *a): + """Called when the :class:`OverlayList` or + :attr:`.DisplayContext.selectedOverlay` changes. + Refreshes the ``LookupTablePanel`` accordingly. + """ + + newOverlay = self._displayCtx.getSelectedOverlay() + + if self.__selectedOverlay is not None and \ + self.__selectedOverlay in self._overlayList: + + display = self._displayCtx.getDisplay(self.__selectedOverlay) + display.removeListener('overlayType', self._name) + + self.__selectedOverlay = newOverlay + + if newOverlay is not None: + + display = self._displayCtx.getDisplay(newOverlay) + display.addListener('overlayType', + self._name, + self.__overlayTypeChanged) + + self.__overlayTypeChanged() + + + def __overlayTypeChanged(self, *a): + """Called when the :attr:`.Display.overlayType` property of the + currently selected overlay changes. If the :class:`.DisplayOpts` + instance associated with the new overlay type is a :class:`.LabelOpts`, + a listener is addd to its ``lut`` property. + """ + + if self.__selectedOpts is not None: + self.__selectedOpts.removeListener('lut', self._name) + self.__selectedOpts = None + + overlay = self.__selectedOverlay + opts = None + + if overlay is not None: + opts = self._displayCtx.getOpts(overlay) + + if not isinstance(opts, displayctx.LabelOpts): + return + + opts.addListener('lut', self._name, self.__lutChanged) + + self.__selectedOpts = opts + self.__lutChanged() + self.Layout() + + + def __lutChanged(self, *a): + """Called when the :attr:`.LabelOpts.lut` property associated + with the currently selected overlay changes. + """ + self.__setLut(self.__selectedOpts.lut) + - opts = self.__selectedOpts - value = opts.lut.labels[ev.idx].value() +class LabelWidget(wx.Panel): + + def __init__(self, lutPanel, lut, value): + wx.Panel.__init__(self, lutPanel) - self.__selectedLut.disableListener('labels', self._name) - opts.lut.set(value, name=ev.label) - self.__selectedLut.enableListener('labels', self._name) + self.lutPanel = lutPanel + self.lut = lut + self.value = value + # TODO Change the enable box to a toggle + # button with an eye icon + + self.valueLabel = wx.StaticText(self, + style=wx.ALIGN_CENTRE_VERTICAL | + wx.ALIGN_RIGHT) + self.enableBox = wx.CheckBox(self) + self.colourButton = wx.ColourPickerCtrl(self) + self.sizer = wx.BoxSizer(wx.HORIZONTAL) + self.SetSizer(self.sizer) + self.sizer.Add(self.valueLabel, flag=wx.ALIGN_CENTRE, proportion=1) + self.sizer.Add(self.enableBox, flag=wx.ALIGN_CENTRE, proportion=1) + self.sizer.Add(self.colourButton, flag=wx.ALIGN_CENTRE, proportion=1) + + label = lut.get(value) + colour = [np.floor(c * 255.0) for c in label.colour()] + + self.valueLabel .SetLabel(str(value)) + self.colourButton.SetColour(colour) + self.enableBox .SetValue(label.enabled()) + + self.enableBox .Bind(wx.EVT_CHECKBOX, self.__onEnable) + self.colourButton.Bind(wx.EVT_COLOURPICKER_CHANGED, self.__onColour) + + + def __onEnable(self, ev): + + # Disable the LutPanel listener, otherwise + # it will recreate the label list (see + # LookupTablePanel._createLabelList) + self.lut.disableListener('labels', self.lutPanel._name) + self.lut.set(self.value, enabled=self.enableBox.GetValue()) + self.lut.enableListener('labels', self.lutPanel._name) + + + def __onColour(self, ev): + + newColour = self.colourButton.GetColour() + newColour = [c / 255.0 for c in newColour] + + self.lut.disableListener('labels', self.lutPanel._name) + self.lut.set(self.value, colour=newColour) + self.lut.enableListener('labels', self.lutPanel._name) + + class NewLutDialog(wx.Dialog): """A dialog which is displayed when the user chooses to create a new LUT.