]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/colourchooser/pycolourchooser.py
Oops
[wxWidgets.git] / wxPython / wx / lib / colourchooser / pycolourchooser.py
CommitLineData
d14a1e28
RD
1"""
2wxPyColourChooser
3Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
1fded56b 4
d14a1e28 5This file is part of wxPyColourChooser.
1fded56b 6
d14a1e28
RD
7This version of wxPyColourChooser is open source; you can redistribute it
8and/or modify it under the licensed terms.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13"""
14
b881fc78
RD
15# 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net)
16#
17# o 2.5 compatability update.
18#
19
20import wx
21
22import pycolourbox
23import pypalette
24import pycolourslider
25import colorsys
26import intl
27
28from intl import _ # _
29
30class wxPyColourChooser(wx.Panel):
d14a1e28
RD
31 """A Pure-Python implementation of the colour chooser dialog.
32
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.
38 """
39
40 colour_names = [
41 'ORANGE',
42 'GOLDENROD',
43 'WHEAT',
44 'SPRING GREEN',
45 'SKY BLUE',
46 'SLATE BLUE',
47 'MEDIUM VIOLET RED',
48 'PURPLE',
49
50 'RED',
51 'YELLOW',
52 'MEDIUM SPRING GREEN',
53 'PALE GREEN',
54 'CYAN',
55 'LIGHT STEEL BLUE',
56 'ORCHID',
57 'LIGHT MAGENTA',
58
59 'BROWN',
60 'YELLOW',
61 'GREEN',
62 'CADET BLUE',
63 'MEDIUM BLUE',
64 'MAGENTA',
65 'MAROON',
66 'ORANGE RED',
67
68 'FIREBRICK',
69 'CORAL',
70 'FOREST GREEN',
71 'AQUAMARINE',
72 'BLUE',
73 'NAVY',
74 'THISTLE',
75 'MEDIUM VIOLET RED',
76
77 'INDIAN RED',
78 'GOLD',
79 'MEDIUM SEA GREEN',
80 'MEDIUM BLUE',
81 'MIDNIGHT BLUE',
82 'GREY',
83 'PURPLE',
84 'KHAKI',
85
86 'BLACK',
87 'MEDIUM FOREST GREEN',
88 'KHAKI',
89 'DARK GREY',
90 'SEA GREEN',
91 'LIGHT GREY',
92 'MEDIUM SLATE BLUE',
93 'WHITE',
94 ]
95
96 # Generate the custom colours. These colours are shared across
97 # all instances of the colour chooser
98 NO_CUSTOM_COLOURS = 16
b881fc78 99 custom_colours = [ (wx.Colour(255, 255, 255),
d14a1e28
RD
100 pycolourslider.PyColourSlider.HEIGHT / 2)
101 ] * NO_CUSTOM_COLOURS
102 last_custom = 0
103
b881fc78
RD
104 idADD_CUSTOM = wx.NewId()
105 idSCROLL = wx.NewId()
d14a1e28
RD
106
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
110 resizeable."""
b881fc78 111 wx.Panel.__init__(self, parent, id)
d14a1e28 112
b881fc78
RD
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"))
d14a1e28 116
b881fc78 117 self.Bind(wx.EVT_BUTTON, self.onAddCustom, self.add_button)
d14a1e28
RD
118
119 # Since we're going to be constructing widgets that require some serious
120 # computation, let's process any events (like redraws) right now
b881fc78 121 wx.Yield()
d14a1e28
RD
122
123 # Create the basic colours palette
124 self.colour_boxs = [ ]
b881fc78 125 colour_grid = wx.GridSizer(6, 8)
d14a1e28 126 for name in self.colour_names:
b881fc78 127 new_id = wx.NewId()
d14a1e28 128 box = pycolourbox.PyColourBox(self, new_id)
b881fc78
RD
129
130 box.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=box: self.onBasicClick(x, b))
131
d14a1e28 132 self.colour_boxs.append(box)
b881fc78 133 colour_grid.Add(box, 0, wx.EXPAND)
d14a1e28
RD
134
135 # Create the custom colours palette
136 self.custom_boxs = [ ]
b881fc78 137 custom_grid = wx.GridSizer(2, 8)
d14a1e28 138 for wxcolour, slidepos in self.custom_colours:
b881fc78 139 new_id = wx.NewId()
d14a1e28 140 custom = pycolourbox.PyColourBox(self, new_id)
b881fc78
RD
141
142 custom.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=custom: self.onCustomClick(x, b))
143
d14a1e28 144 custom.SetColour(wxcolour)
b881fc78 145 custom_grid.Add(custom, 0, wx.EXPAND)
d14a1e28
RD
146 self.custom_boxs.append(custom)
147
b881fc78 148 csizer = wx.BoxSizer(wx.VERTICAL)
7722248d 149 csizer.Add((1, 25))
b881fc78 150 csizer.Add(self.basic_label, 0, wx.EXPAND)
7722248d 151 csizer.Add((1, 5))
b881fc78 152 csizer.Add(colour_grid, 0, wx.EXPAND)
7722248d 153 csizer.Add((1, 25))
b881fc78 154 csizer.Add(self.custom_label, 0, wx.EXPAND)
7722248d 155 csizer.Add((1, 5))
b881fc78 156 csizer.Add(custom_grid, 0, wx.EXPAND)
7722248d 157 csizer.Add((1, 5))
b881fc78 158 csizer.Add(self.add_button, 0, wx.EXPAND)
d14a1e28
RD
159
160 self.palette = pypalette.PyPalette(self, -1)
161 self.colour_slider = pycolourslider.PyColourSlider(self, -1)
b881fc78
RD
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)
165 )
166
167 self.Bind(wx.EVT_COMMAND_SCROLL, self.onScroll, self.slider)
168 psizer = wx.BoxSizer(wx.HORIZONTAL)
d14a1e28 169 psizer.Add(self.palette, 0, 0)
7722248d 170 psizer.Add((10, 1))
b881fc78
RD
171 psizer.Add(self.colour_slider, 0, wx.ALIGN_CENTER_VERTICAL)
172 psizer.Add(self.slider, 0, wx.ALIGN_CENTER_VERTICAL)
d14a1e28
RD
173
174 # Register mouse events for dragging across the palette
b881fc78
RD
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)
d14a1e28
RD
178 self.mouse_down = False
179
b881fc78
RD
180 self.solid = pycolourbox.PyColourBox(self, -1, size=(75, 50))
181 slabel = wx.StaticText(self, -1, _("Solid Colour"))
182 ssizer = wx.BoxSizer(wx.VERTICAL)
d14a1e28 183 ssizer.Add(self.solid, 0, 0)
7722248d 184 ssizer.Add((1, 2))
b881fc78 185 ssizer.Add(slabel, 0, wx.ALIGN_CENTER_HORIZONTAL)
d14a1e28 186
b881fc78
RD
187 hlabel = wx.StaticText(self, -1, _("H:"))
188 self.hentry = wx.TextCtrl(self, -1)
d14a1e28 189 self.hentry.SetSize((40, -1))
b881fc78
RD
190 slabel = wx.StaticText(self, -1, _("S:"))
191 self.sentry = wx.TextCtrl(self, -1)
d14a1e28 192 self.sentry.SetSize((40, -1))
b881fc78
RD
193 vlabel = wx.StaticText(self, -1, _("V:"))
194 self.ventry = wx.TextCtrl(self, -1)
d14a1e28 195 self.ventry.SetSize((40, -1))
b881fc78 196 hsvgrid = wx.FlexGridSizer(1, 6, 2, 2)
d14a1e28 197 hsvgrid.AddMany ([
b881fc78
RD
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),
d14a1e28
RD
201 ])
202
b881fc78
RD
203 rlabel = wx.StaticText(self, -1, _("R:"))
204 self.rentry = wx.TextCtrl(self, -1)
d14a1e28 205 self.rentry.SetSize((40, -1))
b881fc78
RD
206 glabel = wx.StaticText(self, -1, _("G:"))
207 self.gentry = wx.TextCtrl(self, -1)
d14a1e28 208 self.gentry.SetSize((40, -1))
b881fc78
RD
209 blabel = wx.StaticText(self, -1, _("B:"))
210 self.bentry = wx.TextCtrl(self, -1)
d14a1e28 211 self.bentry.SetSize((40, -1))
b881fc78 212 lgrid = wx.FlexGridSizer(1, 6, 2, 2)
d14a1e28 213 lgrid.AddMany([
b881fc78
RD
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),
d14a1e28
RD
217 ])
218
b881fc78 219 gsizer = wx.GridSizer(2, 1)
d14a1e28
RD
220 gsizer.SetVGap (10)
221 gsizer.SetHGap (2)
b881fc78
RD
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)
d14a1e28 224
b881fc78
RD
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)
d14a1e28 228
b881fc78 229 vsizer = wx.BoxSizer(wx.VERTICAL)
7722248d 230 vsizer.Add((1, 5))
d14a1e28 231 vsizer.Add(psizer, 0, 0)
7722248d 232 vsizer.Add((1, 15))
b881fc78 233 vsizer.Add(hsizer, 0, wx.EXPAND)
d14a1e28 234
b881fc78 235 sizer = wx.BoxSizer(wx.HORIZONTAL)
7722248d 236 sizer.Add((5, 1))
b881fc78 237 sizer.Add(csizer, 0, wx.EXPAND)
7722248d 238 sizer.Add((10, 1))
b881fc78 239 sizer.Add(vsizer, 0, wx.EXPAND)
d14a1e28
RD
240 self.SetAutoLayout(True)
241 self.SetSizer(sizer)
242 sizer.Fit(self)
243
244 self.InitColours()
245 self.UpdateColour(self.solid.GetColour())
246
247 def InitColours(self):
248 """Initializes the pre-set palette colours."""
249 for i in range(len(self.colour_names)):
b881fc78 250 colour = wx.TheColourDatabase.FindColour(self.colour_names[i])
d14a1e28
RD
251 self.colour_boxs[i].SetColourTuple((colour.Red(),
252 colour.Green(),
253 colour.Blue()))
254
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())
265
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
275
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)
281
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
285 added colours."""
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
293
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)
301
302 def UpdateColour(self, colour):
303 """Performs necessary updates for when the colour selection has
304 changed."""
305 # Reset the palette to erase any highlighting
306 self.palette.ReDraw()
307
308 # Set the color info
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)
314
315 def UpdateEntries(self, colour):
316 """Updates the color levels to display the new values."""
317 # Temporary bindings
318 r = colour.Red()
319 g = colour.Green()
320 b = colour.Blue()
321
322 # Update the RGB entries
323 self.rentry.SetValue(str(r))
324 self.gentry.SetValue(str(g))
325 self.bentry.SetValue(str(b))
326
327 # Convert to HSV
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))
332
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)
339
340 def onPaletteUp(self, event):
341 """Stores state that the mouse is no longer depressed."""
342 self.mouse_down = False
343
344 def onPaletteMotion(self, event):
345 """Updates the colour values during mouse motion while the
346 mouse button is depressed."""
347 if self.mouse_down:
348 self.doPaletteClick(event.m_x, event.m_y)
349
350 def doPaletteClick(self, m_x, m_y):
351 """Updates the colour values based on the mouse location
352 over the palette."""
353 # Get the colour value and update
354 colour = self.palette.GetValue(m_x, m_y)
355 self.UpdateColour(colour)
356
357 # Highlight a fresh selected area
358 self.palette.ReDraw()
359 self.palette.HighlightPoint(m_x, m_y)
360
361 # Force an onscreen update
362 self.solid.Update()
363 self.colour_slider.Refresh()
364
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)
371
372 def SetValue(self, colour):
373 """Updates the colour chooser to reflect the given wxColour."""
374 self.UpdateColour(colour)
375
376 def GetValue(self):
377 """Returns a wxColour object indicating the current colour choice."""
378 return self.solid.GetColour()
379
380def main():
381 """Simple test display."""
b881fc78 382 class App(wx.App):
d14a1e28 383 def OnInit(self):
b881fc78 384 frame = wx.Frame(None, -1, 'PyColourChooser Test')
d14a1e28
RD
385
386 chooser = wxPyColourChooser(frame, -1)
b881fc78 387 sizer = wx.BoxSizer(wx.VERTICAL)
d14a1e28
RD
388 sizer.Add(chooser, 0, 0)
389 frame.SetAutoLayout(True)
390 frame.SetSizer(sizer)
391 sizer.Fit(frame)
392
393 frame.Show(True)
394 self.SetTopWindow(frame)
395 return True
b881fc78 396 app = App(False)
d14a1e28
RD
397 app.MainLoop()
398
399if __name__ == '__main__':
400 main()