python-3.6.zip added from Github

README.cosmo contains the necessary links.
This commit is contained in:
ahgamut 2021-08-08 09:38:33 +05:30 committed by Justine Tunney
parent 75fc601ff5
commit 0c4c56ff39
4219 changed files with 1968626 additions and 0 deletions

View file

@ -0,0 +1,130 @@
"""Chip viewer and widget.
In the lower left corner of the main Pynche window, you will see two
ChipWidgets, one for the selected color and one for the nearest color. The
selected color is the actual RGB value expressed as an X11 #COLOR name. The
nearest color is the named color from the X11 database that is closest to the
selected color in 3D space. There may be other colors equally close, but the
nearest one is the first one found.
Clicking on the nearest color chip selects that named color.
The ChipViewer class includes the entire lower left quandrant; i.e. both the
selected and nearest ChipWidgets.
"""
from tkinter import *
import ColorDB
class ChipWidget:
_WIDTH = 150
_HEIGHT = 80
def __init__(self,
master = None,
width = _WIDTH,
height = _HEIGHT,
text = 'Color',
initialcolor = 'blue',
presscmd = None,
releasecmd = None):
# create the text label
self.__label = Label(master, text=text)
self.__label.grid(row=0, column=0)
# create the color chip, implemented as a frame
self.__chip = Frame(master, relief=RAISED, borderwidth=2,
width=width,
height=height,
background=initialcolor)
self.__chip.grid(row=1, column=0)
# create the color name
self.__namevar = StringVar()
self.__namevar.set(initialcolor)
self.__name = Entry(master, textvariable=self.__namevar,
relief=FLAT, justify=CENTER, state=DISABLED,
font=self.__label['font'])
self.__name.grid(row=2, column=0)
# create the message area
self.__msgvar = StringVar()
self.__name = Entry(master, textvariable=self.__msgvar,
relief=FLAT, justify=CENTER, state=DISABLED,
font=self.__label['font'])
self.__name.grid(row=3, column=0)
# set bindings
if presscmd:
self.__chip.bind('<ButtonPress-1>', presscmd)
if releasecmd:
self.__chip.bind('<ButtonRelease-1>', releasecmd)
def set_color(self, color):
self.__chip.config(background=color)
def get_color(self):
return self.__chip['background']
def set_name(self, colorname):
self.__namevar.set(colorname)
def set_message(self, message):
self.__msgvar.set(message)
def press(self):
self.__chip.configure(relief=SUNKEN)
def release(self):
self.__chip.configure(relief=RAISED)
class ChipViewer:
def __init__(self, switchboard, master=None):
self.__sb = switchboard
self.__frame = Frame(master, relief=RAISED, borderwidth=1)
self.__frame.grid(row=3, column=0, ipadx=5, sticky='NSEW')
# create the chip that will display the currently selected color
# exactly
self.__sframe = Frame(self.__frame)
self.__sframe.grid(row=0, column=0)
self.__selected = ChipWidget(self.__sframe, text='Selected')
# create the chip that will display the nearest real X11 color
# database color name
self.__nframe = Frame(self.__frame)
self.__nframe.grid(row=0, column=1)
self.__nearest = ChipWidget(self.__nframe, text='Nearest',
presscmd = self.__buttonpress,
releasecmd = self.__buttonrelease)
def update_yourself(self, red, green, blue):
# Selected always shows the #rrggbb name of the color, nearest always
# shows the name of the nearest color in the database. BAW: should
# an exact match be indicated in some way?
#
# Always use the #rrggbb style to actually set the color, since we may
# not be using X color names (e.g. "web-safe" names)
colordb = self.__sb.colordb()
rgbtuple = (red, green, blue)
rrggbb = ColorDB.triplet_to_rrggbb(rgbtuple)
# find the nearest
nearest = colordb.nearest(red, green, blue)
nearest_tuple = colordb.find_byname(nearest)
nearest_rrggbb = ColorDB.triplet_to_rrggbb(nearest_tuple)
self.__selected.set_color(rrggbb)
self.__nearest.set_color(nearest_rrggbb)
# set the name and messages areas
self.__selected.set_name(rrggbb)
if rrggbb == nearest_rrggbb:
self.__selected.set_message(nearest)
else:
self.__selected.set_message('')
self.__nearest.set_name(nearest_rrggbb)
self.__nearest.set_message(nearest)
def __buttonpress(self, event=None):
self.__nearest.press()
def __buttonrelease(self, event=None):
self.__nearest.release()
rrggbb = self.__nearest.get_color()
red, green, blue = ColorDB.rrggbb_to_triplet(rrggbb)
self.__sb.update_views(red, green, blue)

View file

@ -0,0 +1,271 @@
"""Color Database.
This file contains one class, called ColorDB, and several utility functions.
The class must be instantiated by the get_colordb() function in this file,
passing it a filename to read a database out of.
The get_colordb() function will try to examine the file to figure out what the
format of the file is. If it can't figure out the file format, or it has
trouble reading the file, None is returned. You can pass get_colordb() an
optional filetype argument.
Supporte file types are:
X_RGB_TXT -- X Consortium rgb.txt format files. Three columns of numbers
from 0 .. 255 separated by whitespace. Arbitrary trailing
columns used as the color name.
The utility functions are useful for converting between the various expected
color formats, and for calculating other color values.
"""
import sys
import re
from types import *
class BadColor(Exception):
pass
DEFAULT_DB = None
SPACE = ' '
COMMASPACE = ', '
# generic class
class ColorDB:
def __init__(self, fp):
lineno = 2
self.__name = fp.name
# Maintain several dictionaries for indexing into the color database.
# Note that while Tk supports RGB intensities of 4, 8, 12, or 16 bits,
# for now we only support 8 bit intensities. At least on OpenWindows,
# all intensities in the /usr/openwin/lib/rgb.txt file are 8-bit
#
# key is (red, green, blue) tuple, value is (name, [aliases])
self.__byrgb = {}
# key is name, value is (red, green, blue)
self.__byname = {}
# all unique names (non-aliases). built-on demand
self.__allnames = None
for line in fp:
# get this compiled regular expression from derived class
mo = self._re.match(line)
if not mo:
print('Error in', fp.name, ' line', lineno, file=sys.stderr)
lineno += 1
continue
# extract the red, green, blue, and name
red, green, blue = self._extractrgb(mo)
name = self._extractname(mo)
keyname = name.lower()
# BAW: for now the `name' is just the first named color with the
# rgb values we find. Later, we might want to make the two word
# version the `name', or the CapitalizedVersion, etc.
key = (red, green, blue)
foundname, aliases = self.__byrgb.get(key, (name, []))
if foundname != name and foundname not in aliases:
aliases.append(name)
self.__byrgb[key] = (foundname, aliases)
# add to byname lookup
self.__byname[keyname] = key
lineno = lineno + 1
# override in derived classes
def _extractrgb(self, mo):
return [int(x) for x in mo.group('red', 'green', 'blue')]
def _extractname(self, mo):
return mo.group('name')
def filename(self):
return self.__name
def find_byrgb(self, rgbtuple):
"""Return name for rgbtuple"""
try:
return self.__byrgb[rgbtuple]
except KeyError:
raise BadColor(rgbtuple)
def find_byname(self, name):
"""Return (red, green, blue) for name"""
name = name.lower()
try:
return self.__byname[name]
except KeyError:
raise BadColor(name)
def nearest(self, red, green, blue):
"""Return the name of color nearest (red, green, blue)"""
# BAW: should we use Voronoi diagrams, Delaunay triangulation, or
# octree for speeding up the locating of nearest point? Exhaustive
# search is inefficient, but seems fast enough.
nearest = -1
nearest_name = ''
for name, aliases in self.__byrgb.values():
r, g, b = self.__byname[name.lower()]
rdelta = red - r
gdelta = green - g
bdelta = blue - b
distance = rdelta * rdelta + gdelta * gdelta + bdelta * bdelta
if nearest == -1 or distance < nearest:
nearest = distance
nearest_name = name
return nearest_name
def unique_names(self):
# sorted
if not self.__allnames:
self.__allnames = []
for name, aliases in self.__byrgb.values():
self.__allnames.append(name)
self.__allnames.sort(key=str.lower)
return self.__allnames
def aliases_of(self, red, green, blue):
try:
name, aliases = self.__byrgb[(red, green, blue)]
except KeyError:
raise BadColor((red, green, blue))
return [name] + aliases
class RGBColorDB(ColorDB):
_re = re.compile(
r'\s*(?P<red>\d+)\s+(?P<green>\d+)\s+(?P<blue>\d+)\s+(?P<name>.*)')
class HTML40DB(ColorDB):
_re = re.compile(r'(?P<name>\S+)\s+(?P<hexrgb>#[0-9a-fA-F]{6})')
def _extractrgb(self, mo):
return rrggbb_to_triplet(mo.group('hexrgb'))
class LightlinkDB(HTML40DB):
_re = re.compile(r'(?P<name>(.+))\s+(?P<hexrgb>#[0-9a-fA-F]{6})')
def _extractname(self, mo):
return mo.group('name').strip()
class WebsafeDB(ColorDB):
_re = re.compile('(?P<hexrgb>#[0-9a-fA-F]{6})')
def _extractrgb(self, mo):
return rrggbb_to_triplet(mo.group('hexrgb'))
def _extractname(self, mo):
return mo.group('hexrgb').upper()
# format is a tuple (RE, SCANLINES, CLASS) where RE is a compiled regular
# expression, SCANLINES is the number of header lines to scan, and CLASS is
# the class to instantiate if a match is found
FILETYPES = [
(re.compile('Xorg'), RGBColorDB),
(re.compile('XConsortium'), RGBColorDB),
(re.compile('HTML'), HTML40DB),
(re.compile('lightlink'), LightlinkDB),
(re.compile('Websafe'), WebsafeDB),
]
def get_colordb(file, filetype=None):
colordb = None
fp = open(file)
try:
line = fp.readline()
if not line:
return None
# try to determine the type of RGB file it is
if filetype is None:
filetypes = FILETYPES
else:
filetypes = [filetype]
for typere, class_ in filetypes:
mo = typere.search(line)
if mo:
break
else:
# no matching type
return None
# we know the type and the class to grok the type, so suck it in
colordb = class_(fp)
finally:
fp.close()
# save a global copy
global DEFAULT_DB
DEFAULT_DB = colordb
return colordb
_namedict = {}
def rrggbb_to_triplet(color):
"""Converts a #rrggbb color to the tuple (red, green, blue)."""
rgbtuple = _namedict.get(color)
if rgbtuple is None:
if color[0] != '#':
raise BadColor(color)
red = color[1:3]
green = color[3:5]
blue = color[5:7]
rgbtuple = int(red, 16), int(green, 16), int(blue, 16)
_namedict[color] = rgbtuple
return rgbtuple
_tripdict = {}
def triplet_to_rrggbb(rgbtuple):
"""Converts a (red, green, blue) tuple to #rrggbb."""
global _tripdict
hexname = _tripdict.get(rgbtuple)
if hexname is None:
hexname = '#%02x%02x%02x' % rgbtuple
_tripdict[rgbtuple] = hexname
return hexname
def triplet_to_fractional_rgb(rgbtuple):
return [x / 256 for x in rgbtuple]
def triplet_to_brightness(rgbtuple):
# return the brightness (grey level) along the scale 0.0==black to
# 1.0==white
r = 0.299
g = 0.587
b = 0.114
return r*rgbtuple[0] + g*rgbtuple[1] + b*rgbtuple[2]
if __name__ == '__main__':
colordb = get_colordb('/usr/openwin/lib/rgb.txt')
if not colordb:
print('No parseable color database found')
sys.exit(1)
# on my system, this color matches exactly
target = 'navy'
red, green, blue = rgbtuple = colordb.find_byname(target)
print(target, ':', red, green, blue, triplet_to_rrggbb(rgbtuple))
name, aliases = colordb.find_byrgb(rgbtuple)
print('name:', name, 'aliases:', COMMASPACE.join(aliases))
r, g, b = (1, 1, 128) # nearest to navy
r, g, b = (145, 238, 144) # nearest to lightgreen
r, g, b = (255, 251, 250) # snow
print('finding nearest to', target, '...')
import time
t0 = time.time()
nearest = colordb.nearest(r, g, b)
t1 = time.time()
print('found nearest color', nearest, 'in', t1-t0, 'seconds')
# dump the database
for n in colordb.unique_names():
r, g, b = colordb.find_byname(n)
aliases = colordb.aliases_of(r, g, b)
print('%20s: (%3d/%3d/%3d) == %s' % (n, r, g, b,
SPACE.join(aliases[1:])))

View file

