#--------------------------------------------------------------------------
# Software:     InVesalius - Software de Reconstrucao 3D de Imagens Medicas
# Copyright:    (C) 2001  Centro de Pesquisas Renato Archer
# Homepage:     http://www.softwarepublico.gov.br
# Contact:      invesalius@cti.gov.br
# License:      GNU - GPL 2 (LICENSE.txt/LICENCA.txt)
#--------------------------------------------------------------------------
#    Este programa e software livre; voce pode redistribui-lo e/ou
#    modifica-lo sob os termos da Licenca Publica Geral GNU, conforme
#    publicada pela Free Software Foundation; de acordo com a versao 2
#    da Licenca.
#
#    Este programa eh distribuido na expectativa de ser util, mas SEM
#    QUALQUER GARANTIA; sem mesmo a garantia implicita de
#    COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM
#    PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
#    detalhes.
#--------------------------------------------------------------------------
import sys
 
import wx
import wx.lib.agw.fourwaysplitter as fws
from wx.lib.pubsub import pub as Publisher
 
import data.viewer_slice as slice_viewer
import data.viewer_volume as volume_viewer
import project
import widgets.slice_menu as slice_menu_
 
from gui.widgets.clut_raycasting import CLUTRaycastingWidget, \
        EVT_CLUT_POINT_RELEASE, EVT_CLUT_CURVE_SELECT, \
        EVT_CLUT_CURVE_WL_CHANGE
 
from constants import ID_TO_BMP
 
