]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/colourchooser/pycolourchooser.py
Merged the wxPy_newswig branch into the HEAD branch (main trunk)
[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 import pycolourbox
16 import pypalette
17 import pycolourslider
18 import colorsys
19 from intl import _
20 from wxPython.wx import *
21
22 class wxPyColourChooser(wxPanel):
23 """A Pure-Python implementation of the colour chooser dialog.
24
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.
30 """
31
32 colour_names = [
33 'ORANGE',
34 'GOLDENROD',
35 'WHEAT',
36 'SPRING GREEN',
37 'SKY BLUE',
38 'SLATE BLUE',
39 'MEDIUM VIOLET RED',
40 'PURPLE',
41
42 'RED',
43 'YELLOW',
44 'MEDIUM SPRING GREEN',
45 'PALE GREEN',
46 'CYAN',
47 'LIGHT STEEL BLUE',
48 'ORCHID',
49 'LIGHT MAGENTA',
50
51 'BROWN',
52 'YELLOW',
53 'GREEN',
54 'CADET BLUE',
55 'MEDIUM BLUE',
56 'MAGENTA',
57 'MAROON',
58 'ORANGE RED',
59
60 'FIREBRICK',
61 'CORAL',
62 'FOREST GREEN',
63 'AQUAMARINE',
64 'BLUE',
65 'NAVY',
66 'THISTLE',
67 'MEDIUM VIOLET RED',
68
69 'INDIAN RED',
70 'GOLD',
71 'MEDIUM SEA GREEN',
72 'MEDIUM BLUE',
73 'MIDNIGHT BLUE',
74 'GREY',
75 'PURPLE',
76 'KHAKI',
77
78 'BLACK',
79 'MEDIUM FOREST GREEN',
80 'KHAKI',
81 'DARK GREY',
82 'SEA GREEN',
83 'LIGHT GREY',
84 'MEDIUM SLATE BLUE',
85 'WHITE',
86 ]
87
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)
93 ] * NO_CUSTOM_COLOURS
94 last_custom = 0
95
96 idADD_CUSTOM = wxNewId()
97 idSCROLL = wxNewId()
98
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
102 resizeable."""
103 wxPanel.__init__(self, parent, id)
104
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"))
108
109 EVT_BUTTON(self, self.idADD_CUSTOM, self.onAddCustom)
110
111 # Since we're going to be constructing widgets that require some serious
112 # computation, let's process any events (like redraws) right now
113 wxYield()
114
115 # Create the basic colours palette
116 self.colour_boxs = [ ]
117 colour_grid = wxGridSizer(6, 8)
118 for name in self.colour_names:
119 new_id = wxNewId()
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)
124
125 # Create the custom colours palette
126 self.custom_boxs = [ ]
127 custom_grid = wxGridSizer(2, 8)
128 for wxcolour, slidepos in self.custom_colours:
129 new_id = wxNewId()
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)
135
136 csizer = wxBoxSizer(wxVERTICAL)
137 csizer.Add(1, 25)
138 csizer.Add(self.basic_label, 0, wxEXPAND)
139 csizer.Add(1, 5)
140 csizer.Add(colour_grid, 0, wxEXPAND)
141 csizer.Add(1, 25)
142 csizer.Add(self.custom_label, 0, wxEXPAND)
143 csizer.Add(1, 5)
144 csizer.Add(custom_grid, 0, wxEXPAND)
145 csizer.Add(1, 5)
146 csizer.Add(self.add_button, 0, wxEXPAND)
147
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)
155 psizer.Add(10, 1)
156 psizer.Add(self.colour_slider, 0, wxALIGN_CENTER_VERTICAL)
157 psizer.Add(self.slider, 0, wxALIGN_CENTER_VERTICAL)
158
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
164
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)
169 ssizer.Add(1, 2)
170 ssizer.Add(slabel, 0, wxALIGN_CENTER_HORIZONTAL)
171
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)
182 hsvgrid.AddMany ([
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),
186 ])
187
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)
198 lgrid.AddMany([
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),
202 ])
203
204 gsizer = wxGridSizer(2, 1)
205 gsizer.SetVGap (10)
206 gsizer.SetHGap (2)
207 gsizer.Add(hsvgrid, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
208 gsizer.Add(lgrid, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL)
209
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)
213
214 vsizer = wxBoxSizer(wxVERTICAL)
215 vsizer.Add(1, 5)
216 vsizer.Add(psizer, 0, 0)
217 vsizer.Add(1, 15)
218 vsizer.Add(hsizer, 0, wxEXPAND)
219
220 sizer = wxBoxSizer(wxHORIZONTAL)
221 sizer.Add(5, 1)
222 sizer.Add(csizer, 0, wxEXPAND)
223 sizer.Add(10, 1)
224 sizer.Add(vsizer, 0, wxEXPAND)
225 self.SetAutoLayout(True)
226 self.SetSizer(sizer)
227 sizer.Fit(self)
228
229 self.InitColours()
230 self.UpdateColour(self.solid.GetColour())
231
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(),
237 colour.Green(),
238 colour.Blue()))
239
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())
250
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
260
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)
266
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
270 added colours."""
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
278
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)
286
287 def UpdateColour(self, colour):
288 """Performs necessary updates for when the colour selection has
289 changed."""
290 # Reset the palette to erase any highlighting
291 self.palette.ReDraw()
292
293 # Set the color info
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)
299
300 def UpdateEntries(self, colour):
301 """Updates the color levels to display the new values."""
302 # Temporary bindings
303 r = colour.Red()
304 g = colour.Green()
305 b = colour.Blue()
306
307 # Update the RGB entries
308 self.rentry.SetValue(str(r))
309 self.gentry.SetValue(str(g))
310 self.bentry.SetValue(str(b))
311
312 # Convert to HSV
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))
317
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)
324
325 def onPaletteUp(self, event):
326 """Stores state that the mouse is no longer depressed."""
327 self.mouse_down = False
328
329 def onPaletteMotion(self, event):
330 """Updates the colour values during mouse motion while the
331 mouse button is depressed."""
332 if self.mouse_down:
333 self.doPaletteClick(event.m_x, event.m_y)
334
335 def doPaletteClick(self, m_x, m_y):
336 """Updates the colour values based on the mouse location
337 over the palette."""
338 # Get the colour value and update
339 colour = self.palette.GetValue(m_x, m_y)
340 self.UpdateColour(colour)
341
342 # Highlight a fresh selected area
343 self.palette.ReDraw()
344 self.palette.HighlightPoint(m_x, m_y)
345
346 # Force an onscreen update
347 self.solid.Update()
348 self.colour_slider.Refresh()
349
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)
356
357 def SetValue(self, colour):
358 """Updates the colour chooser to reflect the given wxColour."""
359 self.UpdateColour(colour)
360
361 def GetValue(self):
362 """Returns a wxColour object indicating the current colour choice."""
363 return self.solid.GetColour()
364
365 def main():
366 """Simple test display."""
367 class App(wxApp):
368 def OnInit(self):
369 frame = wxFrame(NULL, -1, 'PyColourChooser Test')
370
371 chooser = wxPyColourChooser(frame, -1)
372 sizer = wxBoxSizer(wxVERTICAL)
373 sizer.Add(chooser, 0, 0)
374 frame.SetAutoLayout(True)
375 frame.SetSizer(sizer)
376 sizer.Fit(frame)
377
378 frame.Show(True)
379 self.SetTopWindow(frame)
380 return True
381 app = App()
382 app.MainLoop()
383
384 if __name__ == '__main__':
385 main()