@ -0,0 +1,273 @@
"""DetailsViewer class.
This class implements a pure input window which allows you to meticulously
edit the current color. You have both mouse control of the color (via the
buttons along the bottom row), and there are keyboard bindings for each of the
increment/decrement buttons.
The top three check buttons allow you to specify which of the three color
variations are tied together when incrementing and decrementing. Red, green,
and blue are self evident. By tying together red and green, you can modify
the yellow level of the color. By tying together red and blue, you can modify
the magenta level of the color. By tying together green and blue, you can
modify the cyan level, and by tying all three together, you can modify the
grey level.
The behavior at the boundaries (0 and 255) are defined by the `At boundary'
option menu:
Stop
When the increment or decrement would send any of the tied variations
out of bounds, the entire delta is discarded.
Wrap Around
When the increment or decrement would send any of the tied variations
out of bounds, the out of bounds variation is wrapped around to the
other side. Thus if red were at 238 and 25 were added to it, red
would have the value 7.
Preserve Distance
When the increment or decrement would send any of the tied variations
out of bounds, all tied variations are wrapped as one, so as to
preserve the distance between them. Thus if green and blue were tied,
and green was at 238 while blue was at 223, and an increment of 25
were applied, green would be at 15 and blue would be at 0.
Squash
When the increment or decrement would send any of the tied variations
out of bounds, the out of bounds variation is set to the ceiling of
255 or floor of 0, as appropriate. In this way, all tied variations
are squashed to one edge or the other.
The following key bindings can be used as accelerators. Note that Pynche can
fall behind if you hold the key down as a key repeat:
Left arrow == -1
Right arrow == +1
Control + Left == -10
Control + Right == 10
Shift + Left == -25
Shift + Right == +25
"""
from tkinter import *
STOP = 'Stop'
WRAP = 'Wrap Around'
RATIO = 'Preserve Distance'
GRAV = 'Squash'
ADDTOVIEW = 'Details Window...'
class DetailsViewer:
def __init__(self, switchboard, master=None):
self.__sb = switchboard
optiondb = switchboard.optiondb()
self.__red, self.__green, self.__blue = switchboard.current_rgb()
# GUI
root = self.__root = Toplevel(master, class_='Pynche')
root.protocol('WM_DELETE_WINDOW', self.withdraw)
root.title('Pynche Details Window')
root.iconname('Pynche Details Window')
root.bind('<Alt-q>', self.__quit)
root.bind('<Alt-Q>', self.__quit)
root.bind('<Alt-w>', self.withdraw)
root.bind('<Alt-W>', self.withdraw)
# accelerators
root.bind('<KeyPress-Left>', self.__minus1)
root.bind('<KeyPress-Right>', self.__plus1)
root.bind('<Control-KeyPress-Left>', self.__minus10)
root.bind('<Control-KeyPress-Right>', self.__plus10)
root.bind('<Shift-KeyPress-Left>', self.__minus25)
root.bind('<Shift-KeyPress-Right>', self.__plus25)
#
# color ties
frame = self.__frame = Frame(root)
frame.pack(expand=YES, fill=X)
self.__l1 = Label(frame, text='Move Sliders:')
self.__l1.grid(row=1, column=0, sticky=E)
self.__rvar = IntVar()
self.__rvar.set(optiondb.get('RSLIDER', 4))
self.__radio1 = Checkbutton(frame, text='Red',
variable=self.__rvar,
command=self.__effect,
onvalue=4, offvalue=0)
self.__radio1.grid(row=1, column=1, sticky=W)
self.__gvar = IntVar()
self.__gvar.set(optiondb.get('GSLIDER', 2))
self.__radio2 = Checkbutton(frame, text='Green',
variable=self.__gvar,
command=self.__effect,
onvalue=2, offvalue=0)
self.__radio2.grid(row=2, column=1, sticky=W)
self.__bvar = IntVar()
self.__bvar.set(optiondb.get('BSLIDER', 1))
self.__radio3 = Checkbutton(frame, text='Blue',
variable=self.__bvar,
command=self.__effect,
onvalue=1, offvalue=0)
self.__radio3.grid(row=3, column=1, sticky=W)
self.__l2 = Label(frame)
self.__l2.grid(row=4, column=1, sticky=W)
self.__effect()
#
# Boundary behavior
self.__l3 = Label(frame, text='At boundary:')
self.__l3.grid(row=5, column=0, sticky=E)
self.__boundvar = StringVar()
self.__boundvar.set(optiondb.get('ATBOUND', STOP))
self.__omenu = OptionMenu(frame, self.__boundvar,
STOP, WRAP, RATIO, GRAV)
self.__omenu.grid(row=5, column=1, sticky=W)
self.__omenu.configure(width=17)
#
# Buttons
frame = self.__btnframe = Frame(frame)
frame.grid(row=0, column=0, columnspan=2, sticky='EW')
self.__down25 = Button(frame, text='-25',
command=self.__minus25)
self.__down10 = Button(frame, text='-10',
command=self.__minus10)
self.__down1 = Button(frame, text='-1',
command=self.__minus1)
self.__up1 = Button(frame, text='+1',
command=self.__plus1)
self.__up10 = Button(frame, text='+10',
command=self.__plus10)
self.__up25 = Button(frame, text='+25',
command=self.__plus25)
self.__down25.pack(expand=YES, fill=X, side=LEFT)
self.__down10.pack(expand=YES, fill=X, side=LEFT)
self.__down1.pack(expand=YES, fill=X, side=LEFT)
self.__up1.pack(expand=YES, fill=X, side=LEFT)
self.__up10.pack(expand=YES, fill=X, side=LEFT)
self.__up25.pack(expand=YES, fill=X, side=LEFT)
def __effect(self, event=None):
tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
if tie in (0, 1, 2, 4):
text = ''
else:
text = '(= %s Level)' % {3: 'Cyan',
5: 'Magenta',
6: 'Yellow',
7: 'Grey'}[tie]
self.__l2.configure(text=text)
def __quit(self, event=None):
self.__root.quit()
def withdraw(self, event=None):
self.__root.withdraw()
def deiconify(self, event=None):
self.__root.deiconify()
def __minus25(self, event=None):
self.__delta(-25)
def __minus10(self, event=None):
self.__delta(-10)
def __minus1(self, event=None):
self.__delta(-1)
def __plus1(self, event=None):
self.__delta(1)
def __plus10(self, event=None):
self.__delta(10)
def __plus25(self, event=None):
self.__delta(25)
def __delta(self, delta):
tie = []
if self.__rvar.get():
red = self.__red + delta
tie.append(red)
else:
red = self.__red
if self.__gvar.get():
green = self.__green + delta
tie.append(green)
else:
green = self.__green
if self.__bvar.get():
blue = self.__blue + delta
tie.append(blue)
else:
blue = self.__blue
# now apply at boundary behavior
atbound = self.__boundvar.get()
if atbound == STOP:
if red < 0 or green < 0 or blue < 0 or \
red > 255 or green > 255 or blue > 255:
# then
red, green, blue = self.__red, self.__green, self.__blue
elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
if red < 0:
red += 256
if green < 0:
green += 256
if blue < 0:
blue += 256
if red > 255:
red -= 256
if green > 255:
green -= 256
if blue > 255:
blue -= 256
elif atbound == RATIO:
# for when 2 or 3 colors are tied together
dir = 0
for c in tie:
if c < 0:
dir = -1
elif c > 255:
dir = 1
if dir == -1:
delta = max(tie)
if self.__rvar.get():
red = red + 255 - delta
if self.__gvar.get():
green = green + 255 - delta
if self.__bvar.get():
blue = blue + 255 - delta
elif dir == 1:
delta = min(tie)
if self.__rvar.get():
red = red - delta
if self.__gvar.get():
green = green - delta
if self.__bvar.get():
blue = blue - delta
elif atbound == GRAV:
if red < 0:
red = 0
if green < 0:
green = 0
if blue < 0:
blue = 0
if red > 255:
red = 255
if green > 255:
green = 255
if blue > 255:
blue = 255
self.__sb.update_views(red, green, blue)
self.__root.update_idletasks()
def update_yourself(self, red, green, blue):
self.__red = red
self.__green = green
self.__blue = blue
def save_options(self, optiondb):
optiondb['RSLIDER'] = self.__rvar.get()
optiondb['GSLIDER'] = self.__gvar.get()
optiondb['BSLIDER'] = self.__bvar.get()
optiondb['ATBOUND'] = self.__boundvar.get()

View file

@ -0,0 +1,175 @@
"""ListViewer class.
This class implements an input/output view on the color model. It lists every
unique color (e.g. unique r/g/b value) found in the color database. Each
color is shown by small swatch and primary color name. Some colors have
aliases -- more than one name for the same r/g/b value. These aliases are
displayed in the small listbox at the bottom of the screen.
Clicking on a color name or swatch selects that color and updates all other
windows. When a color is selected in a different viewer, the color list is
scrolled to the selected color and it is highlighted. If the selected color
is an r/g/b value without a name, no scrolling occurs.
You can turn off Update On Click if all you want to see is the alias for a
given name, without selecting the color.
"""
from tkinter import *
import ColorDB
ADDTOVIEW = 'Color %List Window...'
class ListViewer:
def __init__(self, switchboard, master=None):
self.__sb = switchboard
optiondb = switchboard.optiondb()
self.__lastbox = None
self.__dontcenter = 0
# GUI
root = self.__root = Toplevel(master, class_='Pynche')
root.protocol('WM_DELETE_WINDOW', self.withdraw)
root.title('Pynche Color List')
root.iconname('Pynche Color List')
root.bind('<Alt-q>', self.__quit)
root.bind('<Alt-Q>', self.__quit)
root.bind('<Alt-w>', self.withdraw)
root.bind('<Alt-W>', self.withdraw)
#
# create the canvas which holds everything, and its scrollbar
#
frame = self.__frame = Frame(root)
frame.pack()
canvas = self.__canvas = Canvas(frame, width=160, height=300,
borderwidth=2, relief=SUNKEN)
self.__scrollbar = Scrollbar(frame)
self.__scrollbar.pack(fill=Y, side=RIGHT)
canvas.pack(fill=BOTH, expand=1)
canvas.configure(yscrollcommand=(self.__scrollbar, 'set'))
self.__scrollbar.configure(command=(canvas, 'yview'))
self.__populate()
#
# Update on click
self.__uoc = BooleanVar()
self.__uoc.set(optiondb.get('UPONCLICK', 1))
self.__uocbtn = Checkbutton(root,
text='Update on Click',
variable=self.__uoc,
command=self.__toggleupdate)
self.__uocbtn.pack(expand=1, fill=BOTH)
#
# alias list
self.__alabel = Label(root, text='Aliases:')
self.__alabel.pack()
self.__aliases = Listbox(root, height=5,
selectmode=BROWSE)
self.__aliases.pack(expand=1, fill=BOTH)
def __populate(self):
#
# create all the buttons
colordb = self.__sb.colordb()
canvas = self.__canvas
row = 0
widest = 0
bboxes = self.__bboxes = []
for name in colordb.unique_names():
exactcolor = ColorDB.triplet_to_rrggbb(colordb.find_byname(name))
canvas.create_rectangle(5, row*20 + 5,
20, row*20 + 20,
fill=exactcolor)
textid = canvas.create_text(25, row*20 + 13,
text=name,
anchor=W)
x1, y1, textend, y2 = canvas.bbox(textid)
boxid = canvas.create_rectangle(3, row*20+3,
textend+3, row*20 + 23,
outline='',
tags=(exactcolor, 'all'))
canvas.bind('<ButtonRelease>', self.__onrelease)
bboxes.append(boxid)
if textend+3 > widest:
widest = textend+3
row += 1
canvheight = (row-1)*20 + 25
canvas.config(scrollregion=(0, 0, 150, canvheight))
for box in bboxes:
x1, y1, x2, y2 = canvas.coords(box)
canvas.coords(box, x1, y1, widest, y2)
def __onrelease(self, event=None):
canvas = self.__canvas
# find the current box
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
ids = canvas.find_overlapping(x, y, x, y)
for boxid in ids:
if boxid in self.__bboxes:
break
else:
## print 'No box found!'
return
tags = self.__canvas.gettags(boxid)
for t in tags:
if t[0] == '#':
break
else:
## print 'No color tag found!'
return
red, green, blue = ColorDB.rrggbb_to_triplet(t)
self.__dontcenter = 1
if self.__uoc.get():
self.__sb.update_views(red, green, blue)
else:
self.update_yourself(red, green, blue)
self.__red, self.__green, self.__blue = red, green, blue
def __toggleupdate(self, event=None):
if self.__uoc.get():
self.__sb.update_views(self.__red, self.__green, self.__blue)
def __quit(self, event=None):
self.__root.quit()
def withdraw(self, event=None):
self.__root.withdraw()
def deiconify(self, event=None):
self.__root.deiconify()
def update_yourself(self, red, green, blue):
canvas = self.__canvas
# turn off the last box
if self.__lastbox:
canvas.itemconfigure(self.__lastbox, outline='')
# turn on the current box
colortag = ColorDB.triplet_to_rrggbb((red, green, blue))
canvas.itemconfigure(colortag, outline='black')
self.__lastbox = colortag
# fill the aliases
self.__aliases.delete(0, END)
try:
aliases = self.__sb.colordb().aliases_of(red, green, blue)[1:]
except ColorDB.BadColor:
self.__aliases.insert(END, '<no matching color>')
return
if not aliases:
self.__aliases.insert(END, '<no aliases>')
else:
for name in aliases:
self.__aliases.insert(END, name)
# maybe scroll the canvas so that the item is visible
if self.__dontcenter:
self.__dontcenter = 0
else:
ig, ig, ig, y1 = canvas.coords(colortag)
ig, ig, ig, y2 = canvas.coords(self.__bboxes[-1])
h = int(canvas['height']) * 0.5
canvas.yview('moveto', (y1-h) / y2)
def save_options(self, optiondb):
optiondb['UPONCLICK'] = self.__uoc.get()
def colordb_changed(self, colordb):
self.__canvas.delete('all')
self.__populate()

229
third_party/python/Tools/pynche/Main.py vendored Normal file
View file