class Panel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
                          size=wx.Size(744, 656))
 
        self.__init_aui_manager()
        self.__bind_events_wx()
        #self.__init_four_way_splitter()
        #self.__init_mix()
 
    def __init_aui_manager(self):
        self.aui_manager = wx.aui.AuiManager()
        self.aui_manager.SetManagedWindow(self)
 
 
        # TODO: Testar mais e verificar melhor opcao
 
        # Position
        # volume          | pos = 0
        # sagital         | pos = 1
        # coronal         | pos = 2
        # axial           | pos = 3
        # Automatico: assim que painel eh inserido ele ocupa local mais acima na janela (menor numero de posicao)
 
        # Layer
        # Layer 0 | Layer 1 | Layer 2 | ...
        # Automatico: todos sao colocados no mesmo layer
 
        # O que eh o Dockable?
 
        # Row
        # Row 0 | Row 1
        # Idem ao layer
 
        # Como funciona Direction?
 
        # Primeira alternativa:
        # modo: 2 no Layer 0, 2 no Layer 1 por exemplo - posicao automatica (ao inves de Layer pode ser utilizado Row)
        # problema: sash control soh aparece no sentido ertical
        # tentativa de solucionar problema seria utilizar Fixed, mas qdo se aciona maximizar nao maximiza inteiro
 
        p1 = slice_viewer.Viewer(self, "AXIAL")
        s1 = wx.aui.AuiPaneInfo().Centre().Row(0).\
             Name("Axial Slice").Caption(_("Axial slice")).\
             MaximizeButton(True).CloseButton(False)
 
        p2 = slice_viewer.Viewer(self, "CORONAL")
        s2 = wx.aui.AuiPaneInfo().Centre().Row(0).\
             Name("Coronal Slice").Caption(_("Coronal slice")).\
             MaximizeButton(True).CloseButton(False)
 
        p3 = slice_viewer.Viewer(self, "SAGITAL")
        s3 = wx.aui.AuiPaneInfo().Centre().Row(1).\
             Name("Sagittal Slice").Caption(_("Sagittal slice")).\
             MaximizeButton(True).CloseButton(False)
 
        p4 = VolumeViewerCover(self)
        #p4 = volume_viewer.Viewer(self)
        s4 = wx.aui.AuiPaneInfo().Row(1).Name("Volume").\
             Bottom().Centre().Caption(_("Volume")).\
             MaximizeButton(True).CloseButton(False)
 
        self.s4 = s4
        self.p4 = p4
 
        menu = slice_menu_.SliceMenu()
        p1.SetPopupMenu(menu)
        p2.SetPopupMenu(menu)
        p3.SetPopupMenu(menu)
 
 
        if sys.platform == 'win32':
            self.aui_manager.AddPane(p1, s1)
            self.aui_manager.AddPane(p2, s2)
            self.aui_manager.AddPane(p3, s3)
            self.aui_manager.AddPane(p4, s4)
        else:
            self.aui_manager.AddPane(p4, s4)
            self.aui_manager.AddPane(p3, s3)
            self.aui_manager.AddPane(p2, s2)
            self.aui_manager.AddPane(p1, s1)
 
        self.aui_manager.Update()
 
    def __bind_events_wx(self):
        self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_MAXIMIZE, self.OnMaximize)
        self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_RESTORE, self.OnRestore)
 
    def OnMaximize(self, evt):
        if evt.GetPane().name == self.s4.name:
            Publisher.sendMessage('Show raycasting widget', None)
 
    def OnRestore(self, evt):
        if evt.GetPane().name == self.s4.name:
            Publisher.sendMessage('Hide raycasting widget', None)
 
    def __init_four_way_splitter(self):
 
        splitter = fws.FourWaySplitter(self, style=wx.SP_LIVE_UPDATE)
 
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(splitter, 1, wx.EXPAND)
        self.SetSizer(sizer)
 
        p1 = slice_viewer.Viewer(self, "AXIAL")
        splitter.AppendWindow(p1)
 
        p2 = slice_viewer.Viewer(self, "CORONAL")
        splitter.AppendWindow(p2)
 
        p3 = slice_viewer.Viewer(self, "SAGITAL")
        splitter.AppendWindow(p3)
 
        p4 = volume_viewer.Viewer(self)
        splitter.AppendWindow(p4)
 
 
    def __init_mix(self):
        aui_manager = wx.aui.AuiManager()
        aui_manager.SetManagedWindow(self)
 
 
        splitter = fws.FourWaySplitter(self, style=wx.SP_LIVE_UPDATE)
 
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(splitter, 1, wx.EXPAND)
        self.SetSizer(sizer)
 
 
        p1 = slice_viewer.Viewer(self, "AXIAL")
        aui_manager.AddPane(p1,
                                 wx.aui.AuiPaneInfo().
                                 Name("Axial Slice").Caption(_("Axial slice")).
                                 MaximizeButton(True).CloseButton(False))
 
        p2 = slice_viewer.Viewer(self, "CORONAL")
        aui_manager.AddPane(p2,
                                 wx.aui.AuiPaneInfo().
                                 Name("Coronal Slice").Caption(_("Coronal slice")).
                                 MaximizeButton(True).CloseButton(False))
 
        p3 = slice_viewer.Viewer(self, "SAGITAL")
        aui_manager.AddPane(p3,
                                 wx.aui.AuiPaneInfo().
                                 Name("Sagittal Slice").Caption(_("Sagittal slice")).
                                 MaximizeButton(True).CloseButton(False))
 
        #p4 = volume_viewer.Viewer(self)
        aui_manager.AddPane(VolumeViewerCover,
                                 wx.aui.AuiPaneInfo().
                                 Name("Volume").Caption(_("Volume")).
                                 MaximizeButton(True).CloseButton(False))
 
        splitter.AppendWindow(p1)
        splitter.AppendWindow(p2)
        splitter.AppendWindow(p3)
        splitter.AppendWindow(p4)
 
 
        aui_manager.Update()
 
