]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/colourchooser/pycolourchooser.py
wxTaskBarIcon works on wxGTK now too.
[wxWidgets.git] / wxPython / wx / lib / colourchooser / pycolourchooser.py
CommitLineData
d14a1e28 1"""
d4b73b1b 2PyColourChooser
d14a1e28 3Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
1fded56b 4
d4b73b1b 5This file is part of PyColourChooser.
1fded56b 6
d4b73b1b 7This version of PyColourChooser is open source; you can redistribute it
d14a1e28
RD
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#
d4b73b1b
RD
19# 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
20#
21# o wxPyColorChooser -> PyColorChooser
22# o wxPyColourChooser -> PyColourChooser
23# o Added wx.InitAllImageHandlers() to test code since
24# that's where it belongs.
25#
b881fc78
RD
26
27import wx
28
29import pycolourbox
30import pypalette
31import pycolourslider
32import colorsys
33import intl
34
35from intl import _ # _
36
d4b73b1b 37class PyColourChooser(wx.Panel):
d14a1e28
RD
38 """A Pure-Python implementation of the colour chooser dialog.
39
40 The PyColourChooser is a pure python implementation of the colour
41 chooser dialog. It's useful for embedding the colour choosing functionality
42 inside other widgets, when the pop-up dialog is undesirable. It can also
43 be used as a drop-in replacement on the GTK platform, as the native
44 dialog is kind of ugly.
45 """
46
47 colour_names = [
48 'ORANGE',
49 'GOLDENROD',
50 'WHEAT',
51 'SPRING GREEN',
52 'SKY BLUE',
53 'SLATE BLUE',
54 'MEDIUM VIOLET RED',
55 'PURPLE',
56
57 'RED',
58 'YELLOW',
59 'MEDIUM SPRING GREEN',
60 'PALE GREEN',
61 'CYAN',
62 'LIGHT STEEL BLUE',
63 'ORCHID',
64 'LIGHT MAGENTA',
65
66 'BROWN',
67 'YELLOW',
68 'GREEN',
69 'CADET BLUE',
70 'MEDIUM BLUE',
71 'MAGENTA',
72 'MAROON',
73 'ORANGE RED',
74
75 'FIREBRICK',
76 'CORAL',
77 'FOREST GREEN',
78 'AQUAMARINE',
79 'BLUE',
80 'NAVY',
81 'THISTLE',
82 'MEDIUM VIOLET RED',
83
84 'INDIAN RED',
85 'GOLD',
86 'MEDIUM SEA GREEN',
87 'MEDIUM BLUE',
88 'MIDNIGHT BLUE',
89 'GREY',
90 'PURPLE',
91 'KHAKI',
92
93 'BLACK',
94 'MEDIUM FOREST GREEN',
95 'KHAKI',
96 'DARK GREY',
97 'SEA GREEN',
98 'LIGHT GREY',
99 'MEDIUM SLATE BLUE',
100 'WHITE',
101 ]
102
103 # Generate the custom colours. These colours are shared across
104 # all instances of the colour chooser
105 NO_CUSTOM_COLOURS = 16
b881fc78 106 custom_colours = [ (wx.Colour(255, 255, 255),
d14a1e28
RD
107 pycolourslider.PyColourSlider.HEIGHT / 2)
108 ] * NO_CUSTOM_COLOURS
109 last_custom = 0
110
b881fc78
RD
111 idADD_CUSTOM = wx.NewId()
112 idSCROLL = wx.NewId()
d14a1e28
RD
113
114 def __init__(self, parent, id):
115 """Creates an instance of the colour chooser. Note that it is best to
116 accept the given size of the colour chooser as it is currently not
117 resizeable."""
b881fc78 118 wx.Panel.__init__(self, parent, id)
d14a1e28 119
b881fc78
RD
120 self.basic_label = wx.StaticText(self, -1, _("Basic Colours:"))
121 self.custom_label = wx.StaticText(self, -1, _("Custom Colours:"))
122 self.add_button = wx.Button(self, self.idADD_CUSTOM, _("Add to Custom Colours"))
d14a1e28 123
b881fc78 124 self.Bind(wx.EVT_BUTTON, self.onAddCustom, self.add_button)
d14a1e28
RD
125
126 # Since we're going to be constructing widgets that require some serious
127 # computation, let's process any events (like redraws) right now
b881fc78 128 wx.Yield()
d14a1e28
RD
129
130 # Create the basic colours palette
131 self.colour_boxs = [ ]
b881fc78 132 colour_grid = wx.GridSizer(6, 8)
d14a1e28 133 for name in self.colour_names:
b881fc78 134 new_id = wx.NewId()
d14a1e28 135 box = pycolourbox.PyColourBox(self, new_id)
b881fc78
RD
136
137 box.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=box: self.onBasicClick(x, b))
138
d14a1e28 139 self.colour_boxs.append(box)
b881fc78 140 colour_grid.Add(box, 0, wx.EXPAND)
d14a1e28
RD
141
142 # Create the custom colours palette
143 self.custom_boxs = [ ]
b881fc78 144 custom_grid = wx.GridSizer(2, 8)
d14a1e28 145 for wxcolour, slidepos in self.custom_colours:
b881fc78 146 new_id = wx.NewId()
d14a1e28 147 custom = pycolourbox.PyColourBox(self, new_id)
b881fc78
RD
148
149 custom.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=custom: self.onCustomClick(x, b))
150
d14a1e28 151 custom.SetColour(wxcolour)
b881fc78 152 custom_grid.Add(custom, 0, wx.EXPAND)
d14a1e28
RD
153 self.custom_boxs.append(custom)
154
b881fc78 155 csizer = wx.BoxSizer(wx.VERTICAL)
7722248d 156 csizer.Add((1, 25))
b881fc78 157 csizer.Add(self.basic_label, 0, wx.EXPAND)
7722248d 158 csizer.Add((1, 5))
b881fc78 159 csizer.Add(colour_grid, 0, wx.EXPAND)
7722248d 160 csizer.Add((1, 25))
b881fc78 161 csizer.Add(self.custom_label, 0, wx.EXPAND)
7722248d 162 csizer.Add((1, 5))
b881fc78 163 csizer.Add(custom_grid, 0, wx.EXPAND)
7722248d 164 csizer.Add((1, 5))
b881fc78 165 csizer.Add(self.add_button, 0, wx.EXPAND)
d14a1e28
RD
166
167 self.palette = pypalette.PyPalette(self, -1)
168 self.colour_slider = pycolourslider.PyColourSlider(self, -1)
b881fc78
RD
169 self.slider = wx.Slider(
170 self, self.idSCROLL, 86, 0, self.colour_slider.HEIGHT - 1,
171 style=wx.SL_VERTICAL, size=(15, self.colour_slider.HEIGHT)
172 )
173
174 self.Bind(wx.EVT_COMMAND_SCROLL, self.onScroll, self.slider)
175 psizer = wx.BoxSizer(wx.HORIZONTAL)
d14a1e28 176 psizer.Add(self.palette, 0, 0)
7722248d 177 psizer.Add((10, 1))
b881fc78
RD
178 psizer.Add(self.colour_slider, 0, wx.ALIGN_CENTER_VERTICAL)
179 psizer.Add(self.slider, 0, wx.ALIGN_CENTER_VERTICAL)
d14a1e28
RD
180
181 # Register mouse events for dragging across the palette
b881fc78
RD
182 self.palette.Bind(wx.EVT_LEFT_DOWN, self.onPaletteDown)
183 self.palette.Bind(wx.EVT_LEFT_UP, self.onPaletteUp)
184 self.palette.Bind(wx.EVT_MOTION, self.onPaletteMotion)
d14a1e28
RD
185 self.mouse_down = False
186
b881fc78
RD
187 self.solid = pycolourbox.PyColourBox(self, -1, size=(75, 50))
188 slabel = wx.StaticText(self, -1, _("Solid Colour"))
189 ssizer = wx.BoxSizer(wx.VERTICAL)
d14a1e28 190 ssizer.Add(self.solid, 0, 0)
7722248d 191 ssizer.Add((1, 2))
b881fc78 192 ssizer.Add(slabel, 0, wx.ALIGN_CENTER_HORIZONTAL)
d14a1e28 193
b881fc78
RD
194 hlabel = wx.StaticText(self, -1, _("H:"))
195 self.hentry = wx.TextCtrl(self, -1)
d14a1e28 196 self.hentry.SetSize((40, -1))
b881fc78
RD
197 slabel = wx.StaticText(self, -1, _("S:"))
198 self.sentry = wx.TextCtrl(self, -1)
d14a1e28 199 self.sentry.SetSize((40, -1))
b881fc78
RD
200 vlabel = wx.StaticText(self, -1, _("V:"))
201 self.ventry = wx.TextCtrl(self, -1)
d14a1e28 202 self.ventry.SetSize((40, -1))
b881fc78 203 hsvgrid = wx.FlexGridSizer(1, 6, 2, 2)
d14a1e28 204 hsvgrid.AddMany ([
b881fc78
RD
205 (hlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.hentry, 0, 0),
206 (slabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.sentry, 0, 0),
207 (vlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.ventry, 0, 0),
d14a1e28
RD
208 ])
209
b881fc78
RD
210 rlabel = wx.StaticText(self, -1, _("R:"))
211 self.rentry = wx.TextCtrl(self, -1)
d14a1e28 212 self.rentry.SetSize((40, -1))
b881fc78
RD
213 glabel = wx.StaticText(self, -1, _("G:"))
214 self.gentry = wx.TextCtrl(self, -1)
d14a1e28 215 self.gentry.SetSize((40, -1))
b881fc78
RD
216 blabel = wx.StaticText(self, -1, _("B:"))
217 self.bentry = wx.TextCtrl(self, -1)
d14a1e28 218 self.bentry.SetSize((40, -1))
b881fc78 219 lgrid = wx.FlexGridSizer(1, 6, 2, 2)
d14a1e28 220 lgrid.AddMany([
b881fc78
RD
221 (rlabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.rentry, 0, 0),
222 (glabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.gentry, 0, 0),
223 (blabel, 0, wx.ALIGN_CENTER_VERTICAL), (self.bentry, 0, 0),
d14a1e28
RD
224 ])
225
b881fc78 226 gsizer = wx.GridSizer(2, 1)
d14a1e28
RD
227 gsizer.SetVGap (10)
228 gsizer.SetHGap (2)
b881fc78
RD
229 gsizer.Add(hsvgrid, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
230 gsizer.Add(lgrid, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
d14a1e28 231
b881fc78
RD
232 hsizer = wx.BoxSizer(wx.HORIZONTAL)
233 hsizer.Add(ssizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
234 hsizer.Add(gsizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
d14a1e28 235
b881fc78 236 vsizer = wx.BoxSizer(wx.VERTICAL)
7722248d 237 vsizer.Add((1, 5))
d14a1e28 238 vsizer.Add(psizer, 0, 0)
7722248d 239 vsizer.Add((1, 15))
b881fc78 240 vsizer.Add(hsizer, 0, wx.EXPAND)
d14a1e28 241
b881fc78 242 sizer = wx.BoxSizer(wx.HORIZONTAL)
7722248d 243 sizer.Add((5, 1))
b881fc78 244 sizer.Add(csizer, 0, wx.EXPAND)
7722248d 245 sizer.Add((10, 1))
b881fc78 246 sizer.Add(vsizer, 0, wx.EXPAND)
d14a1e28
RD
247 self.SetAutoLayout(True)
248 self.SetSizer(sizer)
249 sizer.Fit(self)
250
251 self.InitColours()
252 self.UpdateColour(self.solid.GetColour())
253
254 def InitColours(self):
255 """Initializes the pre-set palette colours."""
256 for i in range(len(self.colour_names)):
b881fc78 257 colour = wx.TheColourDatabase.FindColour(self.colour_names[i])
d14a1e28
RD
258 self.colour_boxs[i].SetColourTuple((colour.Red(),
259 colour.Green(),
260 colour.Blue()))
261
262 def onBasicClick(self, event, box):
263 """Highlights the selected colour box and updates the solid colour
264 display and colour slider to reflect the choice."""
265 if hasattr(self, '_old_custom_highlight'):
266 self._old_custom_highlight.SetHighlight(False)
267 if hasattr(self, '_old_colour_highlight'):
268 self._old_colour_highlight.SetHighlight(False)
269 box.SetHighlight(True)
270 self._old_colour_highlight = box
271 self.UpdateColour(box.GetColour())
272
273 def onCustomClick(self, event, box):
274 """Highlights the selected custom colour box and updates the solid
275 colour display and colour slider to reflect the choice."""
276 if hasattr(self, '_old_colour_highlight'):
277 self._old_colour_highlight.SetHighlight(False)
278 if hasattr(self, '_old_custom_highlight'):
279 self._old_custom_highlight.SetHighlight(False)
280 box.SetHighlight(True)
281 self._old_custom_highlight = box
282
283 # Update the colour panel and then the slider accordingly
284 box_index = self.custom_boxs.index(box)
285 base_colour, slidepos = self.custom_colours[box_index]
286 self.UpdateColour(box.GetColour())
287 self.slider.SetValue(slidepos)
288
289 def onAddCustom(self, event):
290 """Adds a custom colour to the custom colour box set. Boxes are
291 chosen in a round-robin fashion, eventually overwriting previously
292 added colours."""
293 # Store the colour and slider position so we can restore the
294 # custom colours just as they were
295 self.setCustomColour(self.last_custom,
296 self.solid.GetColour(),
297 self.colour_slider.GetBaseColour(),
298 self.slider.GetValue())
299 self.last_custom = (self.last_custom + 1) % self.NO_CUSTOM_COLOURS
300
301 def setCustomColour (self, index, true_colour, base_colour, slidepos):
302 """Sets the custom colour at the given index. true_colour is wxColour
303 object containing the actual rgb value of the custom colour.
304 base_colour (wxColour) and slidepos (int) are used to configure the
305 colour slider and set everything to its original position."""
306 self.custom_boxs[index].SetColour(true_colour)
307 self.custom_colours[index] = (base_colour, slidepos)
308
309 def UpdateColour(self, colour):
310 """Performs necessary updates for when the colour selection has
311 changed."""
312 # Reset the palette to erase any highlighting
313 self.palette.ReDraw()
314
315 # Set the color info
316 self.solid.SetColour(colour)
317 self.colour_slider.SetBaseColour(colour)
318 self.colour_slider.ReDraw()
319 self.slider.SetValue(0)
320 self.UpdateEntries(colour)
321
322 def UpdateEntries(self, colour):
323 """Updates the color levels to display the new values."""
324 # Temporary bindings
325 r = colour.Red()
326 g = colour.Green()
327 b = colour.Blue()
328
329 # Update the RGB entries
330 self.rentry.SetValue(str(r))
331 self.gentry.SetValue(str(g))
332 self.bentry.SetValue(str(b))
333
334 # Convert to HSV
335 h,s,v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)
336 self.hentry.SetValue("%.2f" % (h))
337 self.sentry.SetValue("%.2f" % (s))
338 self.ventry.SetValue("%.2f" % (v))
339
340 def onPaletteDown(self, event):
341 """Stores state that the mouse has been pressed and updates
342 the selected colour values."""
343 self.mouse_down = True
344 self.palette.ReDraw()
345 self.doPaletteClick(event.m_x, event.m_y)
346
347 def onPaletteUp(self, event):
348 """Stores state that the mouse is no longer depressed."""
349 self.mouse_down = False
350
351 def onPaletteMotion(self, event):
352 """Updates the colour values during mouse motion while the
353 mouse button is depressed."""
354 if self.mouse_down:
355 self.doPaletteClick(event.m_x, event.m_y)
356
357 def doPaletteClick(self, m_x, m_y):
358 """Updates the colour values based on the mouse location
359 over the palette."""
360 # Get the colour value and update
361 colour = self.palette.GetValue(m_x, m_y)
362 self.UpdateColour(colour)
363
364 # Highlight a fresh selected area
365 self.palette.ReDraw()
366 self.palette.HighlightPoint(m_x, m_y)
367
368 # Force an onscreen update
369 self.solid.Update()
370 self.colour_slider.Refresh()
371
372 def onScroll(self, event):
373 """Updates the solid colour display to reflect the changing slider."""
374 value = self.slider.GetValue()
375 colour = self.colour_slider.GetValue(value)
376 self.solid.SetColour(colour)
377 self.UpdateEntries(colour)
378
379 def SetValue(self, colour):
380 """Updates the colour chooser to reflect the given wxColour."""
381 self.UpdateColour(colour)
382
383 def GetValue(self):
384 """Returns a wxColour object indicating the current colour choice."""
385 return self.solid.GetColour()
386
387def main():
388 """Simple test display."""
b881fc78 389 class App(wx.App):
d14a1e28 390 def OnInit(self):
b881fc78 391 frame = wx.Frame(None, -1, 'PyColourChooser Test')
d14a1e28 392
d4b73b1b
RD
393 # Added here because that's where it's supposed to be,
394 # not embedded in the library. If it's embedded in the
395 # library, debug messages will be generated for duplicate
396 # handlers.
397 wx.InitAllImageHandlers()
398
399 chooser = PyColourChooser(frame, -1)
b881fc78 400 sizer = wx.BoxSizer(wx.VERTICAL)
d14a1e28
RD
401 sizer.Add(chooser, 0, 0)
402 frame.SetAutoLayout(True)
403 frame.SetSizer(sizer)
404 sizer.Fit(frame)
405
406 frame.Show(True)
407 self.SetTopWindow(frame)
408 return True
b881fc78 409 app = App(False)
d14a1e28
RD
410 app.MainLoop()
411
412if __name__ == '__main__':
413 main()