@ -0,0 +1,229 @@
"""Pynche -- The PYthon Natural Color and Hue Editor.
Contact: %(AUTHNAME)s
Email: %(AUTHEMAIL)s
Version: %(__version__)s
Pynche is based largely on a similar color editor I wrote years ago for the
SunView window system. That editor was called ICE: the Interactive Color
Editor. I'd always wanted to port the editor to X but didn't feel like
hacking X and C code to do it. Fast forward many years, to where Python +
Tkinter provides such a nice programming environment, with enough power, that
I finally buckled down and implemented it. I changed the name because these
days, too many other systems have the acronym `ICE'.
This program currently requires Python 2.2 with Tkinter.
Usage: %(PROGRAM)s [-d file] [-i file] [-X] [-v] [-h] [initialcolor]
Where:
--database file
-d file
Alternate location of a color database file
--initfile file
-i file
Alternate location of the initialization file. This file contains a
persistent database of the current Pynche options and color. This
means that Pynche restores its option settings and current color when
it restarts, using this file (unless the -X option is used). The
default is ~/.pynche
--ignore
-X
Ignore the initialization file when starting up. Pynche will still
write the current option settings to this file when it quits.
--version
-v
print the version number and exit
--help
-h
print this message
initialcolor
initial color, as a color name or #RRGGBB format
"""
__version__ = '1.4.1'
import sys
import os
import getopt
import ColorDB
from PyncheWidget import PyncheWidget
from Switchboard import Switchboard
from StripViewer import StripViewer
from ChipViewer import ChipViewer
from TypeinViewer import TypeinViewer
PROGRAM = sys.argv[0]
AUTHNAME = 'Barry Warsaw'
AUTHEMAIL = 'barry@python.org'
# Default locations of rgb.txt or other textual color database
RGB_TXT = [
# Solaris OpenWindows
'/usr/openwin/lib/rgb.txt',
# Linux
'/usr/lib/X11/rgb.txt',
# The X11R6.4 rgb.txt file
os.path.join(sys.path[0], 'X/rgb.txt'),
# add more here
]
# Do this because PyncheWidget.py wants to get at the interpolated docstring
# too, for its Help menu.
def docstring():
return __doc__ % globals()
def usage(code, msg=''):
print(docstring())
if msg:
print(msg)
sys.exit(code)
def initial_color(s, colordb):
# function called on every color
def scan_color(s, colordb=colordb):
try:
r, g, b = colordb.find_byname(s)
except ColorDB.BadColor:
try:
r, g, b = ColorDB.rrggbb_to_triplet(s)
except ColorDB.BadColor:
return None, None, None
return r, g, b
#
# First try the passed in color
r, g, b = scan_color(s)
if r is None:
# try the same color with '#' prepended, since some shells require
# this to be escaped, which is a pain
r, g, b = scan_color('#' + s)
if r is None:
print('Bad initial color, using gray50:', s)
r, g, b = scan_color('gray50')
if r is None:
usage(1, 'Cannot find an initial color to use')
# does not return
return r, g, b
def build(master=None, initialcolor=None, initfile=None, ignore=None,
dbfile=None):
# create all output widgets
s = Switchboard(not ignore and initfile)
# defer to the command line chosen color database, falling back to the one
# in the .pynche file.
if dbfile is None:
dbfile = s.optiondb().get('DBFILE')
# find a parseable color database
colordb = None
files = RGB_TXT[:]
if dbfile is None:
dbfile = files.pop()
while colordb is None:
try:
colordb = ColorDB.get_colordb(dbfile)
except (KeyError, IOError):
pass
if colordb is None:
if not files:
break
dbfile = files.pop(0)
if not colordb:
usage(1, 'No color database file found, see the -d option.')
s.set_colordb(colordb)
# create the application window decorations
app = PyncheWidget(__version__, s, master=master)
w = app.window()
# these built-in viewers live inside the main Pynche window
s.add_view(StripViewer(s, w))
s.add_view(ChipViewer(s, w))
s.add_view(TypeinViewer(s, w))
# get the initial color as components and set the color on all views. if
# there was no initial color given on the command line, use the one that's
# stored in the option database
if initialcolor is None:
optiondb = s.optiondb()
red = optiondb.get('RED')
green = optiondb.get('GREEN')
blue = optiondb.get('BLUE')
# but if there wasn't any stored in the database, use grey50
if red is None or blue is None or green is None:
red, green, blue = initial_color('grey50', colordb)
else:
red, green, blue = initial_color(initialcolor, colordb)
s.update_views(red, green, blue)
return app, s
def run(app, s):
try:
app.start()
except KeyboardInterrupt:
pass
def main():
try:
opts, args = getopt.getopt(
sys.argv[1:],
'hd:i:Xv',
['database=', 'initfile=', 'ignore', 'help', 'version'])
except getopt.error as msg:
usage(1, msg)
if len(args) == 0:
initialcolor = None
elif len(args) == 1:
initialcolor = args[0]
else:
usage(1)
ignore = False
dbfile = None
initfile = os.path.expanduser('~/.pynche')
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-v', '--version'):
print("""\
Pynche -- The PYthon Natural Color and Hue Editor.
Contact: %(AUTHNAME)s
Email: %(AUTHEMAIL)s
Version: %(__version__)s""" % globals())
sys.exit(0)
elif opt in ('-d', '--database'):
dbfile = arg
elif opt in ('-X', '--ignore'):
ignore = True
elif opt in ('-i', '--initfile'):
initfile = arg
app, sb = build(initialcolor=initialcolor,
initfile=initfile,
ignore=ignore,
dbfile=dbfile)
run(app, sb)
sb.save_views()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,313 @@
"""Main Pynche (Pythonically Natural Color and Hue Editor) widget.
This window provides the basic decorations, primarily including the menubar.
It is used to bring up other windows.
"""
import sys
import os
from tkinter import *
from tkinter import messagebox, filedialog
import ColorDB
# Milliseconds between interrupt checks
KEEPALIVE_TIMER = 500
class PyncheWidget:
def __init__(self, version, switchboard, master=None, extrapath=[]):
self.__sb = switchboard
self.__version = version
self.__textwin = None
self.__listwin = None
self.__detailswin = None
self.__helpwin = None
self.__dialogstate = {}
modal = self.__modal = not not master
# If a master was given, we are running as a modal dialog servant to
# some other application. We rearrange our UI in this case (there's
# no File menu and we get `Okay' and `Cancel' buttons), and we do a
# grab_set() to make ourselves modal
if modal:
self.__tkroot = tkroot = Toplevel(master, class_='Pynche')
tkroot.grab_set()
tkroot.withdraw()
else:
# Is there already a default root for Tk, say because we're
# running under Guido's IDE? :-) Two conditions say no, either the
# import fails or _default_root is None.
tkroot = None
try:
from Tkinter import _default_root
tkroot = self.__tkroot = _default_root
except ImportError:
pass
if not tkroot:
tkroot = self.__tkroot = Tk(className='Pynche')
# but this isn't our top level widget, so make it invisible
tkroot.withdraw()
# create the menubar
menubar = self.__menubar = Menu(tkroot)
#
# File menu
#
filemenu = self.__filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label='Load palette...',
command=self.__load,
underline=0)
if not modal:
filemenu.add_command(label='Quit',
command=self.__quit,
accelerator='Alt-Q',
underline=0)
#
# View menu
#
views = make_view_popups(self.__sb, self.__tkroot, extrapath)
viewmenu = Menu(menubar, tearoff=0)
for v in views:
viewmenu.add_command(label=v.menutext(),
command=v.popup,
underline=v.underline())
#
# Help menu
#
helpmenu = Menu(menubar, name='help', tearoff=0)
helpmenu.add_command(label='About Pynche...',
command=self.__popup_about,
underline=0)
helpmenu.add_command(label='Help...',
command=self.__popup_usage,
underline=0)
#
# Tie them all together
#
menubar.add_cascade(label='File',
menu=filemenu,
underline=0)
menubar.add_cascade(label='View',
menu=viewmenu,
underline=0)
menubar.add_cascade(label='Help',
menu=helpmenu,
underline=0)
# now create the top level window
root = self.__root = Toplevel(tkroot, class_='Pynche', menu=menubar)
root.protocol('WM_DELETE_WINDOW',
modal and self.__bell or self.__quit)
root.title('Pynche %s' % version)
root.iconname('Pynche')
# Only bind accelerators for the File->Quit menu item if running as a
# standalone app
if not modal:
root.bind('<Alt-q>', self.__quit)
root.bind('<Alt-Q>', self.__quit)
else:
# We're a modal dialog so we have a new row of buttons
bframe = Frame(root, borderwidth=1, relief=RAISED)
bframe.grid(row=4, column=0, columnspan=2,
sticky='EW',
ipady=5)
okay = Button(bframe,
text='Okay',
command=self.__okay)
okay.pack(side=LEFT, expand=1)
cancel = Button(bframe,
text='Cancel',
command=self.__cancel)
cancel.pack(side=LEFT, expand=1)
def __quit(self, event=None):
self.__tkroot.quit()
def __bell(self, event=None):
self.__tkroot.bell()
def __okay(self, event=None):
self.__sb.withdraw_views()
self.__tkroot.grab_release()
self.__quit()
def __cancel(self, event=None):
self.__sb.canceled()
self.__okay()
def __keepalive(self):
# Exercise the Python interpreter regularly so keyboard interrupts get
# through.
self.__tkroot.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive)
def start(self):
if not self.__modal:
self.__keepalive()
self.__tkroot.mainloop()
def window(self):
return self.__root
def __popup_about(self, event=None):
from Main import __version__
messagebox.showinfo('About Pynche ' + __version__,
'''\
Pynche %s
The PYthonically Natural
Color and Hue Editor
For information
contact: Barry A. Warsaw
email: bwarsaw@python.org''' % __version__)
def __popup_usage(self, event=None):
if not self.__helpwin:
self.__helpwin = Helpwin(self.__root, self.__quit)
self.__helpwin.deiconify()
def __load(self, event=None):
while 1:
idir, ifile = os.path.split(self.__sb.colordb().filename())
file = filedialog.askopenfilename(
filetypes=[('Text files', '*.txt'),
('All files', '*'),
],
initialdir=idir,
initialfile=ifile)
if not file:
# cancel button
return
try:
colordb = ColorDB.get_colordb(file)
except IOError:
messagebox.showerror('Read error', '''\
Could not open file for reading:
%s''' % file)
continue
if colordb is None:
messagebox.showerror('Unrecognized color file type', '''\
Unrecognized color file type in file:
%s''' % file)
continue
break
self.__sb.set_colordb(colordb)
def withdraw(self):
self.__root.withdraw()
def deiconify(self):
self.__root.deiconify()
class Helpwin:
def __init__(self, master, quitfunc):
from Main import docstring
self.__root = root = Toplevel(master, class_='Pynche')
root.protocol('WM_DELETE_WINDOW', self.__withdraw)
root.title('Pynche Help Window')
root.iconname('Pynche Help Window')
root.bind('<Alt-q>', quitfunc)
root.bind('<Alt-Q>', quitfunc)
root.bind('<Alt-w>', self.__withdraw)
root.bind('<Alt-W>', self.__withdraw)
# more elaborate help is available in the README file
readmefile = os.path.join(sys.path[0], 'README')
try:
fp = None
try:
fp = open(readmefile)
contents = fp.read()
# wax the last page, it contains Emacs cruft
i = contents.rfind('\f')
if i > 0:
contents = contents[:i].rstrip()
finally:
if fp:
fp.close()
except IOError:
sys.stderr.write("Couldn't open Pynche's README, "
'using docstring instead.\n')
contents = docstring()
self.__text = text = Text(root, relief=SUNKEN,
width=80, height=24)
self.__text.focus_set()
text.insert(0.0, contents)
scrollbar = Scrollbar(root)
scrollbar.pack(fill=Y, side=RIGHT)
text.pack(fill=BOTH, expand=YES)
text.configure(yscrollcommand=(scrollbar, 'set'))
scrollbar.configure(command=(text, 'yview'))
def __withdraw(self, event=None):
self.__root.withdraw()
def deiconify(self):
self.__root.deiconify()
import functools
@functools.total_ordering
class PopupViewer:
def __init__(self, module, name, switchboard, root):
self.__m = module
self.__name = name
self.__sb = switchboard
self.__root = root
self.__menutext = module.ADDTOVIEW
# find the underline character
underline = module.ADDTOVIEW.find('%')
if underline == -1:
underline = 0
else:
self.__menutext = module.ADDTOVIEW.replace('%', '', 1)
self.__underline = underline
self.__window = None
def menutext(self):
return self.__menutext
def underline(self):
return self.__underline
def popup(self, event=None):
if not self.__window:
# class and module must have the same name
class_ = getattr(self.__m, self.__name)
self.__window = class_(self.__sb, self.__root)
self.__sb.add_view(self.__window)
self.__window.deiconify()
def __eq__(self, other):
return self.__menutext == other.__menutext
def __lt__(self, other):
return self.__menutext < other.__menutext
def make_view_popups(switchboard, root, extrapath):
viewers = []
# where we are in the file system
dirs = [os.path.dirname(__file__)] + extrapath
for dir in dirs:
if dir == '':
dir = '.'
for file in os.listdir(dir):
if file[-9:] == 'Viewer.py':
name = file[:-3]
try:
module = __import__(name)
except ImportError:
# Pynche is running from inside a package, so get the
# module using the explicit path.
pkg = __import__('pynche.'+name)
module = getattr(pkg, name)
if hasattr(module, 'ADDTOVIEW') and module.ADDTOVIEW:
# this is an external viewer
v = PopupViewer(module, name, switchboard, root)
viewers.append(v)
# sort alphabetically
viewers.sort()
return viewers

398
third_party/python/Tools/pynche/README vendored Normal file
View file

