Source code for fsleyes.controls.lookuptablepanel

#
# 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 classes and functions are defined in this module, all for use by
the ``LookupTablePanel``:

.. autosummary::
   :nosignatures:

   promptForLutName
   LabelWidget
   LutLabelDialog
"""


import            os
import os.path as op
import            logging

import wx

import fsl.utils.idle                as idle
import fsl.utils.settings            as fslsettings
import fsl.data.melodicimage         as fslmelimage

import fsleyes_props                 as props
import fsleyes_widgets.elistbox      as elistbox
import fsleyes_widgets.utils.status  as status

import fsleyes.controls.controlpanel as ctrlpanel
import fsleyes.displaycontext        as displayctx
import fsleyes.colourmaps            as fslcmaps
import fsleyes.strings               as strings



log = logging.getLogger(__name__)


[docs]class LookupTablePanel(ctrlpanel.ControlPanel): """A ``LookupTablePanel`` is a :class:`.ControlPanel` which allows users to manage ``LookupTable`` instances. A ``LookupTablePanel`` looks something like this: .. image:: images/lookuptablepanel.png :scale: 50% :align: center A ``LookupTablePanel`` allows the user to do the following: - 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. - Save/load a ``LookupTable`` to/from a file. The ``LookupTablePanel`` keeps track of the currently selected overlay (see the :attr:`.DisplayContext.selectedOverlay` property). If the overlay is associated with a :class:`.LabelOpts` instance, its :attr:`.Labelopts.lut` property will be updated when the :class:`LookupTable` is changed through the ``LookupTablePanel``, and vice versa. """
[docs] def __init__(self, parent, overlayList, displayCtx, frame): """Create a ``LookupTablePanel``. :arg parent: The :mod:`wx` parent object. :arg overlayList: The :class:`.OverlayList` instance. :arg displayCtx: The :class:`.DisplayContext` instance. :arg frame: The :class:`.FSLeyesFrame` instance. """ ctrlpanel.ControlPanel.__init__( self, parent, overlayList, displayCtx, frame) self.__controlCol = wx.Panel(self) self.__labelList = elistbox.EditableListBox( self, style=(elistbox.ELB_NO_MOVE | elistbox.ELB_NO_ADD | elistbox.ELB_NO_REMOVE | elistbox.ELB_WIDGET_RIGHT)) self.__lutChoice = wx.Choice(self.__controlCol) self.__selAllButton = wx.Button(self.__controlCol) self.__selNoneButton = wx.Button(self.__controlCol) self.__addLabelButton = wx.Button(self.__controlCol) self.__rmLabelButton = wx.Button(self.__controlCol) self.__newLutButton = wx.Button(self.__controlCol) self.__copyLutButton = wx.Button(self.__controlCol) self.__saveLutButton = wx.Button(self.__controlCol) self.__loadLutButton = wx.Button(self.__controlCol) self.__controlColSizer = wx.BoxSizer(wx.VERTICAL) self.__sizer = wx.BoxSizer(wx.VERTICAL) self.__controlCol.SetSizer(self.__controlColSizer) self .SetSizer(self.__sizer) self.__controlColSizer.Add(self.__lutChoice, flag=wx.EXPAND) self.__controlColSizer.Add(self.__selAllButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__selNoneButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__addLabelButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__rmLabelButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__newLutButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__copyLutButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__loadLutButton, flag=wx.EXPAND) self.__controlColSizer.Add(self.__saveLutButton, flag=wx.EXPAND) self.__sizer.Add(self.__controlCol, flag=wx.EXPAND) self.__sizer.Add(self.__labelList, flag=wx.EXPAND, proportion=1) # Label the buttons self.__selAllButton .SetLabel(strings.labels[ self, 'selectAll']) self.__selNoneButton .SetLabel(strings.labels[ self, 'selectNone']) self.__addLabelButton.SetLabel(strings.labels[ self, 'addLabel']) self.__rmLabelButton .SetLabel(strings.labels[ self, 'removeLabel']) self.__newLutButton .SetLabel(strings.labels[ self, 'newLut']) self.__copyLutButton .SetLabel(strings.labels[ self, 'copyLut']) self.__loadLutButton .SetLabel(strings.labels[ self, 'loadLut']) self.__saveLutButton .SetLabel(strings.labels[ self, 'saveLut']) self.__lutChoice .Bind(wx.EVT_CHOICE, self.__onLutChoice) self.__selAllButton .Bind(wx.EVT_BUTTON, self.__onSelectAll) self.__selNoneButton .Bind(wx.EVT_BUTTON, self.__onSelectNone) self.__addLabelButton.Bind(wx.EVT_BUTTON, self.__onLabelAdd) self.__rmLabelButton .Bind(wx.EVT_BUTTON, self.__onLabelRemove) self.__newLutButton .Bind(wx.EVT_BUTTON, self.__onNewLut) self.__copyLutButton .Bind(wx.EVT_BUTTON, self.__onCopyLut) self.__loadLutButton .Bind(wx.EVT_BUTTON, self.__onLoadLut) self.__saveLutButton .Bind(wx.EVT_BUTTON, self.__onSaveLut) # The selected overlay / DisplayOpts # are tracked if they use an LUT # (e.g. LabelOpts). And the currently # selected/displayed LUT is always # tracked. self.__selectedOverlay = None self.__selectedOpts = None self.__selectedLut = None # See the __createLabelList method self.__labelListCreateKey = 0 overlayList.addListener('overlays', self.name, self.__selectedOverlayChanged) displayCtx .addListener('selectedOverlay', self.name, self.__selectedOverlayChanged) self.__updateLutChoices() self.__selectedOverlayChanged() # If the selected lut was not set # via the selectedOverlayChanged # call, we'll manually set it here if self.__selectedLut is None: self.__setLut(fslcmaps.getLookupTables()[0]) self.Layout() # The label values (used as labels in the # EditableListBox) seem to get squashed # easily. So we're adding a bit of padding # to force this panel to have a minimum # size, to ensure that the labels are # visible. dc = wx.ClientDC(self.__labelList) w, h = dc.GetTextExtent('9999') minSize = self.__sizer.GetMinSize() self.SetMinSize((minSize[0] + w, minSize[1]))
[docs] def destroy(self): """Must be called when this ``LookupTablePanel`` is no longer needed. Removes some property listeners, and calls the :meth:`ControlPanel.destroy` method. """ self.overlayList.removeListener('overlays', self.name) self.displayCtx .removeListener('selectedOverlay', self.name) overlay = self.__selectedOverlay opts = self.__selectedOpts lut = self.__selectedLut if overlay is not None and overlay in self.overlayList: display = self.displayCtx.getDisplay(overlay) display.removeListener('overlayType', self.name) if opts is not None: opts.removeListener('lut', self.name) if lut is not None: lut.deregister(self.name, 'saved') lut.deregister(self.name, 'added') lut.deregister(self.name, 'removed') self.__selectedOverlay = None self.__selectedOpts = None self.__selectedLut = None ctrlpanel.ControlPanel.destroy(self)
def __updateLutChoices(self): """Refreshes the contents of the lookup table drop down box, using the :class:`.LookupTable` instances returned by the :func:`.colourmaps.getLookupTables` function. """ log.debug('Updating lookup table choices') oldNames = self.__lutChoice.GetItems() oldSelection = self.__lutChoice.GetSelection() luts = fslcmaps.getLookupTables() newNames = [l.name for l in luts] try: newSelection = oldNames.index(oldNames[oldSelection]) except Exception: newSelection = 0 self.__lutChoice.SetItems(newNames) self.__lutChoice.SetSelection(newSelection) for i, lut in enumerate(luts): self.__lutChoice.SetClientData(i, lut) 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 __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``. """ if self.__selectedLut == lut: return log.debug('Selecting lut: {}'.format(lut)) if self.__selectedLut is not None: self.__selectedLut.deregister(self.name, 'saved') self.__selectedLut.deregister(self.name, 'added') self.__selectedLut.deregister(self.name, 'removed') self.__selectedLut = lut if lut is not None: lut.register(self.name, self.__lutSaveStateChanged, 'saved') lut.register(self.name, self.__lutLabelAdded, 'added') lut.register(self.name, self.__lutLabelRemoved, 'removed') if lut is not None and self.__selectedOpts is not None: with props.skip(self.__selectedOpts, 'lut', self.name): self.__selectedOpts.lut = lut 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 __lutLabelAdded(self, lut, topic, label): """Called when the current :class:`.LookupTable` sends an ``'added'`` notification, indicating that a label has been added. Updates the list of displayed labels. """ label, idx = label widget = LabelWidget(self, lut, label) self.__labelList.Insert(str(label.value), idx, clientData=label.value, extraWidget=widget) def __lutLabelRemoved(self, lut, topic, label): """Called when the current :class:`.LookupTable` sends a ``'removed'`` notification, indicating that a label has been removed. Updates the list of displayed labels. """ label, idx = label self.__labelList.Delete(idx) def __onLutChoice(self, ev): """Called when the user changes the selected :class:`.LookupTable` via the lookup table drop down box. See the :meth:`__setLut` method.. """ selection = self.__lutChoice.GetSelection() lut = self.__lutChoice.GetClientData(selection) log.debug('Lut choice: {}'.format(lut)) self.__setLut(lut) def __onSelectAll(self, ev): """Called when the user pushes the *Select all* button. Enables every label on the current LUT. """ for label in self.__selectedLut: label.enabled = True def __onSelectNone(self, ev): """Called when the user pushes the *Select none* button. Disables every label on the current LUT. """ for label in self.__selectedLut: label.enabled = False def __onNewLut(self, ev): """Called when the user presses the *New LUT* button. Prompts the user to enter a name (via :func:`promptForLutName`), and then creates and registers a new :class:`.LookupTable` instance. Updates this ``LookupTablePanel`` via the :meth:`__updateLutChoices` and :meth:`__setLut` methods. """ key, name = promptForLutName() if key is None: return log.debug('Creating and registering new ' 'LookupTable: {}'.format(key)) lut = fslcmaps.LookupTable(key=key, name=name) fslcmaps.registerLookupTable(lut, self.overlayList, self.displayCtx) self.__updateLutChoices() self.__setLut(lut) def __onCopyLut(self, ev): """Called when the user presses the *Copy LUT* button. Prompts the user to enter a name (via :func:`promptForLutName`), and then creates and registers a new :class:`.LookupTable` instance which is initialised with the same labels as the previously selected ``LookupTable``. Updates this ``LookupTablePanel`` via the :meth:`__updateLutChoices` and :meth:`__setLut` methods. """ oldKey = self.__selectedLut.key oldName = self.__selectedLut.name newKey, newName = promptForLutName('{} copy'.format(oldName)) if newKey is None: return log.debug('Creating and registering new ' 'LookupTable {} (copied from {})'.format(newKey, oldKey)) lut = fslcmaps.LookupTable(key=newKey, name=newName) for label in self.__selectedLut.labels(): lut.insert(label.value, name=label.name, colour=label.colour, enabled=label.enabled) fslcmaps.registerLookupTable(lut, self.overlayList, self.displayCtx) self.__updateLutChoices() self.__setLut(lut) def __onLoadLut(self, ev): """Called when the user presses the *Load LUT* button. Does the following: - Prompts the user to select a LUT file with a ``wx.FileDialog`` - Prompts the user to enter a name for the LUT via the :func:`promptForLutName` function. - Creates and registers a new :class:`.LookupTable` instance, initialising it with the selected file. - Updates this ``LookupTablePanel`` via the :meth:`__updateLutChoices` and :meth:`__setLut` methods. """ parent = wx.GetApp().GetTopWindow() loadDir = fslsettings.read('fsleyes.loadlutdir', os.getcwd()) # Prompt the user to select a lut file fileDlg = wx.FileDialog( parent, defaultDir=loadDir, message=strings.titles[self, 'loadLut'], style=wx.FD_OPEN) if fileDlg.ShowModal() != wx.ID_OK: return lutFile = fileDlg.GetPath() lutDir = op.dirname(lutFile) lutName = op.splitext(op.basename(lutFile))[0] # Prompt the user to enter a name lutKey, lutName = promptForLutName(lutName) if lutKey is None: return # Register the lut errTitle = strings.titles[ self, 'loadError'] errMsg = strings.messages[self, 'loadError'].format(lutFile) with status.reportIfError(errTitle, errMsg): lut = fslcmaps.registerLookupTable(lutFile, self.overlayList, self.displayCtx, key=lutKey, name=lutName) # Save the directory for next time fslsettings.write('fsleyes.loadlutdir', lutDir) # Select the lut in the panel 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). """ etitle = strings.titles[ self, 'installerror'] emsg = strings.messages[self, 'installerror'] with status.reportIfError(title=etitle, msg=emsg, raiseError=False): fslcmaps.installLookupTable(self.__selectedLut.key) 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. """ lut = self.__selectedLut value = lut.max() + 1 name = strings.labels['LutLabelDialog.newLabel'] colour = fslcmaps.randomBrightColour() colour = [int(round(c * 255.0)) for c in colour] dlg = LutLabelDialog(self.GetTopLevelParent(), value, name, colour) if dlg.ShowModal() != wx.ID_OK: return lut = self.__selectedLut value = dlg.GetValue() name = dlg.GetName() colour = dlg.GetColour()[:3] colour = [c / 255.0 for c in colour] if lut.get(value) is not None: wx.MessageBox( 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( lut.name, value, name, colour)) with lut.skip(self.name, 'added'): label = lut.insert(value, name=name, colour=colour) widget = LabelWidget(self, lut, label) idx = lut.index(label) self.__labelList.Insert(str(label.value), idx, clientData=label.value, extraWidget=widget) 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`. """ idx = self.__labelList.GetSelection() lut = self.__selectedLut value = lut[idx].value with lut.skip(self.name, 'removed'): lut.delete(value) self.__labelList.Delete(idx) 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 None: return opts = self.displayCtx.getOpts(overlay) if not isinstance(opts, (displayctx.LabelOpts, displayctx.MeshOpts)): # If the image is a Melodic image, show # the melodic classification lut if isinstance(overlay, fslmelimage.MelodicImage): self.__setLut(fslcmaps.getLookupTable('melodic-classes')) 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. Changes the :class:`.LookupTable` displayed on this ``LookupTablePanel`` (see the :meth:`__setLut` method). """ self.__setLut(self.__selectedOpts.lut)
[docs]def promptForLutName(initial=None): """Prompts the user to enter a name for a newly created :class:`.LookupTable`. """ if initial is None: initial = strings.labels[LookupTablePanel, 'newLutDefault'] nameMsg = strings.messages[LookupTablePanel, 'newlut'] nameTitle = strings.titles[ LookupTablePanel, 'newlut'] while True: dlg = wx.TextEntryDialog( wx.GetApp().GetTopWindow(), nameMsg, nameTitle, initial) if dlg.ShowModal() != wx.ID_OK: return None, None lutName = dlg.GetValue() lutKey = fslcmaps.makeValidMapKey(lutName) # a lut with the specified name already exists if fslcmaps.isLookupTableRegistered(lutKey): nameMsg = strings.messages[LookupTablePanel, 'alreadyinstalled'] continue break return lutKey, lutName
[docs]class LabelWidget(wx.Panel): """A ``LabelWidget`` is shown for each label of the :class:`.LookupTable` which is currently displayed on a :class:`LookupTablePanel`. A ``LabelWidget`` allows the user to change the colour and visibility of the label value. """
[docs] def __init__(self, lutPanel, lut, label): """Create a ``LabelWidget``. :arg lutPanel: The :class:`LookupTablePanel` that is displaying this ``LabelWidget``. :arg lut: The :class:`.LookupTable` currently being displayed. :arg label: The :class:`.LutLabel` that this ``LabelWidget`` is associated with. """ wx.Panel.__init__(self, lutPanel) self.__lutPanel = lutPanel self.__lut = lut self.__label = label self.__name = props.makeWidget(self, label, 'name') self.__enable = props.makeWidget(self, label, 'enabled') self.__colour = props.makeWidget(self, label, 'colour') self.__sizer = wx.BoxSizer(wx.HORIZONTAL) self.SetSizer(self.__sizer) self.__sizer.Add(self.__enable, flag=wx.ALIGN_CENTRE) self.__sizer.Add(self.__colour, flag=wx.ALIGN_CENTRE) self.__sizer.Add(self.__name, flag=wx.EXPAND)
[docs]class LutLabelDialog(wx.Dialog): """A dialog which is displayed when the user adds a new label to the :class:`.LookupTable` currently displayed in the :class:`LookupTablePanel`. Prompts the user to enter a label value, name, and colour. After the dialog is dismissed, the entered information is available via the following methods: .. autosummary:: :nosignatures: GetValue GetName GetColour """
[docs] def __init__(self, parent, value, name, colour): """Create a ``LutLabelDialog``. :arg parent: The :mod:`wx` parent object. """ wx.Dialog.__init__(self, parent, title=strings.titles[self]) self.__value = wx.SpinCtrl( self, min=0, max=65535) self.__name = wx.TextCtrl( self) self.__colour = wx.ColourPickerCtrl(self) self.__valueLabel = wx.StaticText(self) self.__nameLabel = wx.StaticText(self) self.__colourLabel = wx.StaticText(self) self.__ok = wx.Button(self, id=wx.ID_OK) self.__cancel = wx.Button(self, id=wx.ID_CANCEL) self.__valueLabel .SetLabel(strings.labels[self, 'value']) self.__nameLabel .SetLabel(strings.labels[self, 'name']) self.__colourLabel.SetLabel(strings.labels[self, 'colour']) self.__ok .SetLabel(strings.labels[self, 'ok']) self.__cancel .SetLabel(strings.labels[self, 'cancel']) self.__value .SetValue( value) self.__name .SetValue( name) self.__colour .SetColour(colour) self.__sizer = wx.GridSizer(4, 2, 0, 0) self.SetSizer(self.__sizer) self.__sizer.Add(self.__valueLabel, flag=wx.EXPAND | wx.TOP | wx.LEFT, border=15) self.__sizer.Add(self.__value, flag=wx.EXPAND | wx.TOP | wx.RIGHT, border=15) self.__sizer.Add(self.__nameLabel, flag=wx.EXPAND | wx.LEFT, border=15) self.__sizer.Add(self.__name, flag=wx.EXPAND | wx.RIGHT, border=15) self.__sizer.Add(self.__colourLabel, flag=wx.EXPAND | wx.LEFT, border=15) self.__sizer.Add(self.__colour, flag=wx.EXPAND | wx.RIGHT, border=15) self.__sizer.Add(self.__ok, flag=wx.EXPAND | wx.BOTTOM | wx.LEFT, border=15) self.__sizer.Add(self.__cancel, flag=wx.EXPAND | wx.BOTTOM | wx.RIGHT, border=15) self.__ok .Bind(wx.EVT_BUTTON, self.__onOk) self.__cancel.Bind(wx.EVT_BUTTON, self.__onCancel) self.__ok.SetDefault() self.Layout() self.Fit() self.CentreOnParent() self.__enteredValue = None self.__enteredName = None self.__enteredColour = None
[docs] def GetValue(self): """Returns the value that was entered by the user. Or, returns ``None`` if the user cancelled the dialog, or the dialog has not yet been closed. """ return self.__enteredValue
[docs] def GetName(self): """Returns the name that was entered by the user. Or, returns ``None`` if the user cancelled the dialog, or the dialog has not yet been closed. """ return self.__enteredName
[docs] def GetColour(self): """Returns the colour that was entered by the user. Or, returns ``None`` if the user cancelled the dialog, or the dialog has not yet been closed. """ return self.__enteredColour
def __onOk(self, ev): """Called when the user confirms the dialog. Saves the name, colour, and value that were entered, and closes the dialog. """ self.__enteredValue = self.__value .GetValue() self.__enteredName = self.__name .GetValue() self.__enteredColour = self.__colour.GetColour() self.EndModal(wx.ID_OK) def __onCancel(self, ev): """Called when the user cancells the dialog. Closes the dialog.""" self.EndModal(wx.ID_CANCEL)