class VolumeInteraction(wx.Panel):
    def __init__(self, parent, id):
        super(VolumeInteraction, self).__init__(parent, id)
        self.can_show_raycasting_widget = 0
        self.__init_aui_manager()
        #sizer = wx.BoxSizer(wx.HORIZONTAL)
        #sizer.Add(volume_viewer.Viewer(self), 1, wx.EXPAND|wx.GROW)
        #self.SetSizer(sizer)
        self.__bind_events()
        self.__bind_events_wx()
        #sizer.Fit(self)
 
    def __init_aui_manager(self):
        self.aui_manager = wx.aui.AuiManager()
        self.aui_manager.SetManagedWindow(self)
 
        p1 = volume_viewer.Viewer(self)
        s1 = wx.aui.AuiPaneInfo().Centre().\
                CloseButton(False).MaximizeButton(False).CaptionVisible(0)
        self.s1 = s1
 
        self.clut_raycasting = CLUTRaycastingWidget(self, -1)
        self.s2 = wx.aui.AuiPaneInfo().Bottom().BestSize((200, 200)).\
                CloseButton(False).MaximizeButton(False).CaptionVisible(0).\
                Hide()
 
        self.aui_manager.AddPane(p1, s1)
        self.aui_manager.AddPane(self.clut_raycasting, self.s2)
        self.aui_manager.Update()
 
    def __bind_events_wx(self):
        self.clut_raycasting.Bind(EVT_CLUT_POINT_RELEASE, self.OnPointChanged)
        self.clut_raycasting.Bind(EVT_CLUT_CURVE_SELECT, self.OnCurveSelected)
        self.clut_raycasting.Bind(EVT_CLUT_CURVE_WL_CHANGE,
                                  self.OnChangeCurveWL)
        #self.Bind(wx.EVT_SIZE, self.OnSize)
        #self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
 
    def __bind_events(self):
        Publisher.subscribe(self.ShowRaycastingWidget,
                                'Show raycasting widget')
        Publisher.subscribe(self.HideRaycastingWidget,
                                'Hide raycasting widget')
        Publisher.subscribe(self.OnSetRaycastPreset,
                                'Update raycasting preset')
        Publisher.subscribe(self.RefreshPoints,
                                'Refresh raycasting widget points')
        Publisher.subscribe(self.LoadHistogram,
                                'Load histogram')
 
    def __update_curve_wwwl_text(self, curve):
        ww, wl = self.clut_raycasting.GetCurveWWWl(curve)
        Publisher.sendMessage('Set raycasting wwwl', (ww, wl, curve))
 
    def ShowRaycastingWidget(self, evt_pubsub=None):
        self.can_show_raycasting_widget = 1
        if self.clut_raycasting.to_draw_points:
            p = self.aui_manager.GetPane(self.clut_raycasting)
            p.Show()
            self.aui_manager.Update()
 
    def HideRaycastingWidget(self, evt_pubsub=None):
        self.can_show_raycasting_widget = 0
        p = self.aui_manager.GetPane(self.clut_raycasting)
        p.Hide()
        self.aui_manager.Update()
 
    def OnPointChanged(self, evt):
        Publisher.sendMessage('Set raycasting refresh', None)
        Publisher.sendMessage('Set raycasting curve', evt.GetCurve())
        Publisher.sendMessage('Render volume viewer')
 
    def OnCurveSelected(self, evt):
        Publisher.sendMessage('Set raycasting curve', evt.GetCurve())
        Publisher.sendMessage('Render volume viewer')
 
    def OnChangeCurveWL(self, evt):
        curve = evt.GetCurve()
        self.__update_curve_wwwl_text(curve)
        Publisher.sendMessage('Render volume viewer')
 
    def OnSetRaycastPreset(self, evt_pubsub):
        preset = project.Project().raycasting_preset
        p = self.aui_manager.GetPane(self.clut_raycasting)
        self.clut_raycasting.SetRaycastPreset(preset)
        if self.clut_raycasting.to_draw_points and \
           self.can_show_raycasting_widget:
            p.Show()
        else:
            p.Hide()
        self.aui_manager.Update()
 
    def LoadHistogram(self, pubsub_evt):
        histogram = pubsub_evt.data[0]
        init, end = pubsub_evt.data[1]
        self.clut_raycasting.SetRange((init, end))
        self.clut_raycasting.SetHistogramArray(histogram, (init, end))
 
    def RefreshPoints(self, pubsub_evt):
        self.clut_raycasting.CalculatePixelPoints()
        self.clut_raycasting.Refresh()
 
import wx.lib.platebtn as pbtn
import wx.lib.buttons as btn
import wx.lib.pubsub as ps
 
import constants as const
import widgets.colourselect as csel
 
[BUTTON_RAYCASTING, BUTTON_VIEW, BUTTON_SLICE_PLANE, BUTTON_3D_STEREO] = [wx.NewId() for num in xrange(4)]
RAYCASTING_TOOLS = wx.NewId()
 