@ -0,0 +1,398 @@
Pynche - The PYthonically Natural Color and Hue Editor
Contact: Barry A. Warsaw
Email: bwarsaw@python.org
Version: 1.3
Introduction
Pynche is a color editor based largely on a similar program that I
originally wrote back in 1987 for the Sunview window system. That
editor was called ICE, the Interactive Color Editor. I'd always
wanted to port this program to X but didn't feel like hacking X
and C code to do it. Fast forward many years, to where Python +
Tkinter provides such a nice programming environment, with enough
power, that I finally buckled down and re-implemented it. I
changed the name because these days, too many other systems have
the acronym `ICE'.
Pynche should work with any variant of Python after 1.5.2
(e.g. 2.0.1 and 2.1.1), using Tk 8.0.x. It's been tested on
Solaris 2.6, Windows NT 4, and various Linux distros. You'll want
to be sure to have at least Tk 8.0.3 for Windows. Also, Pynche is
very colormap intensive, so it doesn't work very well on 8-bit
graphics cards; 24bit+ graphics cards are so cheap these days,
I'll probably never "fix" that.
Pynche must find a text database of colors names in order to
provide `nearest' color matching. Pynche is distributed with an
rgb.txt file from the X11R6.4 distribution for this reason, along
with other "Web related" database (see below). You can use a
different file with the -d option. The file xlicense.txt contains
the license only for rgb.txt and both files are in the X/
subdirectory.
Pynche is pronounced: Pin'-chee
Running Standalone
On Unix, start it by running the `pynche' script. On Windows, run
pynche.pyw to inhibit the console window. When run from the
command line, the following options are recognized:
--database file
-d file
Alternate location of the color database file. Without this
option, the first valid file found will be used (see below).
--initfile file
-i file
Alternate location of the persistent initialization file. See
the section on Persistency below.
--ignore
-X
Ignore the persistent initialization file when starting up.
Pynche will still write the current option settings to the
persistent init file when it quits.
--help
-h
Print the help message.
initialcolor
a Tk color name or #rrggbb color spec to be used as the
initially selected color. This overrides any color saved in
the persistent init file. Since `#' needs to be escaped in
many shells, it is optional in the spec (e.g. #45dd1f is the
same as 45dd1f).
Running as a Modal Dialog
Pynche can be run as a modal dialog, inside another application,
say as a general color chooser. In fact, Grail 0.6 uses Pynche
and a future version of IDLE may as well. Pynche supports the API
implemented by the Tkinter standard tkColorChooser module, with a
few changes as described below. By importing pyColorChooser from
the Pynche package, you can run
pyColorChooser.askcolor()
which will popup Pynche as a modal dialog, and return the selected
color.
There are some UI differences when running as a modal
vs. standalone. When running as a modal, there is no "Quit" menu
item under the "File" menu. Instead there are "Okay" and "Cancel"
buttons.
When "Okay" is hit, askcolor() returns the tuple
((r, g, b), "name")
where r, g, and b are red, green, and blue color values
respectively (in the range 0 to 255). "name" will be a color name
from the color database if there is an exact match, otherwise it
will be an X11 color spec of the form "#rrggbb". Note that this
is different than tkColorChooser, which doesn't know anything
about color names.
askcolor() supports the following optional keyword arguments:
color
the color to set as the initial selected color
master[*]
the master window to use as the parent of the modal
dialog. Without this argument, pyColorChooser will create
its own Tkinter.Tk instance as the master. This may not
be what you want.
databasefile
similar to the --database option, the value must be a
file name
initfile[*]
similar to the --initfile option, the value must be a
file name
ignore[*]
similar to the --ignore flag, the value is a boolean
wantspec
When this is true, the "name" field in the return tuple
will always be a color spec of the form "#rrggbb". It
will not return a color name even if there is a match;
this is so pyColorChooser can exactly match the API of
tkColorChooser.
[*] these arguments must be specified the first time
askcolor() is used and cannot be changed on subsequent calls.
The Colorstrip Window
The top part of the main Pynche window contains the "variation
strips". Each strip contains a number of "color chips". The
strips always indicate the currently selected color by a highlight
rectangle around the selected color chip, with an arrow pointing
to the chip. Each arrow has an associated number giving you the
color value along the variation's axis. Each variation strip
shows you the colors that are reachable from the selected color by
varying just one axis of the color solid.
For example, when the selected color is (in Red/Green/Blue
notation) 127/127/127, the Red Variations strip shows you every
color in the range 0/127/127 to 255/127/127. Similarly for the
green and blue axes. You can select any color by clicking on its
chip. This will update the highlight rectangle and the arrow, as
well as other displays in Pynche.
Click on "Update while dragging" if you want Pynche to update the
selected color while you drag along any variation strip (this will
be a bit slower). Click on "Hexadecimal" to display the arrow
numbers in hex.
There are also two shortcut buttons in this window, which
auto-select Black (0/0/0) and White (255/255/255).
The Proof Window
In the lower left corner of the main window you see two larger
color chips. The Selected chip shows you a larger version of the
color selected in the variation strips, along with its X11 color
specification. The Nearest chip shows you the closest color in
the X11 database to the selected color, giving its X11 color
specification, and below that, its X11 color name. When the
Selected chip color exactly matches the Nearest chip color, you
will see the color name appear below the color specification for
the Selected chip.
Clicking on the Nearest color chip selects that color. Color
distance is calculated in the 3D space of the RGB color solid and
if more than one color name is the same distance from the selected
color, the first one found will be chosen.
Note that there may be more than one X11 color name for the same
RGB value. In that case, the first one found in the text database
is designated the "primary" name, and this is shown under the
Nearest chip. The other names are "aliases" and they are visible
in the Color List Window (see below).
Both the color specifications and color names are selectable for
copying and pasting into another window.
The Type-in Window
At the lower right of the main window are three entry fields.
Here you can type numeric values for any of the three color axes.
Legal values are between 0 and 255, and these fields do not allow
you to enter illegal values. You must hit Enter or Tab to select
the new color.
Click on "Update while typing" if you want Pynche to select the
color on every keystroke (well, every one that produces a legal
value!) Click on "Hexadecimal" to display and enter color values
in hex.
Other Views
There are three secondary windows which are not displayed by
default. You can bring these up via the "View" menu on the main
Pynche window.
The Text Window
The "Text Window" allows you to see what effects various colors
have on the standard Tk text widget elements. In the upper part
of the window is a plain Tk text widget and here you can edit the
text, select a region of text, etc. Below this is a button "Track
color changes". When this is turned on, any colors selected in
the other windows will change the text widget element specified in
the radio buttons below. When this is turned off, text widget
elements are not affected by color selection.
You can choose which element gets changed by color selection by
clicking on one of the radio buttons in the bottom part of this
window. Text foreground and background affect the text in the
upper part of the window. Selection foreground and background
affect the colors of the primary selection which is what you see
when you click the middle button (depending on window system) and
drag it through some text.
The Insertion is the insertion cursor in the text window, where
new text will be inserted as you type. The insertion cursor only
has a background.
The Color List Window
The "Color List" window shows every named color in the color name
database (this window may take a while to come up). In the upper
part of the window you see a scrolling list of all the color names
in the database, in alphabetical order. Click on any color to
select it. In the bottom part of the window is displayed any
aliases for the selected color (those color names that have the
same RGB value, but were found later in the text database). For
example, find the color "Black" and you'll see that its aliases
are "gray0" and "grey0".
If the color has no aliases you'll see "<no aliases>" here. If you
just want to see if a color has an alias, and do not want to select a
color when you click on it, turn off "Update on Click".
Note that the color list is always updated when a color is selected
from the main window. There's no way to turn this feature off. If
the selected color has no matching color name you'll see
"<no matching color>" in the Aliases window.
The Details Window
The "Details" window gives you more control over color selection
than just clicking on a color chip in the main window. The row of
buttons along the top apply the specified increment and decrement
amounts to the selected color. These delta amounts are applied to
the variation strips specified by the check boxes labeled "Move
Sliders". Thus if just Red and Green are selected, hitting -10
will subtract 10 from the color value along the red and green
variation only. Note the message under the checkboxes; this
indicates the primary color level being changed when more than one
slider is tied together. For example, if Red and Green are
selected, you will be changing the Yellow level of the selected
color.
The "At Boundary" behavior determines what happens when any color
variation hits either the lower or upper boundaries (0 or 255) as
a result of clicking on the top row buttons:
Stop
When the increment or decrement would send any of the tied
variations out of bounds, the entire delta is discarded.
Wrap Around
When the increment or decrement would send any of the tied
variations out of bounds, the out of bounds value is wrapped
around to the other side. Thus if red were at 238 and +25
were clicked, red would have the value 7.
Preserve Distance
When the increment or decrement would send any of the tied
variations out of bounds, all tied variations are wrapped as
one, so as to preserve the distance between them. Thus if
green and blue were tied, and green was at 238 while blue was
at 223, and +25 were clicked, green would be at 15 and blue
would be at 0.
Squash
When the increment or decrement would send any of the tied
variations out of bounds, the out of bounds variation is set
to the ceiling of 255 or floor of 0, as appropriate. In this
way, all tied variations are squashed to one edge or the
other.
The top row buttons have the following keyboard accelerators:
-25 == Shift Left Arrow
-10 == Control Left Arrow
-1 == Left Arrow
+1 == Right Arrow
+10 == Control Right Arrow
+25 == Shift Right Arrow
Keyboard Accelerators
Alt-w in any secondary window dismisses the window. In the main
window it exits Pynche (except when running as a modal).
Alt-q in any window exits Pynche (except when running as a modal).
Persistency
Pynche remembers various settings of options and colors between
invocations, storing these values in a `persistent initialization
file'. The actual location of this file is specified by the
--initfile option (see above), and defaults to ~/.pynche.
When Pynche exits, it saves these values in the init file, and
re-reads them when it starts up. There is no locking on this
file, so if you run multiple instances of Pynche at a time, you
may clobber the init file.
The actual options stored include
- the currently selected color
- all settings of checkbox and radio button options in all windows
- the contents of the text window, the current text selection and
insertion point, and all current text widget element color
settings.
- the name of the color database file (but not its contents)
You can inhibit Pynche from reading the init file by supplying the
--ignore option on the command line. However, you cannot suppress
the storing of the settings in the init file on Pynche exit. If
you really want to do this, use /dev/null as the init file, using
--initfile.
Color Name Database Files
Pynche uses a color name database file to calculate the nearest
color to the selected color, and to display in the Color List
view. Several files are distributed with Pynche, described
below. By default, the X11 color name database file is selected.
Other files:
html40colors.txt -- the HTML 4.0 guaranteed color names
websafe.txt -- the 216 "Web-safe" colors that Netscape and MSIE
guarantee will not be dithered. These are specified in #rrggbb
format for both values and names
webcolors.txt -- The 140 color names that Tim Peters and his
sister say NS and MSIE both understand (with some controversy over
AliceBlue).
namedcolors.txt -- an alternative set of Netscape colors.
You can switch between files by choosing "Load palette..." from
the "File" menu. This brings up a standard Tk file dialog.
Choose the file you want and then click "Ok". If Pynche
understands the format in this file, it will load the database and
update the appropriate windows. If not, it will bring up an error
dialog.
To Do
Here's a brief list of things I want to do (some mythical day):
- Better support for resizing the top level windows
- More output views, e.g. color solids
- Have the notion of a `last color selected'; this may require a
new output view
- Support setting the font in the text view
- Support distutils setup.py for installation
I'm open to suggestions!
Local Variables:
indent-tabs-mode: nil
End:

View file

