3 Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
5 This file is part of wxPyColourChooser.
7 This version of wxPyColourChooser is open source; you can redistribute it
8 and/or modify it under the licensed terms.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 from wxPython
.wx
import *
22 class wxPyColourChooser(wxPanel
):
23 """A Pure-Python implementation of the colour chooser dialog.
25 The PyColourChooser is a pure python implementation of the colour
26 chooser dialog. It's useful for embedding the colour choosing functionality
27 inside other widgets, when the pop-up dialog is undesirable. It can also
28 be used as a drop-in replacement on the GTK platform, as the native
29 dialog is kind of ugly.
44 'MEDIUM SPRING GREEN',
79 'MEDIUM FOREST GREEN',
88 # Generate the custom colours. These colours are shared across
89 # all instances of the colour chooser
90 NO_CUSTOM_COLOURS
= 16
91 custom_colours
= [ (wxColour(255, 255, 255),
92 pycolourslider
.PyColourSlider
.HEIGHT
/ 2)
96 idADD_CUSTOM
= wxNewId()
99 def __init__(self
, parent
, id):
100 """Creates an instance of the colour chooser. Note that it is best to
101 accept the given size of the colour chooser as it is currently not
103 wxPanel
.__init
__(self
, parent
, id)
105 self
.basic_label
= wxStaticText(self
, -1, _("Basic Colours:"))
106 self
.custom_label
= wxStaticText(self
, -1, _("Custom Colours:"))
107 self
.add_button
= wxButton(self
, self
.idADD_CUSTOM
, _("Add to Custom Colours"))
109 EVT_BUTTON(self
, self
.idADD_CUSTOM
, self
.onAddCustom
)
111 # Since we're going to be constructing widgets that require some serious
112 # computation, let's process any events (like redraws) right now
115 # Create the basic colours palette
116 self
.colour_boxs
= [ ]
117 colour_grid
= wxGridSizer(6, 8)
118 for name
in self
.colour_names
:
120 box
= pycolourbox
.PyColourBox(self
, new_id
)
121 EVT_LEFT_DOWN(box
.GetColourBox(), lambda x
, b
=box
: self
.onBasicClick(x
, b
))
122 self
.colour_boxs
.append(box
)
123 colour_grid
.Add(box
, 0, wxEXPAND
)
125 # Create the custom colours palette
126 self
.custom_boxs
= [ ]
127 custom_grid
= wxGridSizer(2, 8)
128 for wxcolour
, slidepos
in self
.custom_colours
:
130 custom
= pycolourbox
.PyColourBox(self
, new_id
)
131 EVT_LEFT_DOWN(custom
.GetColourBox(), lambda x
, b
=custom
: self
.onCustomClick(x
, b
))
132 custom
.SetColour(wxcolour
)
133 custom_grid
.Add(custom
, 0, wxEXPAND
)
134 self
.custom_boxs
.append(custom
)
136 csizer
= wxBoxSizer(wxVERTICAL
)
138 csizer
.Add(self
.basic_label
, 0, wxEXPAND
)
140 csizer
.Add(colour_grid
, 0, wxEXPAND
)
142 csizer
.Add(self
.custom_label
, 0, wxEXPAND
)
144 csizer
.Add(custom_grid
, 0, wxEXPAND
)
146 csizer
.Add(self
.add_button
, 0, wxEXPAND
)
148 self
.palette
= pypalette
.PyPalette(self
, -1)
149 self
.colour_slider
= pycolourslider
.PyColourSlider(self
, -1)
150 self
.slider
= wxSlider(self
, self
.idSCROLL
, 86, 0, self
.colour_slider
.HEIGHT
- 1,
151 style
=wxSL_VERTICAL
, size
=wxSize(15, self
.colour_slider
.HEIGHT
))
152 EVT_COMMAND_SCROLL(self
, self
.idSCROLL
, self
.onScroll
)
153 psizer
= wxBoxSizer(wxHORIZONTAL
)
154 psizer
.Add(self
.palette
, 0, 0)
156 psizer
.Add(self
.colour_slider
, 0, wxALIGN_CENTER_VERTICAL
)
157 psizer
.Add(self
.slider
, 0, wxALIGN_CENTER_VERTICAL
)
159 # Register mouse events for dragging across the palette
160 EVT_LEFT_DOWN(self
.palette
, self
.onPaletteDown
)
161 EVT_LEFT_UP(self
.palette
, self
.onPaletteUp
)
162 EVT_MOTION(self
.palette
, self
.onPaletteMotion
)
163 self
.mouse_down
= False
165 self
.solid
= pycolourbox
.PyColourBox(self
, -1, size
=wxSize(75, 50))
166 slabel
= wxStaticText(self
, -1, _("Solid Colour"))
167 ssizer
= wxBoxSizer(wxVERTICAL
)
168 ssizer
.Add(self
.solid
, 0, 0)
170 ssizer
.Add(slabel
, 0, wxALIGN_CENTER_HORIZONTAL
)
172 hlabel
= wxStaticText(self
, -1, _("H:"))
173 self
.hentry
= wxTextCtrl(self
, -1)
174 self
.hentry
.SetSize((40, -1))
175 slabel
= wxStaticText(self
, -1, _("S:"))
176 self
.sentry
= wxTextCtrl(self
, -1)
177 self
.sentry
.SetSize((40, -1))
178 vlabel
= wxStaticText(self
, -1, _("V:"))
179 self
.ventry
= wxTextCtrl(self
, -1)
180 self
.ventry
.SetSize((40, -1))
181 hsvgrid
= wxFlexGridSizer(1, 6, 2, 2)
183 (hlabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.hentry
, 0, 0),
184 (slabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.sentry
, 0, 0),
185 (vlabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.ventry
, 0, 0),
188 rlabel
= wxStaticText(self
, -1, _("R:"))
189 self
.rentry
= wxTextCtrl(self
, -1)
190 self
.rentry
.SetSize((40, -1))
191 glabel
= wxStaticText(self
, -1, _("G:"))
192 self
.gentry
= wxTextCtrl(self
, -1)
193 self
.gentry
.SetSize((40, -1))
194 blabel
= wxStaticText(self
, -1, _("B:"))
195 self
.bentry
= wxTextCtrl(self
, -1)
196 self
.bentry
.SetSize((40, -1))
197 lgrid
= wxFlexGridSizer(1, 6, 2, 2)
199 (rlabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.rentry
, 0, 0),
200 (glabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.gentry
, 0, 0),
201 (blabel
, 0, wxALIGN_CENTER_VERTICAL
), (self
.bentry
, 0, 0),
204 gsizer
= wxGridSizer(2, 1)
207 gsizer
.Add(hsvgrid
, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL
)
208 gsizer
.Add(lgrid
, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL
)
210 hsizer
= wxBoxSizer(wxHORIZONTAL
)
211 hsizer
.Add(ssizer
, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL
)
212 hsizer
.Add(gsizer
, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL
)
214 vsizer
= wxBoxSizer(wxVERTICAL
)
216 vsizer
.Add(psizer
, 0, 0)
218 vsizer
.Add(hsizer
, 0, wxEXPAND
)
220 sizer
= wxBoxSizer(wxHORIZONTAL
)
222 sizer
.Add(csizer
, 0, wxEXPAND
)
224 sizer
.Add(vsizer
, 0, wxEXPAND
)
225 self
.SetAutoLayout(True)
230 self
.UpdateColour(self
.solid
.GetColour())
232 def InitColours(self
):
233 """Initializes the pre-set palette colours."""
234 for i
in range(len(self
.colour_names
)):
235 colour
= wxTheColourDatabase
.FindColour(self
.colour_names
[i
])
236 self
.colour_boxs
[i
].SetColourTuple((colour
.Red(),
240 def onBasicClick(self
, event
, box
):
241 """Highlights the selected colour box and updates the solid colour
242 display and colour slider to reflect the choice."""
243 if hasattr(self
, '_old_custom_highlight'):
244 self
._old
_custom
_highlight
.SetHighlight(False)
245 if hasattr(self
, '_old_colour_highlight'):
246 self
._old
_colour
_highlight
.SetHighlight(False)
247 box
.SetHighlight(True)
248 self
._old
_colour
_highlight
= box
249 self
.UpdateColour(box
.GetColour())
251 def onCustomClick(self
, event
, box
):
252 """Highlights the selected custom colour box and updates the solid
253 colour display and colour slider to reflect the choice."""
254 if hasattr(self
, '_old_colour_highlight'):
255 self
._old
_colour
_highlight
.SetHighlight(False)
256 if hasattr(self
, '_old_custom_highlight'):
257 self
._old
_custom
_highlight
.SetHighlight(False)
258 box
.SetHighlight(True)
259 self
._old
_custom
_highlight
= box
261 # Update the colour panel and then the slider accordingly
262 box_index
= self
.custom_boxs
.index(box
)
263 base_colour
, slidepos
= self
.custom_colours
[box_index
]
264 self
.UpdateColour(box
.GetColour())
265 self
.slider
.SetValue(slidepos
)
267 def onAddCustom(self
, event
):
268 """Adds a custom colour to the custom colour box set. Boxes are
269 chosen in a round-robin fashion, eventually overwriting previously
271 # Store the colour and slider position so we can restore the
272 # custom colours just as they were
273 self
.setCustomColour(self
.last_custom
,
274 self
.solid
.GetColour(),
275 self
.colour_slider
.GetBaseColour(),
276 self
.slider
.GetValue())
277 self
.last_custom
= (self
.last_custom
+ 1) % self
.NO_CUSTOM_COLOURS
279 def setCustomColour (self
, index
, true_colour
, base_colour
, slidepos
):
280 """Sets the custom colour at the given index. true_colour is wxColour
281 object containing the actual rgb value of the custom colour.
282 base_colour (wxColour) and slidepos (int) are used to configure the
283 colour slider and set everything to its original position."""
284 self
.custom_boxs
[index
].SetColour(true_colour
)
285 self
.custom_colours
[index
] = (base_colour
, slidepos
)
287 def UpdateColour(self
, colour
):
288 """Performs necessary updates for when the colour selection has
290 # Reset the palette to erase any highlighting
291 self
.palette
.ReDraw()
294 self
.solid
.SetColour(colour
)
295 self
.colour_slider
.SetBaseColour(colour
)
296 self
.colour_slider
.ReDraw()
297 self
.slider
.SetValue(0)
298 self
.UpdateEntries(colour
)
300 def UpdateEntries(self
, colour
):
301 """Updates the color levels to display the new values."""
307 # Update the RGB entries
308 self
.rentry
.SetValue(str(r
))
309 self
.gentry
.SetValue(str(g
))
310 self
.bentry
.SetValue(str(b
))
313 h
,s
,v
= colorsys
.rgb_to_hsv(r
/ 255.0, g
/ 255.0, b
/ 255.0)
314 self
.hentry
.SetValue("%.2f" % (h
))
315 self
.sentry
.SetValue("%.2f" % (s
))
316 self
.ventry
.SetValue("%.2f" % (v
))
318 def onPaletteDown(self
, event
):
319 """Stores state that the mouse has been pressed and updates
320 the selected colour values."""
321 self
.mouse_down
= True
322 self
.palette
.ReDraw()
323 self
.doPaletteClick(event
.m_x
, event
.m_y
)
325 def onPaletteUp(self
, event
):
326 """Stores state that the mouse is no longer depressed."""
327 self
.mouse_down
= False
329 def onPaletteMotion(self
, event
):
330 """Updates the colour values during mouse motion while the
331 mouse button is depressed."""
333 self
.doPaletteClick(event
.m_x
, event
.m_y
)
335 def doPaletteClick(self
, m_x
, m_y
):
336 """Updates the colour values based on the mouse location
338 # Get the colour value and update
339 colour
= self
.palette
.GetValue(m_x
, m_y
)
340 self
.UpdateColour(colour
)
342 # Highlight a fresh selected area
343 self
.palette
.ReDraw()
344 self
.palette
.HighlightPoint(m_x
, m_y
)
346 # Force an onscreen update
348 self
.colour_slider
.Refresh()
350 def onScroll(self
, event
):
351 """Updates the solid colour display to reflect the changing slider."""
352 value
= self
.slider
.GetValue()
353 colour
= self
.colour_slider
.GetValue(value
)
354 self
.solid
.SetColour(colour
)
355 self
.UpdateEntries(colour
)
357 def SetValue(self
, colour
):
358 """Updates the colour chooser to reflect the given wxColour."""
359 self
.UpdateColour(colour
)
362 """Returns a wxColour object indicating the current colour choice."""
363 return self
.solid
.GetColour()
366 """Simple test display."""
369 frame
= wxFrame(NULL
, -1, 'PyColourChooser Test')
371 chooser
= wxPyColourChooser(frame
, -1)
372 sizer
= wxBoxSizer(wxVERTICAL
)
373 sizer
.Add(chooser
, 0, 0)
374 frame
.SetAutoLayout(True)
375 frame
.SetSizer(sizer
)
379 self
.SetTopWindow(frame
)
384 if __name__
== '__main__':