X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1fded56b375bf7a4687af1cdb182899614c1b2a8..25b3661bd4cb44304418b93c0dee1d0dfb99765c:/wxPython/wx/lib/colourchooser/pycolourchooser.py diff --git a/wxPython/wx/lib/colourchooser/pycolourchooser.py b/wxPython/wx/lib/colourchooser/pycolourchooser.py index 4b1049ef3b..295698896d 100644 --- a/wxPython/wx/lib/colourchooser/pycolourchooser.py +++ b/wxPython/wx/lib/colourchooser/pycolourchooser.py @@ -1,11 +1,413 @@ +""" +PyColourChooser +Copyright (C) 2002 Michael Gilfix -"""Renamer stub: provides a way to drop the wx prefix from wxPython objects.""" +This file is part of PyColourChooser. -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] +This version of PyColourChooser is open source; you can redistribute it +and/or modify it under the licensed terms. -from wx import _rename -from wxPython.lib.colourchooser import pycolourchooser -_rename(globals(), pycolourchooser.__dict__, modulename='lib.colourchooser.pycolourchooser') -del pycolourchooser -del _rename +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +""" + +# 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net) +# +# o 2.5 compatability update. +# +# 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) +# +# o wxPyColorChooser -> PyColorChooser +# o wxPyColourChooser -> PyColourChooser +# o Added wx.InitAllImageHandlers() to test code since +# that's where it belongs. +# + +import wx + +import pycolourbox +import pypalette +import pycolourslider +import colorsys +import intl + +from intl import _ # _ + +class PyColourChooser(wx.Panel): + """A Pure-Python implementation of the colour chooser dialog. + + The PyColourChooser is a pure python implementation of the colour + chooser dialog. It's useful for embedding the colour choosing functionality + inside other widgets, when the pop-up dialog is undesirable. It can also + be used as a drop-in replacement on the GTK platform, as the native + dialog is kind of ugly. + """ + + colour_names = [ + 'ORANGE', + 'GOLDENROD', + 'WHEAT', + 'SPRING GREEN', + 'SKY BLUE', + 'SLATE BLUE', + 'MEDIUM VIOLET RED', + 'PURPLE', + + 'RED', + 'YELLOW', + 'MEDIUM SPRING GREEN', + 'PALE GREEN', + 'CYAN', + 'LIGHT STEEL BLUE', + 'ORCHID', + 'LIGHT MAGENTA', + + 'BROWN', + 'YELLOW', + 'GREEN', + 'CADET BLUE', + 'MEDIUM BLUE', + 'MAGENTA', + 'MAROON', + 'ORANGE RED', + + 'FIREBRICK', + 'CORAL', + 'FOREST GREEN', + 'AQUAMARINE', + 'BLUE', + 'NAVY', + 'THISTLE', + 'MEDIUM VIOLET RED', + + 'INDIAN RED', + 'GOLD', + 'MEDIUM SEA GREEN', + 'MEDIUM BLUE', + 'MIDNIGHT BLUE', + 'GREY', + 'PURPLE', + 'KHAKI', + + 'BLACK', + 'MEDIUM FOREST GREEN', + 'KHAKI', + 'DARK GREY', + 'SEA GREEN', + 'LIGHT GREY', + 'MEDIUM SLATE BLUE', + 'WHITE', + ] + + # Generate the custom colours. These colours are shared across + # all instances of the colour chooser + NO_CUSTOM_COLOURS = 16 + custom_colours = [ (wx.Colour(255, 255, 255), + pycolourslider.PyColourSlider.HEIGHT / 2) + ] * NO_CUSTOM_COLOURS + last_custom = 0 + + idADD_CUSTOM = wx.NewId() + idSCROLL = wx.NewId() + + def __init__(self, parent, id): + """Creates an instance of the colour chooser. Note that it is best to + accept the given size of the colour chooser as it is currently not + resizeable.""" + wx.Panel.__init__(self, parent, id) + + self.basic_label = wx.StaticText(self, -1, _("Basic Colours:")) + self.custom_label = wx.StaticText(self, -1, _("Custom Colours:")) + self.add_button = wx.Button(self, self.idADD_CUSTOM, _("Add to Custom Colours")) + + self.Bind(wx.EVT_BUTTON, self.onAddCustom, self.add_button) + + # Since we're going to be constructing widgets that require some serious + # computation, let's process any events (like redraws) right now + wx.Yield() + + # Create the basic colours palette + self.colour_boxs = [ ] + colour_grid = wx.GridSizer(6, 8) + for name in self.colour_names: + new_id = wx.NewId() + box = pycolourbox.PyColourBox(self, new_id) + + box.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=box: self.onBasicClick(x, b)) + + self.colour_boxs.append(box) + colour_grid.Add(box, 0, wx.EXPAND) + + # Create the custom colours palette + self.custom_boxs = [ ] + custom_grid = wx.GridSizer(2, 8) + for wxcolour, slidepos in self.custom_colours: + new_id = wx.NewId() + custom = pycolourbox.PyColourBox(self, new_id) + + custom.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=custom: self.onCustomClick(x, b)) + + custom.SetColour(wxcolour) + custom_grid.Add(custom, 0, wx.EXPAND) + self.custom_boxs.append(custom) + + csizer = wx.BoxSizer(wx.VERTICAL) + csizer.Add((1, 25)) + csizer.Add(self.basic_label, 0, wx.EXPAND) + csizer.Add((1, 5)) + csizer.Add(colour_grid, 0, wx.EXPAND) + csizer.Add((1, 25)) + csizer.Add(self.custom_label, 0, wx.EXPAND) + csizer.Add((1, 5)) + csizer.Add(custom_grid, 0, wx.EXPAND) + csizer.Add((1, 5)) + csizer.Add(self.add_button, 0, wx.EXPAND) + + self.palette = pypalette.PyPalette(self, -1) + self.colour_slider = pycolourslider.PyColourSlider(self, -1) + self.slider = wx.Slider( + self, self.idSCROLL, 86, 0, self.colour_slider.HEIGHT - 1, + style=wx.SL_VERTICAL, size=(15, self.colour_slider.HEIGHT) + ) + + self.Bind(wx.EVT_COMMAND_SCROLL, self.onScroll, self.slider) + psizer = wx.BoxSizer(wx.HORIZONTAL) + psizer.Add(self.palette, 0, 0) + psizer.Add((10, 1)) + psizer.Add(self.colour_slider, 0, wx.ALIGN_CENTER_VERTICAL) + psizer.Add(self.slider, 0, wx.ALIGN_CENTER_VERTICAL) + + # Register mouse events for dragging across the palette + self.palette.Bind(wx.EVT_LEFT_DOWN, self.onPaletteDown) + self.palette.Bind(wx.EVT_LEFT_UP, self.onPaletteUp) + self.palette.Bind(wx.EVT_MOTION, self.onPaletteMotion) + self.mouse_down = False + + self.solid = pycolourbox.PyColourBox(self, -1, size=(75, 50)) + slabel = wx.StaticText(self, -1, _("Solid Colour")) + ssizer = wx.BoxSizer(wx.VERTICAL) + ssizer.Add(self.solid, 0, 0) + ssizer.Add((1, 2)) + ssizer.Add(slabel, 0, wx.ALIGN_CENTER_HORIZONTAL) + + hlabel = wx.StaticText(self, -1, _("H:")) + self.hentry = wx.TextCtrl(self, -1) + self.hentry.SetSize((40, -1)) + slabel = wx.StaticText(self, -1, _("S:")) + self.sentry = wx.TextCtrl(self, -1) + self.sentry.SetSize((40, -1)) + vlabel = wx.StaticText(self, -1, _("V:")) + self.ventry = wx.TextCtrl(self, -1) + self.ventry.SetSize((40, -1)) + hsvgrid = wx.FlexGridSizer(1, 6, 2, 2) + hsvgrid.AddMany ([ + (hlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.hentry, 0, wx.FIXED_MINSIZE), + (slabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.sentry, 0, wx.FIXED_MINSIZE), + (vlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.ventry, 0, wx.FIXED_MINSIZE), + ]) + + rlabel = wx.StaticText(self, -1, _("R:")) + self.rentry = wx.TextCtrl(self, -1) + self.rentry.SetSize((40, -1)) + glabel = wx.StaticText(self, -1, _("G:")) + self.gentry = wx.TextCtrl(self, -1) + self.gentry.SetSize((40, -1)) + blabel = wx.StaticText(self, -1, _("B:")) + self.bentry = wx.TextCtrl(self, -1) + self.bentry.SetSize((40, -1)) + lgrid = wx.FlexGridSizer(1, 6, 2, 2) + lgrid.AddMany([ + (rlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.rentry, 0, wx.FIXED_MINSIZE), + (glabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.gentry, 0, wx.FIXED_MINSIZE), + (blabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.bentry, 0, wx.FIXED_MINSIZE), + ]) + + gsizer = wx.GridSizer(2, 1) + gsizer.SetVGap (10) + gsizer.SetHGap (2) + gsizer.Add(hsvgrid, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL) + gsizer.Add(lgrid, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL) + + hsizer = wx.BoxSizer(wx.HORIZONTAL) + hsizer.Add(ssizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL) + hsizer.Add(gsizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL) + + vsizer = wx.BoxSizer(wx.VERTICAL) + vsizer.Add((1, 5)) + vsizer.Add(psizer, 0, 0) + vsizer.Add((1, 15)) + vsizer.Add(hsizer, 0, wx.EXPAND) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add((5, 1)) + sizer.Add(csizer, 0, wx.EXPAND) + sizer.Add((10, 1)) + sizer.Add(vsizer, 0, wx.EXPAND) + self.SetAutoLayout(True) + self.SetSizer(sizer) + sizer.Fit(self) + + self.InitColours() + self.UpdateColour(self.solid.GetColour()) + + def InitColours(self): + """Initializes the pre-set palette colours.""" + for i in range(len(self.colour_names)): + colour = wx.TheColourDatabase.FindColour(self.colour_names[i]) + self.colour_boxs[i].SetColourTuple((colour.Red(), + colour.Green(), + colour.Blue())) + + def onBasicClick(self, event, box): + """Highlights the selected colour box and updates the solid colour + display and colour slider to reflect the choice.""" + if hasattr(self, '_old_custom_highlight'): + self._old_custom_highlight.SetHighlight(False) + if hasattr(self, '_old_colour_highlight'): + self._old_colour_highlight.SetHighlight(False) + box.SetHighlight(True) + self._old_colour_highlight = box + self.UpdateColour(box.GetColour()) + + def onCustomClick(self, event, box): + """Highlights the selected custom colour box and updates the solid + colour display and colour slider to reflect the choice.""" + if hasattr(self, '_old_colour_highlight'): + self._old_colour_highlight.SetHighlight(False) + if hasattr(self, '_old_custom_highlight'): + self._old_custom_highlight.SetHighlight(False) + box.SetHighlight(True) + self._old_custom_highlight = box + + # Update the colour panel and then the slider accordingly + box_index = self.custom_boxs.index(box) + base_colour, slidepos = self.custom_colours[box_index] + self.UpdateColour(box.GetColour()) + self.slider.SetValue(slidepos) + + def onAddCustom(self, event): + """Adds a custom colour to the custom colour box set. Boxes are + chosen in a round-robin fashion, eventually overwriting previously + added colours.""" + # Store the colour and slider position so we can restore the + # custom colours just as they were + self.setCustomColour(self.last_custom, + self.solid.GetColour(), + self.colour_slider.GetBaseColour(), + self.slider.GetValue()) + self.last_custom = (self.last_custom + 1) % self.NO_CUSTOM_COLOURS + + def setCustomColour (self, index, true_colour, base_colour, slidepos): + """Sets the custom colour at the given index. true_colour is wxColour + object containing the actual rgb value of the custom colour. + base_colour (wxColour) and slidepos (int) are used to configure the + colour slider and set everything to its original position.""" + self.custom_boxs[index].SetColour(true_colour) + self.custom_colours[index] = (base_colour, slidepos) + + def UpdateColour(self, colour): + """Performs necessary updates for when the colour selection has + changed.""" + # Reset the palette to erase any highlighting + self.palette.ReDraw() + + # Set the color info + self.solid.SetColour(colour) + self.colour_slider.SetBaseColour(colour) + self.colour_slider.ReDraw() + self.slider.SetValue(0) + self.UpdateEntries(colour) + + def UpdateEntries(self, colour): + """Updates the color levels to display the new values.""" + # Temporary bindings + r = colour.Red() + g = colour.Green() + b = colour.Blue() + + # Update the RGB entries + self.rentry.SetValue(str(r)) + self.gentry.SetValue(str(g)) + self.bentry.SetValue(str(b)) + + # Convert to HSV + h,s,v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0) + self.hentry.SetValue("%.2f" % (h)) + self.sentry.SetValue("%.2f" % (s)) + self.ventry.SetValue("%.2f" % (v)) + + def onPaletteDown(self, event): + """Stores state that the mouse has been pressed and updates + the selected colour values.""" + self.mouse_down = True + self.palette.ReDraw() + self.doPaletteClick(event.m_x, event.m_y) + + def onPaletteUp(self, event): + """Stores state that the mouse is no longer depressed.""" + self.mouse_down = False + + def onPaletteMotion(self, event): + """Updates the colour values during mouse motion while the + mouse button is depressed.""" + if self.mouse_down: + self.doPaletteClick(event.m_x, event.m_y) + + def doPaletteClick(self, m_x, m_y): + """Updates the colour values based on the mouse location + over the palette.""" + # Get the colour value and update + colour = self.palette.GetValue(m_x, m_y) + self.UpdateColour(colour) + + # Highlight a fresh selected area + self.palette.ReDraw() + self.palette.HighlightPoint(m_x, m_y) + + # Force an onscreen update + self.solid.Update() + self.colour_slider.Refresh() + + def onScroll(self, event): + """Updates the solid colour display to reflect the changing slider.""" + value = self.slider.GetValue() + colour = self.colour_slider.GetValue(value) + self.solid.SetColour(colour) + self.UpdateEntries(colour) + + def SetValue(self, colour): + """Updates the colour chooser to reflect the given wxColour.""" + self.UpdateColour(colour) + + def GetValue(self): + """Returns a wxColour object indicating the current colour choice.""" + return self.solid.GetColour() + +def main(): + """Simple test display.""" + class App(wx.App): + def OnInit(self): + frame = wx.Frame(None, -1, 'PyColourChooser Test') + + # Added here because that's where it's supposed to be, + # not embedded in the library. If it's embedded in the + # library, debug messages will be generated for duplicate + # handlers. + wx.InitAllImageHandlers() + + chooser = PyColourChooser(frame, -1) + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(chooser, 0, 0) + frame.SetAutoLayout(True) + frame.SetSizer(sizer) + sizer.Fit(frame) + + frame.Show(True) + self.SetTopWindow(frame) + return True + app = App(False) + app.MainLoop() + +if __name__ == '__main__': + main()