@ -0,0 +1,433 @@
"""Strip viewer and related widgets.
The classes in this file implement the StripViewer shown in the top two thirds
of the main Pynche window. It consists of three StripWidgets which display
the variations in red, green, and blue respectively of the currently selected
r/g/b color value.
Each StripWidget shows the color variations that are reachable by varying an
axis of the currently selected color. So for example, if the color is
(R,G,B)=(127,163,196)
then the Red variations show colors from (0,163,196) to (255,163,196), the
Green variations show colors from (127,0,196) to (127,255,196), and the Blue
variations show colors from (127,163,0) to (127,163,255).
The selected color is always visible in all three StripWidgets, and in fact
each StripWidget highlights the selected color, and has an arrow pointing to
the selected chip, which includes the value along that particular axis.
Clicking on any chip in any StripWidget selects that color, and updates all
arrows and other windows. By toggling on Update while dragging, Pynche will
select the color under the cursor while you drag it, but be forewarned that
this can be slow.
"""
from tkinter import *
import ColorDB
# Load this script into the Tcl interpreter and call it in
# StripWidget.set_color(). This is about as fast as it can be with the
# current _tkinter.c interface, which doesn't support Tcl Objects.
TCLPROC = '''\
proc setcolor {canv colors} {
set i 1
foreach c $colors {
$canv itemconfigure $i -fill $c -outline $c
incr i
}
}
'''
# Tcl event types
BTNDOWN = 4
BTNUP = 5
BTNDRAG = 6
SPACE = ' '
def constant(numchips):
step = 255.0 / (numchips - 1)
start = 0.0
seq = []
while numchips > 0:
seq.append(int(start))
start = start + step
numchips = numchips - 1
return seq
# red variations, green+blue = cyan constant
def constant_red_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip([red] * numchips, seq, seq))
# green variations, red+blue = magenta constant
def constant_green_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip(seq, [green] * numchips, seq))
# blue variations, red+green = yellow constant
def constant_blue_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip(seq, seq, [blue] * numchips))
# red variations, green+blue = cyan constant
def constant_cyan_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip(seq, [green] * numchips, [blue] * numchips))
# green variations, red+blue = magenta constant
def constant_magenta_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip([red] * numchips, seq, [blue] * numchips))
# blue variations, red+green = yellow constant
def constant_yellow_generator(numchips, red, green, blue):
seq = constant(numchips)
return list(zip([red] * numchips, [green] * numchips, seq))
class LeftArrow:
_ARROWWIDTH = 30
_ARROWHEIGHT = 15
_YOFFSET = 13
_TEXTYOFFSET = 1
_TAG = ('leftarrow',)
def __init__(self, canvas, x):
self._canvas = canvas
self.__arrow, self.__text = self._create(x)
self.move_to(x)
def _create(self, x):
arrow = self._canvas.create_line(
x, self._ARROWHEIGHT + self._YOFFSET,
x, self._YOFFSET,
x + self._ARROWWIDTH, self._YOFFSET,
arrow='first',
width=3.0,
tags=self._TAG)
text = self._canvas.create_text(
x + self._ARROWWIDTH + 13,
self._ARROWHEIGHT - self._TEXTYOFFSET,
tags=self._TAG,
text='128')
return arrow, text
def _x(self):
coords = list(self._canvas.coords(self._TAG))
assert coords
return coords[0]
def move_to(self, x):
deltax = x - self._x()
self._canvas.move(self._TAG, deltax, 0)
def set_text(self, text):
self._canvas.itemconfigure(self.__text, text=text)
class RightArrow(LeftArrow):
_TAG = ('rightarrow',)
def _create(self, x):
arrow = self._canvas.create_line(
x, self._YOFFSET,
x + self._ARROWWIDTH, self._YOFFSET,
x + self._ARROWWIDTH, self._ARROWHEIGHT + self._YOFFSET,
arrow='last',
width=3.0,
tags=self._TAG)
text = self._canvas.create_text(
x - self._ARROWWIDTH + 15, # BAW: kludge
self._ARROWHEIGHT - self._TEXTYOFFSET,
justify=RIGHT,
text='128',
tags=self._TAG)
return arrow, text
def _x(self):
coords = list(self._canvas.coords(self._TAG))
assert coords
return coords[0] + self._ARROWWIDTH
class StripWidget:
_CHIPHEIGHT = 50
_CHIPWIDTH = 10
_NUMCHIPS = 40
def __init__(self, switchboard,
master = None,
chipwidth = _CHIPWIDTH,
chipheight = _CHIPHEIGHT,
numchips = _NUMCHIPS,
generator = None,
axis = None,
label = '',
uwdvar = None,
hexvar = None):
# instance variables
self.__generator = generator
self.__axis = axis
self.__numchips = numchips
assert self.__axis in (0, 1, 2)
self.__uwd = uwdvar
self.__hexp = hexvar
# the last chip selected
self.__lastchip = None
self.__sb = switchboard
canvaswidth = numchips * (chipwidth + 1)
canvasheight = chipheight + 43 # BAW: Kludge
# create the canvas and pack it
canvas = self.__canvas = Canvas(master,
width=canvaswidth,
height=canvasheight,
## borderwidth=2,
## relief=GROOVE
)
canvas.pack()
canvas.bind('<ButtonPress-1>', self.__select_chip)
canvas.bind('<ButtonRelease-1>', self.__select_chip)
canvas.bind('<B1-Motion>', self.__select_chip)
# Load a proc into the Tcl interpreter. This is used in the
# set_color() method to speed up setting the chip colors.
canvas.tk.eval(TCLPROC)
# create the color strip
chips = self.__chips = []
x = 1
y = 30
tags = ('chip',)
for c in range(self.__numchips):
color = 'grey'
canvas.create_rectangle(
x, y, x+chipwidth, y+chipheight,
fill=color, outline=color,
tags=tags)
x = x + chipwidth + 1 # for outline
chips.append(color)
# create the strip label
self.__label = canvas.create_text(
3, y + chipheight + 8,
text=label,
anchor=W)
# create the arrow and text item
chipx = self.__arrow_x(0)
self.__leftarrow = LeftArrow(canvas, chipx)
chipx = self.__arrow_x(len(chips) - 1)
self.__rightarrow = RightArrow(canvas, chipx)
def __arrow_x(self, chipnum):
coords = self.__canvas.coords(chipnum+1)
assert coords
x0, y0, x1, y1 = coords
return (x1 + x0) / 2.0
# Invoked when one of the chips is clicked. This should just tell the
# switchboard to set the color on all the output components
def __select_chip(self, event=None):
x = event.x
y = event.y
canvas = self.__canvas
chip = canvas.find_overlapping(x, y, x, y)
if chip and (1 <= chip[0] <= self.__numchips):
color = self.__chips[chip[0]-1]
red, green, blue = ColorDB.rrggbb_to_triplet(color)
etype = int(event.type)
if (etype == BTNUP or self.__uwd.get()):
# update everyone
self.__sb.update_views(red, green, blue)
else:
# just track the arrows
self.__trackarrow(chip[0], (red, green, blue))
def __trackarrow(self, chip, rgbtuple):
# invert the last chip
if self.__lastchip is not None:
color = self.__canvas.itemcget(self.__lastchip, 'fill')
self.__canvas.itemconfigure(self.__lastchip, outline=color)
self.__lastchip = chip
# get the arrow's text
coloraxis = rgbtuple[self.__axis]
if self.__hexp.get():
# hex
text = hex(coloraxis)
else:
# decimal
text = repr(coloraxis)
# move the arrow, and set its text
if coloraxis <= 128:
# use the left arrow
self.__leftarrow.set_text(text)
self.__leftarrow.move_to(self.__arrow_x(chip-1))
self.__rightarrow.move_to(-100)
else:
# use the right arrow
self.__rightarrow.set_text(text)
self.__rightarrow.move_to(self.__arrow_x(chip-1))
self.__leftarrow.move_to(-100)
# and set the chip's outline
brightness = ColorDB.triplet_to_brightness(rgbtuple)
if brightness <= 128:
outline = 'white'
else:
outline = 'black'
self.__canvas.itemconfigure(chip, outline=outline)
def update_yourself(self, red, green, blue):
assert self.__generator
i = 1
chip = 0
chips = self.__chips = []
tk = self.__canvas.tk
# get the red, green, and blue components for all chips
for t in self.__generator(self.__numchips, red, green, blue):
rrggbb = ColorDB.triplet_to_rrggbb(t)
chips.append(rrggbb)
tred, tgreen, tblue = t
if tred <= red and tgreen <= green and tblue <= blue:
chip = i
i = i + 1
# call the raw tcl script
colors = SPACE.join(chips)
tk.eval('setcolor %s {%s}' % (self.__canvas._w, colors))
# move the arrows around
self.__trackarrow(chip, (red, green, blue))
def set(self, label, generator):
self.__canvas.itemconfigure(self.__label, text=label)
self.__generator = generator
class StripViewer:
def __init__(self, switchboard, master=None):
self.__sb = switchboard
optiondb = switchboard.optiondb()
# create a frame inside the master.
frame = Frame(master, relief=RAISED, borderwidth=1)
frame.grid(row=1, column=0, columnspan=2, sticky='NSEW')
# create the options to be used later
uwd = self.__uwdvar = BooleanVar()
uwd.set(optiondb.get('UPWHILEDRAG', 0))
hexp = self.__hexpvar = BooleanVar()
hexp.set(optiondb.get('HEXSTRIP', 0))
# create the red, green, blue strips inside their own frame
frame1 = Frame(frame)
frame1.pack(expand=YES, fill=BOTH)
self.__reds = StripWidget(switchboard, frame1,
generator=constant_cyan_generator,
axis=0,
label='Red Variations',
uwdvar=uwd, hexvar=hexp)
self.__greens = StripWidget(switchboard, frame1,
generator=constant_magenta_generator,
axis=1,
label='Green Variations',
uwdvar=uwd, hexvar=hexp)
self.__blues = StripWidget(switchboard, frame1,
generator=constant_yellow_generator,
axis=2,
label='Blue Variations',
uwdvar=uwd, hexvar=hexp)
# create a frame to contain the controls
frame2 = Frame(frame)
frame2.pack(expand=YES, fill=BOTH)
frame2.columnconfigure(0, weight=20)
frame2.columnconfigure(2, weight=20)
padx = 8
# create the black button
blackbtn = Button(frame2,
text='Black',
command=self.__toblack)
blackbtn.grid(row=0, column=0, rowspan=2, sticky=W, padx=padx)
# create the controls
uwdbtn = Checkbutton(frame2,
text='Update while dragging',
variable=uwd)
uwdbtn.grid(row=0, column=1, sticky=W)
hexbtn = Checkbutton(frame2,
text='Hexadecimal',
variable=hexp,
command=self.__togglehex)
hexbtn.grid(row=1, column=1, sticky=W)
# XXX: ignore this feature for now; it doesn't work quite right yet
## gentypevar = self.__gentypevar = IntVar()
## self.__variations = Radiobutton(frame,
## text='Variations',
## variable=gentypevar,
## value=0,
## command=self.__togglegentype)
## self.__variations.grid(row=0, column=1, sticky=W)
## self.__constants = Radiobutton(frame,
## text='Constants',
## variable=gentypevar,
## value=1,
## command=self.__togglegentype)
## self.__constants.grid(row=1, column=1, sticky=W)
# create the white button
whitebtn = Button(frame2,
text='White',
command=self.__towhite)
whitebtn.grid(row=0, column=2, rowspan=2, sticky=E, padx=padx)
def update_yourself(self, red, green, blue):
self.__reds.update_yourself(red, green, blue)
self.__greens.update_yourself(red, green, blue)
self.__blues.update_yourself(red, green, blue)
def __togglehex(self, event=None):
red, green, blue = self.__sb.current_rgb()
self.update_yourself(red, green, blue)
## def __togglegentype(self, event=None):
## which = self.__gentypevar.get()
## if which == 0:
## self.__reds.set(label='Red Variations',
## generator=constant_cyan_generator)
## self.__greens.set(label='Green Variations',
## generator=constant_magenta_generator)
## self.__blues.set(label='Blue Variations',
## generator=constant_yellow_generator)
## elif which == 1:
## self.__reds.set(label='Red Constant',
## generator=constant_red_generator)
## self.__greens.set(label='Green Constant',
## generator=constant_green_generator)
## self.__blues.set(label='Blue Constant',
## generator=constant_blue_generator)
## else:
## assert 0
## self.__sb.update_views_current()
def __toblack(self, event=None):
self.__sb.update_views(0, 0, 0)
def __towhite(self, event=None):
self.__sb.update_views(255, 255, 255)
def save_options(self, optiondb):
optiondb['UPWHILEDRAG'] = self.__uwdvar.get()
optiondb['HEXSTRIP'] = self.__hexpvar.get()

View file

@ -0,0 +1,138 @@
"""Switchboard class.
This class is used to coordinate updates among all Viewers. Every Viewer must
conform to the following interface:
- it must include a method called update_yourself() which takes three
arguments; the red, green, and blue values of the selected color.
- When a Viewer selects a color and wishes to update all other Views, it
should call update_views() on the Switchboard object. Note that the
Viewer typically does *not* update itself before calling update_views(),
since this would cause it to get updated twice.
Optionally, Viewers can also implement:
- save_options() which takes an optiondb (a dictionary). Store into this
dictionary any values the Viewer wants to save in the persistent
~/.pynche file. This dictionary is saved using marshal. The namespace
for the keys is ad-hoc; make sure you don't clobber some other Viewer's
keys!
- withdraw() which takes no arguments. This is called when Pynche is
unmapped. All Viewers should implement this.
- colordb_changed() which takes a single argument, an instance of
ColorDB. This is called whenever the color name database is changed and
gives a chance for the Viewers to do something on those events. See
ListViewer for details.
External Viewers are found dynamically. Viewer modules should have names such
as FooViewer.py. If such a named module has a module global variable called
ADDTOVIEW and this variable is true, the Viewer will be added dynamically to
the `View' menu. ADDTOVIEW contains a string which is used as the menu item
to display the Viewer (one kludge: if the string contains a `%', this is used
to indicate that the next character will get an underline in the menu,
otherwise the first character is underlined).
FooViewer.py should contain a class called FooViewer, and its constructor
should take two arguments, an instance of Switchboard, and optionally a Tk
master window.
"""
import sys
import marshal
class Switchboard:
def __init__(self, initfile):
self.__initfile = initfile
self.__colordb = None
self.__optiondb = {}
self.__views = []
self.__red = 0
self.__green = 0
self.__blue = 0
self.__canceled = 0
# read the initialization file
fp = None
if initfile:
try:
try:
fp = open(initfile, 'rb')
self.__optiondb = marshal.load(fp)
if not isinstance(self.__optiondb, dict):
print('Problem reading options from file:', initfile,
file=sys.stderr)
self.__optiondb = {}
except (IOError, EOFError, ValueError):
pass
finally:
if fp:
fp.close()
def add_view(self, view):
self.__views.append(view)
def update_views(self, red, green, blue):
self.__red = red
self.__green = green
self.__blue = blue
for v in self.__views:
v.update_yourself(red, green, blue)
def update_views_current(self):
self.update_views(self.__red, self.__green, self.__blue)
def current_rgb(self):
return self.__red, self.__green, self.__blue
def colordb(self):
return self.__colordb
def set_colordb(self, colordb):
self.__colordb = colordb
for v in self.__views:
if hasattr(v, 'colordb_changed'):
v.colordb_changed(colordb)
self.update_views_current()
def optiondb(self):
return self.__optiondb
def save_views(self):
# save the current color
self.__optiondb['RED'] = self.__red
self.__optiondb['GREEN'] = self.__green
self.__optiondb['BLUE'] = self.__blue
for v in self.__views:
if hasattr(v, 'save_options'):
v.save_options(self.__optiondb)
# save the name of the file used for the color database. we'll try to
# load this first.
self.__optiondb['DBFILE'] = self.__colordb.filename()
fp = None
try:
try:
fp = open(self.__initfile, 'wb')
except IOError:
print('Cannot write options to file:', \
self.__initfile, file=sys.stderr)
else:
marshal.dump(self.__optiondb, fp)
finally:
if fp:
fp.close()
def withdraw_views(self):
for v in self.__views:
if hasattr(v, 'withdraw'):
v.withdraw()
def canceled(self, flag=1):
self.__canceled = flag
def canceled_p(self):
return self.__canceled

View file

@ -0,0 +1,188 @@
"""TextViewer class.
The TextViewer allows you to see how the selected color would affect various
characteristics of a Tk text widget. This is an output viewer only.
In the top part of the window is a standard text widget with some sample text
in it. You are free to edit this text in any way you want (BAW: allow you to
change font characteristics). If you want changes in other viewers to update
text characteristics, turn on Track color changes.
To select which characteristic tracks the change, select one of the radio
buttons in the window below. Text foreground and background affect the text
in the window above. The Selection is what you see when you click the middle
button and drag it through some text. The Insertion is the insertion cursor
in the text window (which only has a background).
"""
from tkinter import *
import ColorDB
ADDTOVIEW = 'Text Window...'
class TextViewer:
def __init__(self, switchboard, master=None):
self.__sb = switchboard
optiondb = switchboard.optiondb()
root = self.__root = Toplevel(master, class_='Pynche')
root.protocol('WM_DELETE_WINDOW', self.withdraw)
root.title('Pynche Text Window')
root.iconname('Pynche Text Window')
root.bind('<Alt-q>', self.__quit)
root.bind('<Alt-Q>', self.__quit)
root.bind('<Alt-w>', self.withdraw)
root.bind('<Alt-W>', self.withdraw)
#
# create the text widget
#
self.__text = Text(root, relief=SUNKEN,
background=optiondb.get('TEXTBG', 'black'),
foreground=optiondb.get('TEXTFG', 'white'),
width=35, height=15)
sfg = optiondb.get('TEXT_SFG')
if sfg:
self.__text.configure(selectforeground=sfg)
sbg = optiondb.get('TEXT_SBG')
if sbg:
self.__text.configure(selectbackground=sbg)
ibg = optiondb.get('TEXT_IBG')
if ibg:
self.__text.configure(insertbackground=ibg)
self.__text.pack()
self.__text.insert(0.0, optiondb.get('TEXT', '''\
Insert some stuff here and play
with the buttons below to see
how the colors interact in
textual displays.
See how the selection can also
be affected by tickling the buttons
and choosing a color.'''))
insert = optiondb.get('TEXTINS')
if insert:
self.__text.mark_set(INSERT, insert)
try:
start, end = optiondb.get('TEXTSEL', (6.0, END))
self.__text.tag_add(SEL, start, end)
except ValueError:
# selection wasn't set
pass
self.__text.focus_set()
#
# variables
self.__trackp = BooleanVar()
self.__trackp.set(optiondb.get('TRACKP', 0))
self.__which = IntVar()
self.__which.set(optiondb.get('WHICH', 0))
#
# track toggle
self.__t = Checkbutton(root, text='Track color changes',
variable=self.__trackp,
relief=GROOVE,
command=self.__toggletrack)
self.__t.pack(fill=X, expand=YES)
frame = self.__frame = Frame(root)
frame.pack()
#
# labels
self.__labels = []
row = 2
for text in ('Text:', 'Selection:', 'Insertion:'):
l = Label(frame, text=text)
l.grid(row=row, column=0, sticky=E)
self.__labels.append(l)
row += 1
col = 1
for text in ('Foreground', 'Background'):
l = Label(frame, text=text)
l.grid(row=1, column=col)
self.__labels.append(l)
col += 1
#
# radios
self.__radios = []
for col in (1, 2):
for row in (2, 3, 4):
# there is no insertforeground option
if row==4 and col==1:
continue
r = Radiobutton(frame, variable=self.__which,
value=(row-2)*2 + col-1,
command=self.__set_color)
r.grid(row=row, column=col)
self.__radios.append(r)
self.__toggletrack()
def __quit(self, event=None):
self.__root.quit()
def withdraw(self, event=None):
self.__root.withdraw()
def deiconify(self, event=None):
self.__root.deiconify()
def __forceupdate(self, event=None):
self.__sb.update_views_current()
def __toggletrack(self, event=None):
if self.__trackp.get():
state = NORMAL
fg = self.__radios[0]['foreground']
else:
state = DISABLED
fg = self.__radios[0]['disabledforeground']
for r in self.__radios:
r.configure(state=state)
for l in self.__labels:
l.configure(foreground=fg)
def __set_color(self, event=None):
which = self.__which.get()
text = self.__text
if which == 0:
color = text['foreground']
elif which == 1:
color = text['background']
elif which == 2:
color = text['selectforeground']
elif which == 3:
color = text['selectbackground']
elif which == 5:
color = text['insertbackground']
try:
red, green, blue = ColorDB.rrggbb_to_triplet(color)
except ColorDB.BadColor:
# must have been a color name
red, green, blue = self.__sb.colordb().find_byname(color)
self.__sb.update_views(red, green, blue)
def update_yourself(self, red, green, blue):
if self.__trackp.get():
colorname = ColorDB.triplet_to_rrggbb((red, green, blue))
which = self.__which.get()
text = self.__text
if which == 0:
text.configure(foreground=colorname)
elif which == 1:
text.configure(background=colorname)
elif which == 2:
text.configure(selectforeground=colorname)
elif which == 3:
text.configure(selectbackground=colorname)
elif which == 5:
text.configure(insertbackground=colorname)
def save_options(self, optiondb):
optiondb['TRACKP'] = self.__trackp.get()
optiondb['WHICH'] = self.__which.get()
optiondb['TEXT'] = self.__text.get(0.0, 'end - 1c')
optiondb['TEXTSEL'] = self.__text.tag_ranges(SEL)[0:2]
optiondb['TEXTINS'] = self.__text.index(INSERT)
optiondb['TEXTFG'] = self.__text['foreground']
optiondb['TEXTBG'] = self.__text['background']
optiondb['TEXT_SFG'] = self.__text['selectforeground']
optiondb['TEXT_SBG'] = self.__text['selectbackground']
optiondb['TEXT_IBG'] = self.__text['insertbackground']