ID_TO_NAME = {}
ID_TO_TOOL = {}
ID_TO_TOOL_ITEM = {}
TOOL_STATE = {}
ID_TO_ITEMSLICEMENU = {}
ID_TO_ITEM_3DSTEREO = {}
ID_TO_STEREO_NAME = {}
 
 
class VolumeViewerCover(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
 
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(VolumeInteraction(self, -1), 1, wx.EXPAND|wx.GROW)
        sizer.Add(VolumeToolPanel(self), 0, wx.EXPAND|wx.GROW)
        sizer.Fit(self)
 
        self.SetSizer(sizer)
        self.Update()
        self.SetAutoLayout(1)
 
class VolumeToolPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, size = (10,100))
 
        # VOLUME RAYCASTING BUTTON
        BMP_RAYCASTING = wx.Bitmap("../icons/volume_raycasting.png",
                                    wx.BITMAP_TYPE_PNG)
 
        BMP_SLICE_PLANE = wx.Bitmap("../icons/slice_plane.png",
                                    wx.BITMAP_TYPE_PNG)
 
 
        BMP_3D_STEREO = wx.Bitmap("../icons/3D_glasses.png",
                                    wx.BITMAP_TYPE_PNG)
 
 
        button_raycasting = pbtn.PlateButton(self, BUTTON_RAYCASTING,"",
                BMP_RAYCASTING, style=pbtn.PB_STYLE_SQUARE,
                size=(24,24))
 
        button_stereo = pbtn.PlateButton(self, BUTTON_3D_STEREO,"",
                BMP_3D_STEREO, style=pbtn.PB_STYLE_SQUARE,
                    size=(24,24))
 
        button_slice_plane = self.button_slice_plane = pbtn.PlateButton(self, BUTTON_SLICE_PLANE,"",
        BMP_SLICE_PLANE, style=pbtn.PB_STYLE_SQUARE,
        size=(24,24))
 
        self.button_raycasting = button_raycasting
        self.button_stereo = button_stereo
 
        # VOLUME VIEW ANGLE BUTTON
        BMP_FRONT = wx.Bitmap(ID_TO_BMP[const.VOL_FRONT][1],
                              wx.BITMAP_TYPE_PNG)
        button_view = pbtn.PlateButton(self, BUTTON_VIEW, "",
                                        BMP_FRONT, size=(24,24),
                                        style=pbtn.PB_STYLE_SQUARE)
        self.button_view = button_view
 
        # VOLUME COLOUR BUTTON
        if sys.platform == 'linux2':
            size = (28,28)
            sp = 2
        else:
            size = (24,24)
            sp = 5
 
        button_colour= csel.ColourSelect(self, 111,colour=(0,0,0),
                                        size=size)
        self.button_colour = button_colour
 
        # SIZER TO ORGANIZE ALL
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(button_colour, 0, wx.ALL, sp)
        sizer.Add(button_raycasting, 0, wx.TOP|wx.BOTTOM, 1)
        sizer.Add(button_view, 0, wx.TOP|wx.BOTTOM, 1)
        sizer.Add(button_slice_plane, 0, wx.TOP|wx.BOTTOM, 1)
        sizer.Add(button_stereo, 0, wx.TOP|wx.BOTTOM, 1)
 
 
        sizer.Fit(self)
 
        self.SetSizer(sizer)
        self.SetAutoLayout(1)
        self.Update()
        self.Refresh()
 
        self.__init_menus()
        self.__bind_events()
        self.__bind_events_wx()
 
    def __bind_events(self):
        Publisher.subscribe(self.ChangeButtonColour,
                                 'Change volume viewer gui colour')
        Publisher.subscribe(self.DisablePreset, 'Close project data')
        Publisher.subscribe(self.Uncheck, 'Uncheck image plane menu')
        Publisher.subscribe(self.DisableVolumeCutMenu, 'Disable volume cut menu')
 
    def DisablePreset(self, pubsub_evt):
        self.off_item.Check(1)
 
 
    def __bind_events_wx(self):
        self.button_slice_plane.Bind(wx.EVT_LEFT_DOWN, self.OnButtonSlicePlane)
        self.button_raycasting.Bind(wx.EVT_LEFT_DOWN, self.OnButtonRaycasting)
        self.button_view.Bind(wx.EVT_LEFT_DOWN, self.OnButtonView)
        self.button_colour.Bind(csel.EVT_COLOURSELECT, self.OnSelectColour)
        self.button_stereo.Bind(wx.EVT_LEFT_DOWN, self.OnButtonStereo)
 
    def OnButtonRaycasting(self, evt):
        # MENU RELATED TO RAYCASTING TYPES
        self.button_raycasting.PopupMenu(self.menu_raycasting)
 
    def OnButtonStereo(self, evt):
        self.button_stereo.PopupMenu(self.stereo_menu)
 
    def OnButtonView(self, evt):
        self.button_view.PopupMenu(self.menu_view)
 
    def OnButtonSlicePlane(self, evt):
        self.button_slice_plane.PopupMenu(self.slice_plane_menu)
 
    def OnSavePreset(self, evt):
        d = wx.TextEntryDialog(self, _("Preset name"))
        if d.ShowModal() == wx.ID_OK:
            preset_name = d.GetValue()
            Publisher.sendMessage(_("Save raycasting preset"),
                                       preset_name)
 
    def __init_menus(self, pubsub_evt=None):
        # MENU RELATED TO RAYCASTING TYPES
        menu = self.menu = wx.Menu()
        #print "\n\n"
        #print ">>>>", const.RAYCASTING_TYPES.sort()
        #print "\n\n"
        for name in const.RAYCASTING_TYPES:
            id = wx.NewId()
            item = wx.MenuItem(menu, id, name, kind=wx.ITEM_RADIO)
            menu.AppendItem(item)
            if name == const.RAYCASTING_OFF_LABEL:
                self.off_item = item
                item.Check(1)
            ID_TO_NAME[id] = name
 
        menu.AppendSeparator()
        # MENU RELATED TO RAYCASTING TOOLS
        self.id_cutplane = None
        submenu = wx.Menu()
        for name in const.RAYCASTING_TOOLS:
           print name
           id = wx.NewId()
           if not(self.id_cutplane):
               self.id_cutplane = id
 
           item = wx.MenuItem(submenu, id, name, kind=wx.ITEM_CHECK)
           submenu.AppendItem(item)
           ID_TO_TOOL[id] = name
           ID_TO_TOOL_ITEM[id] = item
           TOOL_STATE[id] = False
        self.submenu_raycasting_tools = submenu
        menu.AppendMenu(RAYCASTING_TOOLS, _("Tools"), submenu)
        menu.Enable(RAYCASTING_TOOLS, 0)
 
        self.menu_raycasting = menu
        # In MacOS X and Windows, binding parent menu is enough. But
        # not in GNU Linux - in the last it is necessary to bind the
        # submenu
        if sys.platform != 'win32':
            submenu.Bind(wx.EVT_MENU, self.OnMenuRaycasting)
        menu.Bind(wx.EVT_MENU, self.OnMenuRaycasting)
 
        # VOLUME VIEW ANGLE BUTTON
        menu = wx.Menu()
        for id in ID_TO_BMP:
            bmp =  wx.Bitmap(ID_TO_BMP[id][1], wx.BITMAP_TYPE_PNG)
            item = wx.MenuItem(menu, id, ID_TO_BMP[id][0])
            item.SetBitmap(bmp)
            menu.AppendItem(item)
        menu.Bind(wx.EVT_MENU, self.OnMenuView)
        self.menu_view = menu
 
        #SLICE PLANES BUTTON
        self.slice_plane_menu = slice_plane_menu = wx.Menu()
        itens = ["Axial", "Coronal", "Sagital"]
 
        for value in itens:
            new_id = wx.NewId()
 
            item = wx.MenuItem(slice_plane_menu, new_id, value,
                                            kind = wx.ITEM_CHECK)
            ID_TO_ITEMSLICEMENU[new_id] = item
            slice_plane_menu.AppendItem(item)
 
        slice_plane_menu.Bind(wx.EVT_MENU, self.OnMenuPlaneSlice)
 
 
        #3D Stereo Buttons
        self.stereo_menu = stereo_menu = wx.Menu()
        itens = [const.STEREO_OFF, const.STEREO_RED_BLUE,const.STEREO_ANAGLYPH, const.STEREO_CRISTAL, 
                 const.STEREO_INTERLACED, const.STEREO_LEFT, const.STEREO_RIGHT, const.STEREO_DRESDEN, 
                 const.STEREO_CHECKBOARD]
 
        for value in itens:
            new_id = wx.NewId()
 
            item = wx.MenuItem(stereo_menu, new_id, value, 
                                                kind = wx.ITEM_RADIO)
 
            ID_TO_ITEM_3DSTEREO[new_id] = item
            ID_TO_STEREO_NAME[new_id] = value 
            stereo_menu.AppendItem(item)
 
        stereo_menu.Bind(wx.EVT_MENU, self.OnMenuStereo)
 
        self.Fit()
        self.Update()
 
    def DisableVolumeCutMenu(self, pusub_evt):
        self.menu.Enable(RAYCASTING_TOOLS, 0)
        item = ID_TO_TOOL_ITEM[self.id_cutplane]
        item.Check(0)
 
    def BuildRaycastingMenu(self):
        presets = []
        for folder in const.RAYCASTING_PRESETS_FOLDERS:
                presets += [filename.split(".")[0] for filename in
                            os.listdir(folder) if
                            os.path.isfile(os.path.join(folder,filename))]
 
    def OnMenuPlaneSlice(self, evt):
 
        id = evt.GetId()
        item = ID_TO_ITEMSLICEMENU[id]
        checked = item.IsChecked()
        label = item.GetLabel()
 
        if not (checked):
            Publisher.sendMessage('Disable plane', label)
        else:
            Publisher.sendMessage('Enable plane', label)
 
    def OnMenuStereo(self, evt):
        id = evt.GetId() 
        mode = ID_TO_STEREO_NAME[id]
        Publisher.sendMessage('Set stereo mode', mode)
 
 
    def Uncheck(self, pubsub_evt):        
        for item in self.slice_plane_menu.GetMenuItems():
            if (item.IsChecked()):
                item.Check(0)
 
    def ChangeButtonColour(self, pubsub_evt):
        colour = [i*255 for i in pubsub_evt.data]
        self.button_colour.SetColour(colour)
 
    def OnMenuRaycasting(self, evt):
        """Events from raycasting menu."""
        id = evt.GetId()
        if id in ID_TO_NAME.keys():
            # Raycassting type was selected
            name = ID_TO_NAME[id]
            Publisher.sendMessage('Load raycasting preset',
                                          ID_TO_NAME[id])
            # Enable or disable tools
            if name != const.RAYCASTING_OFF_LABEL:
 	            self.menu_raycasting.Enable(RAYCASTING_TOOLS, 1)
            else:
                self.menu_raycasting.Enable(RAYCASTING_TOOLS, 0)
 
        else:
            # Raycasting tool
            # TODO: In future, when more tools are available
            item = ID_TO_TOOL_ITEM[id]
            #if not item.IsChecked():
            #    for i in ID_TO_TOOL_ITEM.values():
            #        if i is not item:
            #            i.Check(0)
            if not TOOL_STATE[id]:
                Publisher.sendMessage('Enable raycasting tool',
                                          [ID_TO_TOOL[id],1])
                TOOL_STATE[id] = True
                item.Check(1)
            else:
                Publisher.sendMessage('Enable raycasting tool',
                                            [ID_TO_TOOL[id],0])
                TOOL_STATE[id] = False
                item.Check(0)
 
 
    def OnMenuView(self, evt):
        """Events from button menus."""
        bmp = wx.Bitmap(ID_TO_BMP[evt.GetId()][1], wx.BITMAP_TYPE_PNG)
        self.button_view.SetBitmapSelected(bmp)
 
        Publisher.sendMessage('Set volume view angle',
                                   evt.GetId())
        self.Refresh()
 
    def OnSelectColour(self, evt):
        colour = c = [i/255.0 for i in evt.GetValue()]
        Publisher.sendMessage('Change volume viewer background colour', colour)