]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/colourchooser/pycolourchooser.py
Oops
[wxWidgets.git] / wxPython / wx / lib / colourchooser / pycolourchooser.py
1 """
2 wxPyColourChooser
3 Copyright (C) 2002 Michael Gilfix <mgilfix@eecs.tufts.edu>
4
5 This file is part of wxPyColourChooser.
6
7 This version of wxPyColourChooser is open source; you can redistribute it
8 and/or modify it under the licensed terms.
9
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.
13 """
14
15 # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net)
16 #
17 # o 2.5 compatability update.
18 #
19
20 import wx
21
22 import pycolourbox
23 import pypalette
24 import pycolourslider
25 import colorsys
26 import intl
27
28 from intl import _ # _
29
30 class wxPyColourChooser(wx.Panel):
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
99 custom_colours = [ (wx.Colour(255, 255, 255),
100 pycolourslider.PyColourSlider.HEIGHT / 2)
101 ] * NO_CUSTOM_COLOURS
102 last_custom = 0
103
104 idADD_CUSTOM = wx.NewId()
105 idSCROLL = wx.NewId()
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."""
111 wx.Panel.__init__(self, parent, id)
112
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"))
116
117 self.Bind(wx.EVT_BUTTON, self.onAddCustom, self.add_button)
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
121 wx.Yield()
122
123 # Create the basic colours palette
124 self.colour_boxs = [ ]
125 colour_grid = wx.GridSizer(6, 8)
126 for name in self.colour_names:
127 new_id = wx.NewId()
128 box = pycolourbox.PyColourBox(self, new_id)
129
130 box.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=box: self.onBasicClick(x, b))
131
132 self.colour_boxs.append(box)
133 colour_grid.Add(box, 0, wx.EXPAND)
134
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:
139 new_id = wx.NewId()
140 custom = pycolourbox.PyColourBox(self, new_id)
141
142 custom.GetColourBox().Bind(wx.EVT_LEFT_DOWN, lambda x, b=custom: self.onCustomClick(x, b))
143
144 custom.SetColour(wxcolour)
145 custom_grid.Add(custom, 0, wx.EXPAND)
146 self.custom_boxs.append(custom)
147
148 csizer = wx.BoxSizer(wx.VERTICAL)
149 csizer.Add((1, 25))
150 csizer.Add(self.basic_label, 0, wx.EXPAND)
151 csizer.Add((1, 5))
152 csizer.Add(colour_grid, 0, wx.EXPAND)
153 csizer.Add((1, 25))
154 csizer.Add(self.custom_label, 0, wx.EXPAND)
155 csizer.Add((1, 5))
156 csizer.Add(custom_grid, 0, wx.EXPAND)
157 csizer.Add((1, 5))
158 csizer.Add(self.add_button, 0, wx.EXPAND)
159
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)
165 )
166
167 self.Bind(wx.EVT_COMMAND_SCROLL, self.onScroll, self.slider)
168 psizer = wx.BoxSizer(wx.HORIZONTAL)
169 psizer.Add(self.palette, 0, 0)
170 psizer.Add((10, 1))
171 psizer.Add(self.colour_slider, 0, wx.ALIGN_CENTER_VERTICAL)
172 psizer.Add(self.slider, 0, wx.ALIGN_CENTER_VERTICAL)
173
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
179
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)
184 ssizer.Add((1, 2))
185 ssizer.Add(slabel, 0, wx.ALIGN_CENTER_HORIZONTAL)
186
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)
197 hsvgrid.AddMany ([
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),
201 ])
202
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)
213 lgrid.AddMany([
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),
217 ])
218
219 gsizer = wx.GridSizer(2, 1)
220 gsizer.SetVGap (10)
221 gsizer.SetHGap (2)
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)
224
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)
228
229 vsizer = wx.BoxSizer(wx.VERTICAL)
230 vsizer.Add((1, 5))
231 vsizer.Add(psizer, 0, 0)
232 vsizer.Add((1, 15))
233 vsizer.Add(hsizer, 0, wx.EXPAND)
234
235 sizer = wx.BoxSizer(wx.HORIZONTAL)
236 sizer.Add((5, 1))
237 sizer.Add(csizer, 0, wx.EXPAND)
238 sizer.Add((10, 1))
239 sizer.Add(vsizer, 0, wx.EXPAND)
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)):
250 colour = wx.TheColourDatabase.FindColour(self.colour_names[i])
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
380 def main():
381 """Simple test display."""
382 class App(wx.App):
383 def OnInit(self):
384 frame = wx.Frame(None, -1, 'PyColourChooser Test')
385
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)
391 sizer.Fit(frame)
392
393 frame.Show(True)
394 self.SetTopWindow(frame)
395 return True
396 app = App(False)
397 app.MainLoop()
398
399 if __name__ == '__main__':
400 main()