View file

@ -0,0 +1,161 @@
"""TypeinViewer class.
The TypeinViewer is what you see at the lower right of the main Pynche
widget. It contains three text entry fields, one each for red, green, blue.
Input into these windows is highly constrained; it only allows you to enter
values that are legal for a color axis. This usually means 0-255 for decimal
input and 0x0 - 0xff for hex input.
You can toggle whether you want to view and input the values in either decimal
or hex by clicking on Hexadecimal. By clicking on Update while typing, the
color selection will be made on every change to the text field. Otherwise,
you must hit Return or Tab to select the color.
"""
from tkinter import *
class TypeinViewer:
def __init__(self, switchboard, master=None):
# non-gui ivars
self.__sb = switchboard
optiondb = switchboard.optiondb()
self.__hexp = BooleanVar()
self.__hexp.set(optiondb.get('HEXTYPE', 0))
self.__uwtyping = BooleanVar()
self.__uwtyping.set(optiondb.get('UPWHILETYPE', 0))
# create the gui
self.__frame = Frame(master, relief=RAISED, borderwidth=1)
self.__frame.grid(row=3, column=1, sticky='NSEW')
# Red
self.__xl = Label(self.__frame, text='Red:')
self.__xl.grid(row=0, column=0, sticky=E)
subframe = Frame(self.__frame)
subframe.grid(row=0, column=1)
self.__xox = Label(subframe, text='0x')
self.__xox.grid(row=0, column=0, sticky=E)
self.__xox['font'] = 'courier'
self.__x = Entry(subframe, width=3)
self.__x.grid(row=0, column=1)
self.__x.bindtags(self.__x.bindtags() + ('Normalize', 'Update'))
self.__x.bind_class('Normalize', '<Key>', self.__normalize)
self.__x.bind_class('Update' , '<Key>', self.__maybeupdate)
# Green
self.__yl = Label(self.__frame, text='Green:')
self.__yl.grid(row=1, column=0, sticky=E)
subframe = Frame(self.__frame)
subframe.grid(row=1, column=1)
self.__yox = Label(subframe, text='0x')
self.__yox.grid(row=0, column=0, sticky=E)
self.__yox['font'] = 'courier'
self.__y = Entry(subframe, width=3)
self.__y.grid(row=0, column=1)
self.__y.bindtags(self.__y.bindtags() + ('Normalize', 'Update'))
# Blue
self.__zl = Label(self.__frame, text='Blue:')
self.__zl.grid(row=2, column=0, sticky=E)
subframe = Frame(self.__frame)
subframe.grid(row=2, column=1)
self.__zox = Label(subframe, text='0x')
self.__zox.grid(row=0, column=0, sticky=E)
self.__zox['font'] = 'courier'
self.__z = Entry(subframe, width=3)
self.__z.grid(row=0, column=1)
self.__z.bindtags(self.__z.bindtags() + ('Normalize', 'Update'))
# Update while typing?
self.__uwt = Checkbutton(self.__frame,
text='Update while typing',
variable=self.__uwtyping)
self.__uwt.grid(row=3, column=0, columnspan=2, sticky=W)
# Hex/Dec
self.__hex = Checkbutton(self.__frame,
text='Hexadecimal',
variable=self.__hexp,
command=self.__togglehex)
self.__hex.grid(row=4, column=0, columnspan=2, sticky=W)
def __togglehex(self, event=None):
red, green, blue = self.__sb.current_rgb()
if self.__hexp.get():
label = '0x'
else:
label = ' '
self.__xox['text'] = label
self.__yox['text'] = label
self.__zox['text'] = label
self.update_yourself(red, green, blue)
def __normalize(self, event=None):
ew = event.widget
contents = ew.get()
icursor = ew.index(INSERT)
if contents and contents[0] in 'xX' and self.__hexp.get():
contents = '0' + contents
# Figure out the contents in the current base.
try:
if self.__hexp.get():
v = int(contents, 16)
else:
v = int(contents)
except ValueError:
v = None
# If value is not legal, or empty, delete the last character inserted
# and ring the bell. Don't ring the bell if the field is empty (it'll
# just equal zero.
if v is None:
pass
elif v < 0 or v > 255:
i = ew.index(INSERT)
if event.char:
contents = contents[:i-1] + contents[i:]
icursor -= 1
ew.bell()
elif self.__hexp.get():
contents = hex(v)[2:]
else:
contents = int(v)
ew.delete(0, END)
ew.insert(0, contents)
ew.icursor(icursor)
def __maybeupdate(self, event=None):
if self.__uwtyping.get() or event.keysym in ('Return', 'Tab'):
self.__update(event)
def __update(self, event=None):
redstr = self.__x.get() or '0'
greenstr = self.__y.get() or '0'
bluestr = self.__z.get() or '0'
if self.__hexp.get():
base = 16
else:
base = 10
red, green, blue = [int(x, base) for x in (redstr, greenstr, bluestr)]
self.__sb.update_views(red, green, blue)
def update_yourself(self, red, green, blue):
if self.__hexp.get():
sred, sgreen, sblue = [hex(x)[2:] for x in (red, green, blue)]
else:
sred, sgreen, sblue = red, green, blue
x, y, z = self.__x, self.__y, self.__z
xicursor = x.index(INSERT)
yicursor = y.index(INSERT)
zicursor = z.index(INSERT)
x.delete(0, END)
y.delete(0, END)
z.delete(0, END)
x.insert(0, sred)
y.insert(0, sgreen)
z.insert(0, sblue)
x.icursor(xicursor)
y.icursor(yicursor)
z.icursor(zicursor)
def hexp_var(self):
return self.__hexp
def save_options(self, optiondb):
optiondb['HEXTYPE'] = self.__hexp.get()
optiondb['UPWHILETYPE'] = self.__uwtyping.get()

View file

