source: anuga_core/source/anuga/pmesh/PmwColor.py @ 5571

Last change on this file since 5571 was 3489, checked in by duncan, 18 years ago

adding copyright to pmw code.

File size: 12.4 KB
Line 
1"""
2Pmw copyright
3
4Copyright 1997-1999 Telstra Corporation Limited,
5Australia Copyright 2000-2002 Really Good Software Pty Ltd, Australia
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26"""
27
28
29# Functions for converting colors and modifying the color scheme of
30# an application.
31
32import math
33import string
34import sys
35import Tkinter
36
37_PI = math.pi
38_TWO_PI = _PI * 2
39_THIRD_PI = _PI / 3
40_SIXTH_PI = _PI / 6
41_MAX_RGB = float(256 * 256 - 1) # max size of rgb values returned from Tk
42
43def setscheme(root, background=None, **kw):
44    root = root._root()
45    palette = apply(_calcPalette, (root, background,), kw)
46    for option, value in palette.items():
47        root.option_add('*' + option, value, 'widgetDefault')
48
49def getdefaultpalette(root):
50    # Return the default values of all options, using the defaults
51    # from a few widgets.
52
53    ckbtn = Tkinter.Checkbutton(root)
54    entry = Tkinter.Entry(root)
55    scbar = Tkinter.Scrollbar(root)
56
57    orig = {}
58    orig['activeBackground'] = str(ckbtn.configure('activebackground')[4])
59    orig['activeForeground'] = str(ckbtn.configure('activeforeground')[4])
60    orig['background'] = str(ckbtn.configure('background')[4])
61    orig['disabledForeground'] = str(ckbtn.configure('disabledforeground')[4])
62    orig['foreground'] = str(ckbtn.configure('foreground')[4])
63    orig['highlightBackground'] = str(ckbtn.configure('highlightbackground')[4])
64    orig['highlightColor'] = str(ckbtn.configure('highlightcolor')[4])
65    orig['insertBackground'] = str(entry.configure('insertbackground')[4])
66    orig['selectColor'] = str(ckbtn.configure('selectcolor')[4])
67    orig['selectBackground'] = str(entry.configure('selectbackground')[4])
68    orig['selectForeground'] = str(entry.configure('selectforeground')[4])
69    orig['troughColor'] = str(scbar.configure('troughcolor')[4])
70
71    ckbtn.destroy()
72    entry.destroy()
73    scbar.destroy()
74
75    return orig
76
77#======================================================================
78
79# Functions dealing with brightness, hue, saturation and intensity of colors.
80
81def changebrightness(root, colorName, brightness):
82    # Convert the color name into its hue and back into a color of the
83    # required brightness.
84
85    rgb = name2rgb(root, colorName)
86    hue, saturation, intensity = rgb2hsi(rgb)
87    if saturation == 0.0:
88        hue = None
89    return hue2name(hue, brightness)
90
91def hue2name(hue, brightness = None):
92    # Convert the requested hue and brightness into a color name.  If
93    # hue is None, return a grey of the requested brightness.
94
95    if hue is None:
96        rgb = hsi2rgb(0.0, 0.0, brightness)
97    else:
98        while hue < 0:
99            hue = hue + _TWO_PI
100        while hue >= _TWO_PI:
101            hue = hue - _TWO_PI
102
103        rgb = hsi2rgb(hue, 1.0, 1.0)
104        if brightness is not None:
105            b = rgb2brightness(rgb)
106            i = 1.0 - (1.0 - brightness) * b
107            s = bhi2saturation(brightness, hue, i)
108            rgb = hsi2rgb(hue, s, i)
109
110    return rgb2name(rgb)
111
112def bhi2saturation(brightness, hue, intensity):
113    while hue < 0:
114        hue = hue + _TWO_PI
115    while hue >= _TWO_PI:
116        hue = hue - _TWO_PI
117    hue = hue / _THIRD_PI
118    f = hue - math.floor(hue)
119
120    pp = intensity
121    pq = intensity * f
122    pt = intensity - intensity * f
123    pv = 0
124
125    hue = int(hue)
126    if   hue == 0: rgb = (pv, pt, pp)
127    elif hue == 1: rgb = (pq, pv, pp)
128    elif hue == 2: rgb = (pp, pv, pt)
129    elif hue == 3: rgb = (pp, pq, pv)
130    elif hue == 4: rgb = (pt, pp, pv)
131    elif hue == 5: rgb = (pv, pp, pq)
132
133    return (intensity - brightness) / rgb2brightness(rgb)
134
135def hsi2rgb(hue, saturation, intensity):
136    i = intensity
137    if saturation == 0:
138        rgb = [i, i, i]
139    else:
140        while hue < 0:
141            hue = hue + _TWO_PI
142        while hue >= _TWO_PI:
143            hue = hue - _TWO_PI
144        hue = hue / _THIRD_PI
145        f = hue - math.floor(hue)
146        p = i * (1.0 - saturation)
147        q = i * (1.0 - saturation * f)
148        t = i * (1.0 - saturation * (1.0 - f))
149
150        hue = int(hue)
151        if   hue == 0: rgb = [i, t, p]
152        elif hue == 1: rgb = [q, i, p]
153        elif hue == 2: rgb = [p, i, t]
154        elif hue == 3: rgb = [p, q, i]
155        elif hue == 4: rgb = [t, p, i]
156        elif hue == 5: rgb = [i, p, q]
157
158    for index in range(3):
159        val = rgb[index]
160        if val < 0.0:
161            val = 0.0
162        if val > 1.0:
163            val = 1.0
164        rgb[index] = val
165
166    return rgb
167
168def average(rgb1, rgb2, fraction):
169    return (
170        rgb2[0] * fraction + rgb1[0] * (1.0 - fraction),
171        rgb2[1] * fraction + rgb1[1] * (1.0 - fraction),
172        rgb2[2] * fraction + rgb1[2] * (1.0 - fraction)
173    )
174
175def rgb2name(rgb):
176    return '#%02x%02x%02x' % \
177        (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
178
179def rgb2brightness(rgb):
180    # Return the perceived grey level of the color
181    # (0.0 == black, 1.0 == white).
182
183    rf = 0.299
184    gf = 0.587
185    bf = 0.114
186    return rf * rgb[0] + gf * rgb[1] + bf * rgb[2]
187
188def rgb2hsi(rgb):
189    maxc = max(rgb[0], rgb[1], rgb[2])
190    minc = min(rgb[0], rgb[1], rgb[2])
191
192    intensity = maxc
193    if maxc != 0:
194      saturation  = (maxc - minc) / maxc
195    else:
196      saturation = 0.0
197
198    hue = 0.0
199    if saturation != 0.0:
200        c = []
201        for index in range(3):
202            c.append((maxc - rgb[index]) / (maxc - minc))
203
204        if rgb[0] == maxc:
205            hue = c[2] - c[1]
206        elif rgb[1] == maxc:
207            hue = 2 + c[0] - c[2]
208        elif rgb[2] == maxc:
209            hue = 4 + c[1] - c[0]
210
211        hue = hue * _THIRD_PI
212        if hue < 0.0:
213            hue = hue + _TWO_PI
214
215    return (hue, saturation, intensity)
216
217def name2rgb(root, colorName, asInt = 0):
218    if colorName[0] == '#':
219        # Extract rgb information from the color name itself, assuming
220        # it is either #rgb, #rrggbb, #rrrgggbbb, or #rrrrggggbbbb
221        # This is useful, since tk may return incorrect rgb values if
222        # the colormap is full - it will return the rbg values of the
223        # closest color available.
224        colorName = colorName[1:]
225        digits = len(colorName) / 3
226        factor = 16 ** (4 - digits)
227        rgb = (
228            string.atoi(colorName[0:digits], 16) * factor,
229            string.atoi(colorName[digits:digits * 2], 16) * factor,
230            string.atoi(colorName[digits * 2:digits * 3], 16) * factor,
231        )
232    else:
233        # We have no choice but to ask Tk what the rgb values are.
234        rgb = root.winfo_rgb(colorName)
235
236    if not asInt:
237        rgb = (rgb[0] / _MAX_RGB, rgb[1] / _MAX_RGB, rgb[2] / _MAX_RGB)
238    return rgb
239
240def _calcPalette(root, background=None, **kw):
241    # Create a map that has the complete new palette.  If some colors
242    # aren't specified, compute them from other colors that are specified.
243    new = {}
244    for key, value in kw.items():
245        new[key] = value
246    if background is not None:
247        new['background'] = background
248    if not new.has_key('background'):
249        raise ValueError, 'must specify a background color'
250
251    if not new.has_key('foreground'):
252        new['foreground'] = 'black'
253
254    bg = name2rgb(root, new['background'])
255    fg = name2rgb(root, new['foreground'])
256
257    for i in ('activeForeground', 'insertBackground', 'selectForeground',
258            'highlightColor'):
259        if not new.has_key(i):
260            new[i] = new['foreground']
261
262    if not new.has_key('disabledForeground'):
263        newCol = average(bg, fg, 0.3)
264        new['disabledForeground'] = rgb2name(newCol)
265
266    if not new.has_key('highlightBackground'):
267        new['highlightBackground'] = new['background']
268
269    # Set <lighterBg> to a color that is a little lighter that the
270    # normal background.  To do this, round each color component up by
271    # 9% or 1/3 of the way to full white, whichever is greater.
272    lighterBg = []
273    for i in range(3):
274        lighterBg.append(bg[i])
275        inc1 = lighterBg[i] * 0.09
276        inc2 = (1.0 - lighterBg[i]) / 3
277        if inc1 > inc2:
278            lighterBg[i] = lighterBg[i] + inc1
279        else:
280            lighterBg[i] = lighterBg[i] + inc2
281        if lighterBg[i] > 1.0:
282            lighterBg[i] = 1.0
283
284    # Set <darkerBg> to a color that is a little darker that the
285    # normal background.
286    darkerBg = (bg[0] * 0.9, bg[1] * 0.9, bg[2] * 0.9)
287
288    if not new.has_key('activeBackground'):
289        # If the foreground is dark, pick a light active background.
290        # If the foreground is light, pick a dark active background.
291        # XXX This has been disabled, since it does not look very
292        # good with dark backgrounds. If this is ever fixed, the
293        # selectBackground and troughColor options should also be fixed.
294
295        if rgb2brightness(fg) < 0.5:
296            new['activeBackground'] = rgb2name(lighterBg)
297        else:
298            new['activeBackground'] = rgb2name(lighterBg)
299
300    if not new.has_key('selectBackground'):
301        new['selectBackground'] = rgb2name(darkerBg)
302    if not new.has_key('troughColor'):
303        new['troughColor'] = rgb2name(darkerBg)
304    if not new.has_key('selectColor'):
305        new['selectColor'] = 'yellow'
306
307    return new
308
309def spectrum(numColors, correction = 1.0, saturation = 1.0, intensity = 1.0,
310        extraOrange = 1, returnHues = 0):
311    colorList = []
312    division = numColors / 7.0
313    for index in range(numColors):
314        if extraOrange:
315            if index < 2 * division:
316                hue = index / division
317            else:
318                hue = 2 + 2 * (index - 2 * division) / division
319            hue = hue * _SIXTH_PI
320        else:
321            hue = index * _TWO_PI / numColors
322        if returnHues:
323            colorList.append(hue)
324        else:
325            rgb = hsi2rgb(hue, saturation, intensity)
326            if correction != 1.0:
327                rgb = correct(rgb, correction)
328            name = rgb2name(rgb)
329            colorList.append(name)
330    return colorList
331
332def correct(rgb, correction):
333    correction = float(correction)
334    rtn = []
335    for index in range(3):
336        rtn.append((1 - (1 - rgb[index]) ** correction) ** (1 / correction))
337    return rtn
338
339#==============================================================================
340
341def _recolorTree(widget, oldpalette, newcolors):
342    # Change the colors in a widget and its descendants.
343
344    # Change the colors in <widget> and all of its descendants,
345    # according to the <newcolors> dictionary.  It only modifies
346    # colors that have their default values as specified by the
347    # <oldpalette> variable.  The keys of the <newcolors> dictionary
348    # are named after widget configuration options and the values are
349    # the new value for that option.
350
351    for dbOption in newcolors.keys():
352        option = string.lower(dbOption)
353        try:
354            value = str(widget.cget(option))
355        except:
356            continue
357        if oldpalette is None or value == oldpalette[dbOption]:
358            apply(widget.configure, (), {option : newcolors[dbOption]})
359
360    for child in widget.winfo_children():
361       _recolorTree(child, oldpalette, newcolors)
362
363def changecolor(widget, background=None, **kw):
364     root = widget._root()
365     if not hasattr(widget, '_Pmw_oldpalette'):
366         widget._Pmw_oldpalette = getdefaultpalette(root)
367     newpalette = apply(_calcPalette, (root, background,), kw)
368     _recolorTree(widget, widget._Pmw_oldpalette, newpalette)
369     widget._Pmw_oldpalette = newpalette
370
371def bordercolors(root, colorName):
372    # This is the same method that Tk uses for shadows, in TkpGetShadows.
373
374    lightRGB = []
375    darkRGB = []
376    for value in name2rgb(root, colorName, 1):
377        value40pc = (14 * value) / 10
378        if value40pc > _MAX_RGB:
379            value40pc = _MAX_RGB
380        valueHalfWhite = (_MAX_RGB + value) / 2;
381        lightRGB.append(max(value40pc, valueHalfWhite))
382
383        darkValue = (60 * value) / 100
384        darkRGB.append(darkValue)
385
386    return (
387        '#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
388        '#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
389    )
Note: See TracBrowser for help on using the repository browser.