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.
15 # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net)
17 # o 2.5 compatability update.
28 from intl
import _
# _
30 class wxPyColourChooser(wx
.Panel
):
31 """A Pure-Python implementation of the colour chooser dialog.
33 The PyColourChooser is a pure python implementation of the colour
34 chooser dialog. It's useful for embedding the colour choosing functionality
35 inside other widgets, when the pop-up dialog is undesirable. It can also
36 be used as a drop-in replacement on the GTK platform, as the native
37 dialog is kind of ugly.
52 'MEDIUM SPRING GREEN',
87 'MEDIUM FOREST GREEN',
96 # Generate the custom colours. These colours are shared across
97 # all instances of the colour chooser
98 NO_CUSTOM_COLOURS
= 16
99 custom_colours
= [ (wx
.Colour(255, 255, 255),
100 pycolourslider
.PyColourSlider
.HEIGHT
/ 2)
101 ] * NO_CUSTOM_COLOURS
104 idADD_CUSTOM
= wx
.NewId()
105 idSCROLL
= wx
.NewId()
107 def __init__(self
, parent
, id):
108 """Creates an instance of the colour chooser. Note that it is best to
109 accept the given size of the colour chooser as it is currently not
111 wx
.Panel
.__init
__(self
, parent
, id)
113 self
.basic_label
= wx
.StaticText(self
, -1, _("Basic Colours:"))
114 self
.custom_label
= wx
.StaticText(self
, -1, _("Custom Colours:"))
115 self
.add_button
= wx
.Button(self
, self
.idADD_CUSTOM
, _("Add to Custom Colours"))
117 self
.Bind(wx
.EVT_BUTTON
, self
.onAddCustom
, self
.add_button
)
119 # Since we're going to be constructing widgets that require some serious
120 # computation, let's process any events (like redraws) right now
123 # Create the basic colours palette
124 self
.colour_boxs
= [ ]
125 colour_grid
= wx
.GridSizer(6, 8)
126 for name
in self
.colour_names
:
128 box
= pycolourbox
.PyColourBox(self
, new_id
)
130 box
.GetColourBox().Bind(wx
.EVT_LEFT_DOWN
, lambda x
, b
=box
: self
.onBasicClick(x
, b
))
132 self
.colour_boxs
.append(box
)
133 colour_grid
.Add(box
, 0, wx
.EXPAND
)
135 # Create the custom colours palette
136 self
.custom_boxs
= [ ]
137 custom_grid
= wx
.GridSizer(2, 8)
138 for wxcolour
, slidepos
in self
.custom_colours
:
140 custom
= pycolourbox
.PyColourBox(self
, new_id
)
142 custom
.GetColourBox().Bind(wx
.EVT_LEFT_DOWN
, lambda x
, b
=custom
: self
.onCustomClick(x
, b
))
144 custom
.SetColour(wxcolour
)
145 custom_grid
.Add(custom
, 0, wx
.EXPAND
)
146 self
.custom_boxs
.append(custom
)
148 csizer
= wx
.BoxSizer(wx
.VERTICAL
)
150 csizer
.Add(self
.basic_label
, 0, wx
.EXPAND
)
152 csizer
.Add(colour_grid
, 0, wx
.EXPAND
)
154 csizer
.Add(self
.custom_label
, 0, wx
.EXPAND
)
156 csizer
.Add(custom_grid
, 0, wx
.EXPAND
)
158 csizer
.Add(self
.add_button
, 0, wx
.EXPAND
)
160 self
.palette
= pypalette
.PyPalette(self
, -1)
161 self
.colour_slider
= pycolourslider
.PyColourSlider(self
, -1)
162 self
.slider
= wx
.Slider(
163 self
, self
.idSCROLL
, 86, 0, self
.colour_slider
.HEIGHT
- 1,
164 style
=wx
.SL_VERTICAL
, size
=(15, self
.colour_slider
.HEIGHT
)
167 self
.Bind(wx
.EVT_COMMAND_SCROLL
, self
.onScroll
, self
.slider
)
168 psizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
169 psizer
.Add(self
.palette
, 0, 0)
171 psizer
.Add(self
.colour_slider
, 0, wx
.ALIGN_CENTER_VERTICAL
)
172 psizer
.Add(self
.slider
, 0, wx
.ALIGN_CENTER_VERTICAL
)
174 # Register mouse events for dragging across the palette
175 self
.palette
.Bind(wx
.EVT_LEFT_DOWN
, self
.onPaletteDown
)
176 self
.palette
.Bind(wx
.EVT_LEFT_UP
, self
.onPaletteUp
)
177 self
.palette
.Bind(wx
.EVT_MOTION
, self
.onPaletteMotion
)
178 self
.mouse_down
= False
180 self
.solid
= pycolourbox
.PyColourBox(self
, -1, size
=(75, 50))
181 slabel
= wx
.StaticText(self
, -1, _("Solid Colour"))
182 ssizer
= wx
.BoxSizer(wx
.VERTICAL
)
183 ssizer
.Add(self
.solid
, 0, 0)
185 ssizer
.Add(slabel
, 0, wx
.ALIGN_CENTER_HORIZONTAL
)
187 hlabel
= wx
.StaticText(self
, -1, _("H:"))
188 self
.hentry
= wx
.TextCtrl(self
, -1)
189 self
.hentry
.SetSize((40, -1))
190 slabel
= wx
.StaticText(self
, -1, _("S:"))
191 self
.sentry
= wx
.TextCtrl(self
, -1)
192 self
.sentry
.SetSize((40, -1))
193 vlabel
= wx
.StaticText(self
, -1, _("V:"))
194 self
.ventry
= wx
.TextCtrl(self
, -1)
195 self
.ventry
.SetSize((40, -1))
196 hsvgrid
= wx
.FlexGridSizer(1, 6, 2, 2)
198 (hlabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.hentry
, 0, 0),
199 (slabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.sentry
, 0, 0),
200 (vlabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.ventry
, 0, 0),
203 rlabel
= wx
.StaticText(self
, -1, _("R:"))
204 self
.rentry
= wx
.TextCtrl(self
, -1)
205 self
.rentry
.SetSize((40, -1))
206 glabel
= wx
.StaticText(self
, -1, _("G:"))
207 self
.gentry
= wx
.TextCtrl(self
, -1)
208 self
.gentry
.SetSize((40, -1))
209 blabel
= wx
.StaticText(self
, -1, _("B:"))
210 self
.bentry
= wx
.TextCtrl(self
, -1)
211 self
.bentry
.SetSize((40, -1))
212 lgrid
= wx
.FlexGridSizer(1, 6, 2, 2)
214 (rlabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.rentry
, 0, 0),
215 (glabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.gentry
, 0, 0),
216 (blabel
, 0, wx
.ALIGN_CENTER_VERTICAL
), (self
.bentry
, 0, 0),
219 gsizer
= wx
.GridSizer(2, 1)
222 gsizer
.Add(hsvgrid
, 0, wx
.ALIGN_CENTER_VERTICAL | wx
.ALIGN_CENTER_HORIZONTAL
)
223 gsizer
.Add(lgrid
, 0, wx
.ALIGN_CENTER_VERTICAL | wx
.ALIGN_CENTER_HORIZONTAL
)
225 hsizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
226 hsizer
.Add(ssizer
, 0, wx
.ALIGN_CENTER_VERTICAL | wx
.ALIGN_CENTER_HORIZONTAL
)
227 hsizer
.Add(gsizer
, 0, wx
.ALIGN_CENTER_VERTICAL | wx
.ALIGN_CENTER_HORIZONTAL
)
229 vsizer
= wx
.BoxSizer(wx
.VERTICAL
)
231 vsizer
.Add(psizer
, 0, 0)
233 vsizer
.Add(hsizer
, 0, wx
.EXPAND
)
235 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
237 sizer
.Add(csizer
, 0, wx
.EXPAND
)
239 sizer
.Add(vsizer
, 0, wx
.EXPAND
)
240 self
.SetAutoLayout(True)
245 self
.UpdateColour(self
.solid
.GetColour())
247 def InitColours(self
):
248 """Initializes the pre-set palette colours."""
249 for i
in range(len(self
.colour_names
)):
250 colour
= wx
.TheColourDatabase
.FindColour(self
.colour_names
[i
])
251 self
.colour_boxs
[i
].SetColourTuple((colour
.Red(),
255 def onBasicClick(self
, event
, box
):
256 """Highlights the selected colour box and updates the solid colour
257 display and colour slider to reflect the choice."""
258 if hasattr(self
, '_old_custom_highlight'):
259 self
._old
_custom
_highlight
.SetHighlight(False)
260 if hasattr(self
, '_old_colour_highlight'):
261 self
._old
_colour
_highlight
.SetHighlight(False)
262 box
.SetHighlight(True)
263 self
._old
_colour
_highlight
= box
264 self
.UpdateColour(box
.GetColour())
266 def onCustomClick(self
, event
, box
):
267 """Highlights the selected custom colour box and updates the solid
268 colour display and colour slider to reflect the choice."""
269 if hasattr(self
, '_old_colour_highlight'):
270 self
._old
_colour
_highlight
.SetHighlight(False)
271 if hasattr(self
, '_old_custom_highlight'):
272 self
._old
_custom
_highlight
.SetHighlight(False)
273 box
.SetHighlight(True)
274 self
._old
_custom
_highlight
= box
276 # Update the colour panel and then the slider accordingly
277 box_index
= self
.custom_boxs
.index(box
)
278 base_colour
, slidepos
= self
.custom_colours
[box_index
]
279 self
.UpdateColour(box
.GetColour())
280 self
.slider
.SetValue(slidepos
)
282 def onAddCustom(self
, event
):
283 """Adds a custom colour to the custom colour box set. Boxes are
284 chosen in a round-robin fashion, eventually overwriting previously
286 # Store the colour and slider position so we can restore the
287 # custom colours just as they were
288 self
.setCustomColour(self
.last_custom
,
289 self
.solid
.GetColour(),
290 self
.colour_slider
.GetBaseColour(),
291 self
.slider
.GetValue())
292 self
.last_custom
= (self
.last_custom
+ 1) % self
.NO_CUSTOM_COLOURS
294 def setCustomColour (self
, index
, true_colour
, base_colour
, slidepos
):
295 """Sets the custom colour at the given index. true_colour is wxColour
296 object containing the actual rgb value of the custom colour.
297 base_colour (wxColour) and slidepos (int) are used to configure the
298 colour slider and set everything to its original position."""
299 self
.custom_boxs
[index
].SetColour(true_colour
)
300 self
.custom_colours
[index
] = (base_colour
, slidepos
)
302 def UpdateColour(self
, colour
):
303 """Performs necessary updates for when the colour selection has
305 # Reset the palette to erase any highlighting
306 self
.palette
.ReDraw()
309 self
.solid
.SetColour(colour
)
310 self
.colour_slider
.SetBaseColour(colour
)
311 self
.colour_slider
.ReDraw()
312 self
.slider
.SetValue(0)
313 self
.UpdateEntries(colour
)
315 def UpdateEntries(self
, colour
):
316 """Updates the color levels to display the new values."""
322 # Update the RGB entries
323 self
.rentry
.SetValue(str(r
))
324 self
.gentry
.SetValue(str(g
))
325 self
.bentry
.SetValue(str(b
))
328 h
,s
,v
= colorsys
.rgb_to_hsv(r
/ 255.0, g
/ 255.0, b
/ 255.0)
329 self
.hentry
.SetValue("%.2f" % (h
))
330 self
.sentry
.SetValue("%.2f" % (s
))
331 self
.ventry
.SetValue("%.2f" % (v
))
333 def onPaletteDown(self
, event
):
334 """Stores state that the mouse has been pressed and updates
335 the selected colour values."""
336 self
.mouse_down
= True
337 self
.palette
.ReDraw()
338 self
.doPaletteClick(event
.m_x
, event
.m_y
)
340 def onPaletteUp(self
, event
):
341 """Stores state that the mouse is no longer depressed."""
342 self
.mouse_down
= False
344 def onPaletteMotion(self
, event
):
345 """Updates the colour values during mouse motion while the
346 mouse button is depressed."""
348 self
.doPaletteClick(event
.m_x
, event
.m_y
)
350 def doPaletteClick(self
, m_x
, m_y
):
351 """Updates the colour values based on the mouse location
353 # Get the colour value and update
354 colour
= self
.palette
.GetValue(m_x
, m_y
)
355 self
.UpdateColour(colour
)
357 # Highlight a fresh selected area
358 self
.palette
.ReDraw()
359 self
.palette
.HighlightPoint(m_x
, m_y
)
361 # Force an onscreen update
363 self
.colour_slider
.Refresh()
365 def onScroll(self
, event
):
366 """Updates the solid colour display to reflect the changing slider."""
367 value
= self
.slider
.GetValue()
368 colour
= self
.colour_slider
.GetValue(value
)
369 self
.solid
.SetColour(colour
)
370 self
.UpdateEntries(colour
)
372 def SetValue(self
, colour
):
373 """Updates the colour chooser to reflect the given wxColour."""
374 self
.UpdateColour(colour
)
377 """Returns a wxColour object indicating the current colour choice."""
378 return self
.solid
.GetColour()
381 """Simple test display."""
384 frame
= wx
.Frame(None, -1, 'PyColourChooser Test')
386 chooser
= wxPyColourChooser(frame
, -1)
387 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
388 sizer
.Add(chooser
, 0, 0)
389 frame
.SetAutoLayout(True)
390 frame
.SetSizer(sizer
)
394 self
.SetTopWindow(frame
)
399 if __name__
== '__main__':