@ -0,0 +1,753 @@
! $XConsortium: rgb.txt,v 10.41 94/02/20 18:39:36 rws Exp $
255 250 250 snow
248 248 255 ghost white
248 248 255 GhostWhite
245 245 245 white smoke
245 245 245 WhiteSmoke
220 220 220 gainsboro
255 250 240 floral white
255 250 240 FloralWhite
253 245 230 old lace
253 245 230 OldLace
250 240 230 linen
250 235 215 antique white
250 235 215 AntiqueWhite
255 239 213 papaya whip
255 239 213 PapayaWhip
255 235 205 blanched almond
255 235 205 BlanchedAlmond
255 228 196 bisque
255 218 185 peach puff
255 218 185 PeachPuff
255 222 173 navajo white
255 222 173 NavajoWhite
255 228 181 moccasin
255 248 220 cornsilk
255 255 240 ivory
255 250 205 lemon chiffon
255 250 205 LemonChiffon
255 245 238 seashell
240 255 240 honeydew
245 255 250 mint cream
245 255 250 MintCream
240 255 255 azure
240 248 255 alice blue
240 248 255 AliceBlue
230 230 250 lavender
255 240 245 lavender blush
255 240 245 LavenderBlush
255 228 225 misty rose
255 228 225 MistyRose
255 255 255 white
0 0 0 black
47 79 79 dark slate gray
47 79 79 DarkSlateGray
47 79 79 dark slate grey
47 79 79 DarkSlateGrey
105 105 105 dim gray
105 105 105 DimGray
105 105 105 dim grey
105 105 105 DimGrey
112 128 144 slate gray
112 128 144 SlateGray
112 128 144 slate grey
112 128 144 SlateGrey
119 136 153 light slate gray
119 136 153 LightSlateGray
119 136 153 light slate grey
119 136 153 LightSlateGrey
190 190 190 gray
190 190 190 grey
211 211 211 light grey
211 211 211 LightGrey
211 211 211 light gray
211 211 211 LightGray
25 25 112 midnight blue
25 25 112 MidnightBlue
0 0 128 navy
0 0 128 navy blue
0 0 128 NavyBlue
100 149 237 cornflower blue
100 149 237 CornflowerBlue
72 61 139 dark slate blue
72 61 139 DarkSlateBlue
106 90 205 slate blue
106 90 205 SlateBlue
123 104 238 medium slate blue
123 104 238 MediumSlateBlue
132 112 255 light slate blue
132 112 255 LightSlateBlue
0 0 205 medium blue
0 0 205 MediumBlue
65 105 225 royal blue
65 105 225 RoyalBlue
0 0 255 blue
30 144 255 dodger blue
30 144 255 DodgerBlue
0 191 255 deep sky blue
0 191 255 DeepSkyBlue
135 206 235 sky blue
135 206 235 SkyBlue
135 206 250 light sky blue
135 206 250 LightSkyBlue
70 130 180 steel blue
70 130 180 SteelBlue
176 196 222 light steel blue
176 196 222 LightSteelBlue
173 216 230 light blue
173 216 230 LightBlue
176 224 230 powder blue
176 224 230 PowderBlue
175 238 238 pale turquoise
175 238 238 PaleTurquoise
0 206 209 dark turquoise
0 206 209 DarkTurquoise
72 209 204 medium turquoise
72 209 204 MediumTurquoise
64 224 208 turquoise
0 255 255 cyan
224 255 255 light cyan
224 255 255 LightCyan
95 158 160 cadet blue
95 158 160 CadetBlue
102 205 170 medium aquamarine
102 205 170 MediumAquamarine
127 255 212 aquamarine
0 100 0 dark green
0 100 0 DarkGreen
85 107 47 dark olive green
85 107 47 DarkOliveGreen
143 188 143 dark sea green
143 188 143 DarkSeaGreen
46 139 87 sea green
46 139 87 SeaGreen
60 179 113 medium sea green
60 179 113 MediumSeaGreen
32 178 170 light sea green
32 178 170 LightSeaGreen
152 251 152 pale green
152 251 152 PaleGreen
0 255 127 spring green
0 255 127 SpringGreen
124 252 0 lawn green
124 252 0 LawnGreen
0 255 0 green
127 255 0 chartreuse
0 250 154 medium spring green
0 250 154 MediumSpringGreen
173 255 47 green yellow
173 255 47 GreenYellow
50 205 50 lime green
50 205 50 LimeGreen
154 205 50 yellow green
154 205 50 YellowGreen
34 139 34 forest green
34 139 34 ForestGreen
107 142 35 olive drab
107 142 35 OliveDrab
189 183 107 dark khaki
189 183 107 DarkKhaki
240 230 140 khaki
238 232 170 pale goldenrod
238 232 170 PaleGoldenrod
250 250 210 light goldenrod yellow
250 250 210 LightGoldenrodYellow
255 255 224 light yellow
255 255 224 LightYellow
255 255 0 yellow
255 215 0 gold
238 221 130 light goldenrod
238 221 130 LightGoldenrod
218 165 32 goldenrod
184 134 11 dark goldenrod
184 134 11 DarkGoldenrod
188 143 143 rosy brown
188 143 143 RosyBrown
205 92 92 indian red
205 92 92 IndianRed
139 69 19 saddle brown
139 69 19 SaddleBrown
160 82 45 sienna
205 133 63 peru
222 184 135 burlywood
245 245 220 beige
245 222 179 wheat
244 164 96 sandy brown
244 164 96 SandyBrown
210 180 140 tan
210 105 30 chocolate
178 34 34 firebrick
165 42 42 brown
233 150 122 dark salmon
233 150 122 DarkSalmon
250 128 114 salmon
255 160 122 light salmon
255 160 122 LightSalmon
255 165 0 orange
255 140 0 dark orange
255 140 0 DarkOrange
255 127 80 coral
240 128 128 light coral
240 128 128 LightCoral
255 99 71 tomato
255 69 0 orange red
255 69 0 OrangeRed
255 0 0 red
255 105 180 hot pink
255 105 180 HotPink
255 20 147 deep pink
255 20 147 DeepPink
255 192 203 pink
255 182 193 light pink
255 182 193 LightPink
219 112 147 pale violet red
219 112 147 PaleVioletRed
176 48 96 maroon
199 21 133 medium violet red
199 21 133 MediumVioletRed
208 32 144 violet red
208 32 144 VioletRed
255 0 255 magenta
238 130 238 violet
221 160 221 plum
218 112 214 orchid
186 85 211 medium orchid
186 85 211 MediumOrchid
153 50 204 dark orchid
153 50 204 DarkOrchid
148 0 211 dark violet
148 0 211 DarkViolet
138 43 226 blue violet
138 43 226 BlueViolet
160 32 240 purple
147 112 219 medium purple
147 112 219 MediumPurple
216 191 216 thistle
255 250 250 snow1
238 233 233 snow2
205 201 201 snow3
139 137 137 snow4
255 245 238 seashell1
238 229 222 seashell2
205 197 191 seashell3
139 134 130 seashell4
255 239 219 AntiqueWhite1
238 223 204 AntiqueWhite2
205 192 176 AntiqueWhite3
139 131 120 AntiqueWhite4
255 228 196 bisque1
238 213 183 bisque2
205 183 158 bisque3
139 125 107 bisque4
255 218 185 PeachPuff1
238 203 173 PeachPuff2
205 175 149 PeachPuff3
139 119 101 PeachPuff4
255 222 173 NavajoWhite1
238 207 161 NavajoWhite2
205 179 139 NavajoWhite3
139 121 94 NavajoWhite4
255 250 205 LemonChiffon1
238 233 191 LemonChiffon2
205 201 165 LemonChiffon3
139 137 112 LemonChiffon4
255 248 220 cornsilk1
238 232 205 cornsilk2
205 200 177 cornsilk3
139 136 120 cornsilk4
255 255 240 ivory1
238 238 224 ivory2
205 205 193 ivory3
139 139 131 ivory4
240 255 240 honeydew1
224 238 224 honeydew2
193 205 193 honeydew3
131 139 131 honeydew4
255 240 245 LavenderBlush1
238 224 229 LavenderBlush2
205 193 197 LavenderBlush3
139 131 134 LavenderBlush4
255 228 225 MistyRose1
238 213 210 MistyRose2
205 183 181 MistyRose3
139 125 123 MistyRose4
240 255 255 azure1
224 238 238 azure2
193 205 205 azure3
131 139 139 azure4
131 111 255 SlateBlue1
122 103 238 SlateBlue2
105 89 205 SlateBlue3
71 60 139 SlateBlue4
72 118 255 RoyalBlue1
67 110 238 RoyalBlue2
58 95 205 RoyalBlue3
39 64 139 RoyalBlue4
0 0 255 blue1
0 0 238 blue2
0 0 205 blue3
0 0 139 blue4
30 144 255 DodgerBlue1
28 134 238 DodgerBlue2
24 116 205 DodgerBlue3
16 78 139 DodgerBlue4
99 184 255 SteelBlue1
92 172 238 SteelBlue2
79 148 205 SteelBlue3
54 100 139 SteelBlue4
0 191 255 DeepSkyBlue1
0 178 238 DeepSkyBlue2
0 154 205 DeepSkyBlue3
0 104 139 DeepSkyBlue4
135 206 255 SkyBlue1
126 192 238 SkyBlue2
108 166 205 SkyBlue3
74 112 139 SkyBlue4
176 226 255 LightSkyBlue1
164 211 238 LightSkyBlue2
141 182 205 LightSkyBlue3
96 123 139 LightSkyBlue4
198 226 255 SlateGray1
185 211 238 SlateGray2
159 182 205 SlateGray3
108 123 139 SlateGray4
202 225 255 LightSteelBlue1
188 210 238 LightSteelBlue2
162 181 205 LightSteelBlue3
110 123 139 LightSteelBlue4
191 239 255 LightBlue1
178 223 238 LightBlue2
154 192 205 LightBlue3
104 131 139 LightBlue4
224 255 255 LightCyan1
209 238 238 LightCyan2
180 205 205 LightCyan3
122 139 139 LightCyan4
187 255 255 PaleTurquoise1
174 238 238 PaleTurquoise2
150 205 205 PaleTurquoise3
102 139 139 PaleTurquoise4
152 245 255 CadetBlue1
142 229 238 CadetBlue2
122 197 205 CadetBlue3
83 134 139 CadetBlue4
0 245 255 turquoise1
0 229 238 turquoise2
0 197 205 turquoise3
0 134 139 turquoise4
0 255 255 cyan1
0 238 238 cyan2
0 205 205 cyan3
0 139 139 cyan4
151 255 255 DarkSlateGray1
141 238 238 DarkSlateGray2
121 205 205 DarkSlateGray3
82 139 139 DarkSlateGray4
127 255 212 aquamarine1
118 238 198 aquamarine2
102 205 170 aquamarine3
69 139 116 aquamarine4
193 255 193 DarkSeaGreen1
180 238 180 DarkSeaGreen2
155 205 155 DarkSeaGreen3
105 139 105 DarkSeaGreen4
84 255 159 SeaGreen1
78 238 148 SeaGreen2
67 205 128 SeaGreen3
46 139 87 SeaGreen4
154 255 154 PaleGreen1
144 238 144 PaleGreen2
124 205 124 PaleGreen3
84 139 84 PaleGreen4
0 255 127 SpringGreen1
0 238 118 SpringGreen2
0 205 102 SpringGreen3
0 139 69 SpringGreen4
0 255 0 green1
0 238 0 green2
0 205 0 green3
0 139 0 green4
127 255 0 chartreuse1
118 238 0 chartreuse2
102 205 0 chartreuse3
69 139 0 chartreuse4
192 255 62 OliveDrab1
179 238 58 OliveDrab2
154 205 50 OliveDrab3
105 139 34 OliveDrab4
202 255 112 DarkOliveGreen1
188 238 104 DarkOliveGreen2
162 205 90 DarkOliveGreen3
110 139 61 DarkOliveGreen4
255 246 143 khaki1
238 230 133 khaki2
205 198 115 khaki3
139 134 78 khaki4
255 236 139 LightGoldenrod1
238 220 130 LightGoldenrod2
205 190 112 LightGoldenrod3
139 129 76 LightGoldenrod4
255 255 224 LightYellow1
238 238 209 LightYellow2
205 205 180 LightYellow3
139 139 122 LightYellow4
255 255 0 yellow1
238 238 0 yellow2
205 205 0 yellow3
139 139 0 yellow4
255 215 0 gold1
238 201 0 gold2
205 173 0 gold3
139 117 0 gold4
255 193 37 goldenrod1
238 180 34 goldenrod2
205 155 29 goldenrod3
139 105 20 goldenrod4
255 185 15 DarkGoldenrod1
238 173 14 DarkGoldenrod2
205 149 12 DarkGoldenrod3
139 101 8 DarkGoldenrod4
255 193 193 RosyBrown1
238 180 180 RosyBrown2
205 155 155 RosyBrown3
139 105 105 RosyBrown4
255 106 106 IndianRed1
238 99 99 IndianRed2
205 85 85 IndianRed3
139 58 58 IndianRed4
255 130 71 sienna1
238 121 66 sienna2
205 104 57 sienna3
139 71 38 sienna4
255 211 155 burlywood1
238 197 145 burlywood2
205 170 125 burlywood3
139 115 85 burlywood4
255 231 186 wheat1
238 216 174 wheat2
205 186 150 wheat3
139 126 102 wheat4
255 165 79 tan1
238 154 73 tan2
205 133 63 tan3
139 90 43 tan4
255 127 36 chocolate1
238 118 33 chocolate2
205 102 29 chocolate3
139 69 19 chocolate4
255 48 48 firebrick1
238 44 44 firebrick2
205 38 38 firebrick3
139 26 26 firebrick4
255 64 64 brown1
238 59 59 brown2
205 51 51 brown3
139 35 35 brown4
255 140 105 salmon1
238 130 98 salmon2
205 112 84 salmon3
139 76 57 salmon4
255 160 122 LightSalmon1
238 149 114 LightSalmon2
205 129 98 LightSalmon3
139 87 66 LightSalmon4
255 165 0 orange1
238 154 0 orange2
205 133 0 orange3
139 90 0 orange4
255 127 0 DarkOrange1
238 118 0 DarkOrange2
205 102 0 DarkOrange3
139 69 0 DarkOrange4
255 114 86 coral1
238 106 80 coral2
205 91 69 coral3
139 62 47 coral4
255 99 71 tomato1
238 92 66 tomato2
205 79 57 tomato3
139 54 38 tomato4
255 69 0 OrangeRed1
238 64 0 OrangeRed2
205 55 0 OrangeRed3
139 37 0 OrangeRed4
255 0 0 red1
238 0 0 red2
205 0 0 red3
139 0 0 red4
255 20 147 DeepPink1
238 18 137 DeepPink2
205 16 118 DeepPink3
139 10 80 DeepPink4
255 110 180 HotPink1
238 106 167 HotPink2
205 96 144 HotPink3
139 58 98 HotPink4
255 181 197 pink1
238 169 184 pink2
205 145 158 pink3
139 99 108 pink4
255 174 185 LightPink1
238 162 173 LightPink2
205 140 149 LightPink3
139 95 101 LightPink4
255 130 171 PaleVioletRed1
238 121 159 PaleVioletRed2
205 104 137 PaleVioletRed3
139 71 93 PaleVioletRed4
255 52 179 maroon1
238 48 167 maroon2
205 41 144 maroon3
139 28 98 maroon4
255 62 150 VioletRed1
238 58 140 VioletRed2
205 50 120 VioletRed3
139 34 82 VioletRed4
255 0 255 magenta1
238 0 238 magenta2
205 0 205 magenta3
139 0 139 magenta4
255 131 250 orchid1
238 122 233 orchid2
205 105 201 orchid3
139 71 137 orchid4
255 187 255 plum1
238 174 238 plum2
205 150 205 plum3
139 102 139 plum4
224 102 255 MediumOrchid1
209 95 238 MediumOrchid2
180 82 205 MediumOrchid3
122 55 139 MediumOrchid4
191 62 255 DarkOrchid1
178 58 238 DarkOrchid2
154 50 205 DarkOrchid3
104 34 139 DarkOrchid4
155 48 255 purple1
145 44 238 purple2
125 38 205 purple3
85 26 139 purple4
171 130 255 MediumPurple1
159 121 238 MediumPurple2
137 104 205 MediumPurple3
93 71 139 MediumPurple4
255 225 255 thistle1
238 210 238 thistle2
205 181 205 thistle3
139 123 139 thistle4
0 0 0 gray0
0 0 0 grey0
3 3 3 gray1
3 3 3 grey1
5 5 5 gray2
5 5 5 grey2
8 8 8 gray3
8 8 8 grey3
10 10 10 gray4
10 10 10 grey4
13 13 13 gray5
13 13 13 grey5
15 15 15 gray6
15 15 15 grey6
18 18 18 gray7
18 18 18 grey7
20 20 20 gray8
20 20 20 grey8
23 23 23 gray9
23 23 23 grey9
26 26 26 gray10
26 26 26 grey10
28 28 28 gray11
28 28 28 grey11
31 31 31 gray12
31 31 31 grey12
33 33 33 gray13
33 33 33 grey13
36 36 36 gray14
36 36 36 grey14
38 38 38 gray15
38 38 38 grey15
41 41 41 gray16
41 41 41 grey16
43 43 43 gray17
43 43 43 grey17
46 46 46 gray18
46 46 46 grey18
48 48 48 gray19
48 48 48 grey19
51 51 51 gray20
51 51 51 grey20
54 54 54 gray21
54 54 54 grey21
56 56 56 gray22
56 56 56 grey22
59 59 59 gray23
59 59 59 grey23
61 61 61 gray24
61 61 61 grey24
64 64 64 gray25
64 64 64 grey25
66 66 66 gray26
66 66 66 grey26
69 69 69 gray27
69 69 69 grey27
71 71 71 gray28
71 71 71 grey28
74 74 74 gray29
74 74 74 grey29
77 77 77 gray30
77 77 77 grey30
79 79 79 gray31
79 79 79 grey31
82 82 82 gray32
82 82 82 grey32
84 84 84 gray33
84 84 84 grey33
87 87 87 gray34
87 87 87 grey34
89 89 89 gray35
89 89 89 grey35
92 92 92 gray36
92 92 92 grey36
94 94 94 gray37
94 94 94 grey37
97 97 97 gray38
97 97 97 grey38
99 99 99 gray39
99 99 99 grey39
102 102 102 gray40
102 102 102 grey40
105 105 105 gray41
105 105 105 grey41
107 107 107 gray42
107 107 107 grey42
110 110 110 gray43
110 110 110 grey43
112 112 112 gray44
112 112 112 grey44
115 115 115 gray45
115 115 115 grey45
117 117 117 gray46
117 117 117 grey46
120 120 120 gray47
120 120 120 grey47
122 122 122 gray48
122 122 122 grey48
125 125 125 gray49
125 125 125 grey49
127 127 127 gray50
127 127 127 grey50
130 130 130 gray51
130 130 130 grey51
133 133 133 gray52
133 133 133 grey52
135 135 135 gray53
135 135 135 grey53
138 138 138 gray54
138 138 138 grey54
140 140 140 gray55
140 140 140 grey55
143 143 143 gray56
143 143 143 grey56
145 145 145 gray57
145 145 145 grey57
148 148 148 gray58
148 148 148 grey58
150 150 150 gray59
150 150 150 grey59
153 153 153 gray60
153 153 153 grey60
156 156 156 gray61
156 156 156 grey61
158 158 158 gray62
158 158 158 grey62
161 161 161 gray63
161 161 161 grey63
163 163 163 gray64
163 163 163 grey64
166 166 166 gray65
166 166 166 grey65
168 168 168 gray66
168 168 168 grey66
171 171 171 gray67
171 171 171 grey67
173 173 173 gray68
173 173 173 grey68
176 176 176 gray69
176 176 176 grey69
179 179 179 gray70
179 179 179 grey70
181 181 181 gray71
181 181 181 grey71
184 184 184 gray72
184 184 184 grey72
186 186 186 gray73
186 186 186 grey73
189 189 189 gray74
189 189 189 grey74
191 191 191 gray75
191 191 191 grey75
194 194 194 gray76
194 194 194 grey76
196 196 196 gray77
196 196 196 grey77
199 199 199 gray78
199 199 199 grey78
201 201 201 gray79
201 201 201 grey79
204 204 204 gray80
204 204 204 grey80
207 207 207 gray81
207 207 207 grey81
209 209 209 gray82
209 209 209 grey82
212 212 212 gray83
212 212 212 grey83
214 214 214 gray84
214 214 214 grey84
217 217 217 gray85
217 217 217 grey85
219 219 219 gray86
219 219 219 grey86
222 222 222 gray87
222 222 222 grey87
224 224 224 gray88
224 224 224 grey88
227 227 227 gray89
227 227 227 grey89
229 229 229 gray90
229 229 229 grey90
232 232 232 gray91
232 232 232 grey91
235 235 235 gray92
235 235 235 grey92
237 237 237 gray93
237 237 237 grey93
240 240 240 gray94
240 240 240 grey94
242 242 242 gray95
242 242 242 grey95
245 245 245 gray96
245 245 245 grey96
247 247 247 gray97
247 247 247 grey97
250 250 250 gray98
250 250 250 grey98
252 252 252 gray99
252 252 252 grey99
255 255 255 gray100
255 255 255 grey100
169 169 169 dark grey
169 169 169 DarkGrey
169 169 169 dark gray
169 169 169 DarkGray
0 0 139 dark blue
0 0 139 DarkBlue
0 139 139 dark cyan
0 139 139 DarkCyan
139 0 139 dark magenta
139 0 139 DarkMagenta
139 0 0 dark red
139 0 0 DarkRed
144 238 144 light green
144 238 144 LightGreen

View file

@ -0,0 +1,29 @@
X Window System License - X11R6.4
Copyright (c) 1998 The Open Group
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of The Open Group shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from The Open Group.
X Window System is a trademark of The Open Group

View file

@ -0,0 +1 @@
# Dummy file to make this directory a package.

View file

@ -0,0 +1,17 @@
# HTML 4.0 color names
Black #000000
Silver #c0c0c0
Gray #808080
White #ffffff
Maroon #800000
Red #ff0000
Purple #800080
Fuchsia #ff00ff
Green #008000
Lime #00ff00
Olive #808000
Yellow #ffff00
Navy #000080
Blue #0000ff
Teal #008080
Aqua #00ffff

View file

@ -0,0 +1,100 @@
# named colors from http://www.lightlink.com/xine/bells/namedcolors.html
White #FFFFFF
Red #FF0000
Green #00FF00
Blue #0000FF
Magenta #FF00FF
Cyan #00FFFF
Yellow #FFFF00
Black #000000
Aquamarine #70DB93
Baker's Chocolate #5C3317
Blue Violet #9F5F9F
Brass #B5A642
Bright Gold #D9D919
Brown #A62A2A
Bronze #8C7853
Bronze II #A67D3D
Cadet Blue #5F9F9F
Cool Copper #D98719
Copper #B87333
Coral #FF7F00
Corn Flower Blue #42426F
Dark Brown #5C4033
Dark Green #2F4F2F
Dark Green Copper #4A766E
Dark Olive Green #4F4F2F
Dark Orchid #9932CD
Dark Purple #871F78
Dark Slate Blue #6B238E
Dark Slate Grey #2F4F4F
Dark Tan #97694F
Dark Turquoise #7093DB
Dark Wood #855E42
Dim Grey #545454
Dusty Rose #856363
Feldspar #D19275
Firebrick #8E2323
Forest Green #238E23
Gold #CD7F32
Goldenrod #DBDB70
Grey #C0C0C0
Green Copper #527F76
Green Yellow #93DB70
Hunter Green #215E21
Indian Red #4E2F2F
Khaki #9F9F5F
Light Blue #C0D9D9
Light Grey #A8A8A8
Light Steel Blue #8F8FBD
Light Wood #E9C2A6
Lime Green #32CD32
Mandarian Orange #E47833
Maroon #8E236B
Medium Aquamarine #32CD99
Medium Blue #3232CD
Medium Forest Green #6B8E23
Medium Goldenrod #EAEAAE
Medium Orchid #9370DB
Medium Sea Green #426F42
Medium Slate Blue #7F00FF
Medium Spring Green #7FFF00
Medium Turquoise #70DBDB
Medium Violet Red #DB7093
Medium Wood #A68064
Midnight Blue #2F2F4F
Navy Blue #23238E
Neon Blue #4D4DFF
Neon Pink #FF6EC7
New Midnight Blue #00009C
New Tan #EBC79E
Old Gold #CFB53B
Orange #FF7F00
Orange Red #FF2400
Orchid #DB70DB
Pale Green #8FBC8F
Pink #BC8F8F
Plum #EAADEA
Quartz #D9D9F3
Rich Blue #5959AB
Salmon #6F4242
Scarlet #8C1717
Sea Green #238E68
Semi-Sweet Chocolate #6B4226
Sienna #8E6B23
Silver #E6E8FA
Sky Blue #3299CC
Slate Blue #007FFF
Spicy Pink #FF1CAE
Spring Green #00FF7F
Steel Blue #236B8E
Summer Sky #38B0DE
Tan #DB9370
Thistle #D8BFD8
Turquoise #ADEAEA
Very Dark Brown #5C4033
Very Light Grey #CDCDCD
Violet #4F2F4F
Violet Red #CC3299
Wheat #D8D8BF
Yellow Green #99CC32

View file

@ -0,0 +1,125 @@
"""Color chooser implementing (almost) the tkColorColor interface
"""
import os
import Main
import ColorDB
class Chooser:
"""Ask for a color"""
def __init__(self,
master = None,
databasefile = None,
initfile = None,
ignore = None,
wantspec = None):
self.__master = master
self.__databasefile = databasefile
self.__initfile = initfile or os.path.expanduser('~/.pynche')
self.__ignore = ignore
self.__pw = None
self.__wantspec = wantspec
def show(self, color, options):
# scan for options that can override the ctor options
self.__wantspec = options.get('wantspec', self.__wantspec)
dbfile = options.get('databasefile', self.__databasefile)
# load the database file
colordb = None
if dbfile != self.__databasefile:
colordb = ColorDB.get_colordb(dbfile)
if not self.__master:
from tkinter import Tk
self.__master = Tk()
if not self.__pw:
self.__pw, self.__sb = \
Main.build(master = self.__master,
initfile = self.__initfile,
ignore = self.__ignore)
else:
self.__pw.deiconify()
# convert color
if colordb:
self.__sb.set_colordb(colordb)
else:
colordb = self.__sb.colordb()
if color:
r, g, b = Main.initial_color(color, colordb)
self.__sb.update_views(r, g, b)
# reset the canceled flag and run it
self.__sb.canceled(0)
Main.run(self.__pw, self.__sb)
rgbtuple = self.__sb.current_rgb()
self.__pw.withdraw()
# check to see if the cancel button was pushed
if self.__sb.canceled_p():
return None, None
# Try to return the color name from the database if there is an exact
# match, otherwise use the "#rrggbb" spec. BAW: Forget about color
# aliases for now, maybe later we should return these too.
name = None
if not self.__wantspec:
try:
name = colordb.find_byrgb(rgbtuple)[0]
except ColorDB.BadColor:
pass
if name is None:
name = ColorDB.triplet_to_rrggbb(rgbtuple)
return rgbtuple, name
def save(self):
if self.__sb:
self.__sb.save_views()
# convenience stuff
_chooser = None
def askcolor(color = None, **options):
"""Ask for a color"""
global _chooser
if not _chooser:
_chooser = Chooser(**options)
return _chooser.show(color, options)
def save():
global _chooser
if _chooser:
_chooser.save()
# test stuff
if __name__ == '__main__':
from tkinter import *
class Tester:
def __init__(self):
self.__root = tk = Tk()
b = Button(tk, text='Choose Color...', command=self.__choose)
b.pack()
self.__l = Label(tk)
self.__l.pack()
q = Button(tk, text='Quit', command=self.__quit)
q.pack()
def __choose(self, event=None):
rgb, name = askcolor(master=self.__root)
if rgb is None:
text = 'You hit CANCEL!'
else:
r, g, b = rgb
text = 'You picked %s (%3d/%3d/%3d)' % (name, r, g, b)
self.__l.configure(text=text)
def __quit(self, event=None):
self.__root.quit()
def run(self):
self.__root.mainloop()
t = Tester()
t.run()
# simpler
## print 'color:', askcolor()
## print 'color:', askcolor()

7
third_party/python/Tools/pynche/pynche vendored Executable file
View file

@ -0,0 +1,7 @@
#! /usr/bin/env python
"""Run this file under Unix, or when debugging under Windows.
Run the file pynche.pyw under Windows to inhibit the console window.
"""
import Main
Main.main()

7
third_party/python/Tools/pynche/pynche.pyw vendored Executable file
View file

@ -0,0 +1,7 @@
#! /usr/bin/env python
"""Run this file under Windows to inhibit the console window.
Run the file pynche.py under Unix or when debugging under Windows.
"""
import Main
Main.main()

View file

@ -0,0 +1,141 @@
# De-facto NS & MSIE recognized HTML color names
AliceBlue #f0f8ff
AntiqueWhite #faebd7
Aqua #00ffff
Aquamarine #7fffd4
Azure #f0ffff
Beige #f5f5dc
Bisque #ffe4c4
Black #000000
BlanchedAlmond #ffebcd
Blue #0000ff
BlueViolet #8a2be2
Brown #a52a2a
BurlyWood #deb887
CadetBlue #5f9ea0
Chartreuse #7fff00
Chocolate #d2691e
Coral #ff7f50
CornflowerBlue #6495ed
Cornsilk #fff8dc
Crimson #dc143c
Cyan #00ffff
DarkBlue #00008b
DarkCyan #008b8b
DarkGoldenrod #b8860b
DarkGray #a9a9a9
DarkGreen #006400
DarkKhaki #bdb76b
DarkMagenta #8b008b
DarkOliveGreen #556b2f
DarkOrange #ff8c00
DarkOrchid #9932cc
DarkRed #8b0000
DarkSalmon #e9967a
DarkSeaGreen #8fbc8f
DarkSlateBlue #483d8b
DarkSlateGray #2f4f4f
DarkTurquoise #00ced1
DarkViolet #9400d3
DeepPink #ff1493
DeepSkyBlue #00bfff
DimGray #696969
DodgerBlue #1e90ff
FireBrick #b22222
FloralWhite #fffaf0
ForestGreen #228b22
Fuchsia #ff00ff
Gainsboro #dcdcdc
GhostWhite #f8f8ff
Gold #ffd700
Goldenrod #daa520
Gray #808080
Green #008000
GreenYellow #adff2f
Honeydew #f0fff0
HotPink #ff69b4
IndianRed #cd5c5c
Indigo #4b0082
Ivory #fffff0
Khaki #f0e68c
Lavender #e6e6fa
LavenderBlush #fff0f5
LawnGreen #7cfc00
LemonChiffon #fffacd
LightBlue #add8e6
LightCoral #f08080
LightCyan #e0ffff
LightGoldenrodYellow #fafad2
LightGreen #90ee90
LightGrey #d3d3d3
LightPink #ffb6c1
LightSalmon #ffa07a
LightSeaGreen #20b2aa
LightSkyBlue #87cefa
LightSlateGray #778899
LightSteelBlue #b0c4de
LightYellow #ffffe0
Lime #00ff00
LimeGreen #32cd32
Linen #faf0e6
Magenta #ff00ff
Maroon #800000
MediumAquamarine #66cdaa
MediumBlue #0000cd
MediumOrchid #ba55d3
MediumPurple #9370db
MediumSeaGreen #3cb371
MediumSlateBlue #7b68ee
MediumSpringGreen #00fa9a
MediumTurquoise #48d1cc
MediumVioletRed #c71585
MidnightBlue #191970
MintCream #f5fffa
MistyRose #ffe4e1
Moccasin #ffe4b5
NavajoWhite #ffdead
Navy #000080
OldLace #fdf5e6
Olive #808000
OliveDrab #6b8e23
Orange #ffa500
OrangeRed #ff4500
Orchid #da70d6
PaleGoldenrod #eee8aa
PaleGreen #98fb98
PaleTurquoise #afeeee
PaleVioletRed #db7093
PapayaWhip #ffefd5
PeachPuff #ffdab9
Peru #cd853f
Pink #ffc0cb
Plum #dda0dd
PowderBlue #b0e0e6
Purple #800080
Red #ff0000
RosyBrown #bc8f8f
RoyalBlue #4169e1
SaddleBrown #8b4513
Salmon #fa8072
SandyBrown #f4a460
SeaGreen #2e8b57
Seashell #fff5ee
Sienna #a0522d
Silver #c0c0c0
SkyBlue #87ceeb
SlateBlue #6a5acd
SlateGray #708090
Snow #fffafa
SpringGreen #00ff7f
SteelBlue #4682b4
Tan #d2b48c
Teal #008080
Thistle #d8bfd8
Tomato #ff6347
Turquoise #40e0d0
Violet #ee82ee
Wheat #f5deb3
White #ffffff
WhiteSmoke #f5f5f5
Yellow #ffff00
YellowGreen #9acd32

View file

@ -0,0 +1,217 @@
# Websafe RGB values
#000000
#000033
#000066
#000099
#0000cc
#0000ff
#003300
#003333
#003366
#003399
#0033cc
#0033ff
#006600
#006633
#006666
#006699
#0066cc
#0066ff
#009900
#009933
#009966
#009999
#0099cc
#0099ff
#00cc00
#00cc33
#00cc66
#00cc99
#00cccc
#00ccff
#00ff00
#00ff33
#00ff66
#00ff99
#00ffcc
#00ffff
#330000
#330033
#330066
#330099
#3300cc
#3300ff
#333300
#333333
#333366
#333399
#3333cc
#3333ff
#336600
#336633
#336666
#336699
#3366cc
#3366ff
#339900
#339933
#339966
#339999
#3399cc
#3399ff
#33cc00
#33cc33
#33cc66
#33cc99
#33cccc
#33ccff
#33ff00
#33ff33
#33ff66
#33ff99
#33ffcc
#33ffff
#660000
#660033
#660066
#660099
#6600cc
#6600ff
#663300
#663333
#663366
#663399
#6633cc
#6633ff
#666600
#666633
#666666
#666699
#6666cc
#6666ff
#669900
#669933
#669966
#669999
#6699cc
#6699ff
#66cc00
#66cc33
#66cc66
#66cc99
#66cccc
#66ccff
#66ff00
#66ff33
#66ff66
#66ff99
#66ffcc
#66ffff
#990000
#990033
#990066
#990099
#9900cc
#9900ff
#993300
#993333
#993366
#993399
#9933cc
#9933ff
#996600
#996633
#996666
#996699
#9966cc
#9966ff
#999900
#999933
#999966
#999999
#9999cc
#9999ff
#99cc00
#99cc33
#99cc66
#99cc99
#99cccc
#99ccff
#99ff00
#99ff33
#99ff66
#99ff99
#99ffcc
#99ffff
#cc0000
#cc0033
#cc0066
#cc0099
#cc00cc
#cc00ff
#cc3300
#cc3333
#cc3366
#cc3399
#cc33cc
#cc33ff
#cc6600
#cc6633
#cc6666
#cc6699
#cc66cc
#cc66ff
#cc9900
#cc9933
#cc9966
#cc9999
#cc99cc
#cc99ff
#cccc00
#cccc33
#cccc66
#cccc99
#cccccc
#ccccff
#ccff00
#ccff33
#ccff66
#ccff99
#ccffcc
#ccffff
#ff0000
#ff0033
#ff0066
#ff0099
#ff00cc
#ff00ff
#ff3300
#ff3333
#ff3366
#ff3399
#ff33cc
#ff33ff
#ff6600
#ff6633
#ff6666
#ff6699
#ff66cc
#ff66ff
#ff9900
#ff9933
#ff9966
#ff9999
#ff99cc
#ff99ff
#ffcc00
#ffcc33
#ffcc66
#ffcc99
#ffcccc
#ffccff
#ffff00
#ffff33
#ffff66
#ffff99
#ffffcc
#ffffff