1 | """Pynche -- The PYthon Natural Color and Hue Editor.
|
---|
2 |
|
---|
3 | Contact: %(AUTHNAME)s
|
---|
4 | Email: %(AUTHEMAIL)s
|
---|
5 | Version: %(__version__)s
|
---|
6 |
|
---|
7 | Pynche is based largely on a similar color editor I wrote years ago for the
|
---|
8 | SunView window system. That editor was called ICE: the Interactive Color
|
---|
9 | Editor. I'd always wanted to port the editor to X but didn't feel like
|
---|
10 | hacking X and C code to do it. Fast forward many years, to where Python +
|
---|
11 | Tkinter provides such a nice programming environment, with enough power, that
|
---|
12 | I finally buckled down and implemented it. I changed the name because these
|
---|
13 | days, too many other systems have the acronym `ICE'.
|
---|
14 |
|
---|
15 | This program currently requires Python 2.2 with Tkinter.
|
---|
16 |
|
---|
17 | Usage: %(PROGRAM)s [-d file] [-i file] [-X] [-v] [-h] [initialcolor]
|
---|
18 |
|
---|
19 | Where:
|
---|
20 | --database file
|
---|
21 | -d file
|
---|
22 | Alternate location of a color database file
|
---|
23 |
|
---|
24 | --initfile file
|
---|
25 | -i file
|
---|
26 | Alternate location of the initialization file. This file contains a
|
---|
27 | persistent database of the current Pynche options and color. This
|
---|
28 | means that Pynche restores its option settings and current color when
|
---|
29 | it restarts, using this file (unless the -X option is used). The
|
---|
30 | default is ~/.pynche
|
---|
31 |
|
---|
32 | --ignore
|
---|
33 | -X
|
---|
34 | Ignore the initialization file when starting up. Pynche will still
|
---|
35 | write the current option settings to this file when it quits.
|
---|
36 |
|
---|
37 | --version
|
---|
38 | -v
|
---|
39 | print the version number and exit
|
---|
40 |
|
---|
41 | --help
|
---|
42 | -h
|
---|
43 | print this message
|
---|
44 |
|
---|
45 | initialcolor
|
---|
46 | initial color, as a color name or #RRGGBB format
|
---|
47 | """
|
---|
48 |
|
---|
49 | __version__ = '1.4.1'
|
---|
50 |
|
---|
51 | import sys
|
---|
52 | import os
|
---|
53 | import getopt
|
---|
54 | import ColorDB
|
---|
55 |
|
---|
56 | from PyncheWidget import PyncheWidget
|
---|
57 | from Switchboard import Switchboard
|
---|
58 | from StripViewer import StripViewer
|
---|
59 | from ChipViewer import ChipViewer
|
---|
60 | from TypeinViewer import TypeinViewer
|
---|
61 |
|
---|
62 |
|
---|
63 | |
---|
64 |
|
---|
65 | PROGRAM = sys.argv[0]
|
---|
66 | AUTHNAME = 'Barry Warsaw'
|
---|
67 | AUTHEMAIL = 'barry@python.org'
|
---|
68 |
|
---|
69 | # Default locations of rgb.txt or other textual color database
|
---|
70 | RGB_TXT = [
|
---|
71 | # Solaris OpenWindows
|
---|
72 | '/usr/openwin/lib/rgb.txt',
|
---|
73 | # Linux
|
---|
74 | '/usr/lib/X11/rgb.txt',
|
---|
75 | # The X11R6.4 rgb.txt file
|
---|
76 | os.path.join(sys.path[0], 'X/rgb.txt'),
|
---|
77 | # add more here
|
---|
78 | ]
|
---|
79 |
|
---|
80 |
|
---|
81 | |
---|
82 |
|
---|
83 | # Do this because PyncheWidget.py wants to get at the interpolated docstring
|
---|
84 | # too, for its Help menu.
|
---|
85 | def docstring():
|
---|
86 | return __doc__ % globals()
|
---|
87 |
|
---|
88 |
|
---|
89 | def usage(code, msg=''):
|
---|
90 | print docstring()
|
---|
91 | if msg:
|
---|
92 | print msg
|
---|
93 | sys.exit(code)
|
---|
94 |
|
---|
95 |
|
---|
96 | |
---|
97 |
|
---|
98 | def initial_color(s, colordb):
|
---|
99 | # function called on every color
|
---|
100 | def scan_color(s, colordb=colordb):
|
---|
101 | try:
|
---|
102 | r, g, b = colordb.find_byname(s)
|
---|
103 | except ColorDB.BadColor:
|
---|
104 | try:
|
---|
105 | r, g, b = ColorDB.rrggbb_to_triplet(s)
|
---|
106 | except ColorDB.BadColor:
|
---|
107 | return None, None, None
|
---|
108 | return r, g, b
|
---|
109 | #
|
---|
110 | # First try the passed in color
|
---|
111 | r, g, b = scan_color(s)
|
---|
112 | if r is None:
|
---|
113 | # try the same color with '#' prepended, since some shells require
|
---|
114 | # this to be escaped, which is a pain
|
---|
115 | r, g, b = scan_color('#' + s)
|
---|
116 | if r is None:
|
---|
117 | print 'Bad initial color, using gray50:', s
|
---|
118 | r, g, b = scan_color('gray50')
|
---|
119 | if r is None:
|
---|
120 | usage(1, 'Cannot find an initial color to use')
|
---|
121 | # does not return
|
---|
122 | return r, g, b
|
---|
123 |
|
---|
124 |
|
---|
125 | |
---|
126 |
|
---|
127 | def build(master=None, initialcolor=None, initfile=None, ignore=None,
|
---|
128 | dbfile=None):
|
---|
129 | # create all output widgets
|
---|
130 | s = Switchboard(not ignore and initfile)
|
---|
131 | # defer to the command line chosen color database, falling back to the one
|
---|
132 | # in the .pynche file.
|
---|
133 | if dbfile is None:
|
---|
134 | dbfile = s.optiondb().get('DBFILE')
|
---|
135 | # find a parseable color database
|
---|
136 | colordb = None
|
---|
137 | files = RGB_TXT[:]
|
---|
138 | if dbfile is None:
|
---|
139 | dbfile = files.pop()
|
---|
140 | while colordb is None:
|
---|
141 | try:
|
---|
142 | colordb = ColorDB.get_colordb(dbfile)
|
---|
143 | except (KeyError, IOError):
|
---|
144 | pass
|
---|
145 | if colordb is None:
|
---|
146 | if not files:
|
---|
147 | break
|
---|
148 | dbfile = files.pop(0)
|
---|
149 | if not colordb:
|
---|
150 | usage(1, 'No color database file found, see the -d option.')
|
---|
151 | s.set_colordb(colordb)
|
---|
152 |
|
---|
153 | # create the application window decorations
|
---|
154 | app = PyncheWidget(__version__, s, master=master)
|
---|
155 | w = app.window()
|
---|
156 |
|
---|
157 | # these built-in viewers live inside the main Pynche window
|
---|
158 | s.add_view(StripViewer(s, w))
|
---|
159 | s.add_view(ChipViewer(s, w))
|
---|
160 | s.add_view(TypeinViewer(s, w))
|
---|
161 |
|
---|
162 | # get the initial color as components and set the color on all views. if
|
---|
163 | # there was no initial color given on the command line, use the one that's
|
---|
164 | # stored in the option database
|
---|
165 | if initialcolor is None:
|
---|
166 | optiondb = s.optiondb()
|
---|
167 | red = optiondb.get('RED')
|
---|
168 | green = optiondb.get('GREEN')
|
---|
169 | blue = optiondb.get('BLUE')
|
---|
170 | # but if there wasn't any stored in the database, use grey50
|
---|
171 | if red is None or blue is None or green is None:
|
---|
172 | red, green, blue = initial_color('grey50', colordb)
|
---|
173 | else:
|
---|
174 | red, green, blue = initial_color(initialcolor, colordb)
|
---|
175 | s.update_views(red, green, blue)
|
---|
176 | return app, s
|
---|
177 |
|
---|
178 |
|
---|
179 | def run(app, s):
|
---|
180 | try:
|
---|
181 | app.start()
|
---|
182 | except KeyboardInterrupt:
|
---|
183 | pass
|
---|
184 |
|
---|
185 |
|
---|
186 | |
---|
187 |
|
---|
188 | def main():
|
---|
189 | try:
|
---|
190 | opts, args = getopt.getopt(
|
---|
191 | sys.argv[1:],
|
---|
192 | 'hd:i:Xv',
|
---|
193 | ['database=', 'initfile=', 'ignore', 'help', 'version'])
|
---|
194 | except getopt.error, msg:
|
---|
195 | usage(1, msg)
|
---|
196 |
|
---|
197 | if len(args) == 0:
|
---|
198 | initialcolor = None
|
---|
199 | elif len(args) == 1:
|
---|
200 | initialcolor = args[0]
|
---|
201 | else:
|
---|
202 | usage(1)
|
---|
203 |
|
---|
204 | ignore = False
|
---|
205 | dbfile = None
|
---|
206 | initfile = os.path.expanduser('~/.pynche')
|
---|
207 | for opt, arg in opts:
|
---|
208 | if opt in ('-h', '--help'):
|
---|
209 | usage(0)
|
---|
210 | elif opt in ('-v', '--version'):
|
---|
211 | print """\
|
---|
212 | Pynche -- The PYthon Natural Color and Hue Editor.
|
---|
213 | Contact: %(AUTHNAME)s
|
---|
214 | Email: %(AUTHEMAIL)s
|
---|
215 | Version: %(__version__)s""" % globals()
|
---|
216 | sys.exit(0)
|
---|
217 | elif opt in ('-d', '--database'):
|
---|
218 | dbfile = arg
|
---|
219 | elif opt in ('-X', '--ignore'):
|
---|
220 | ignore = True
|
---|
221 | elif opt in ('-i', '--initfile'):
|
---|
222 | initfile = arg
|
---|
223 |
|
---|
224 | app, sb = build(initialcolor=initialcolor,
|
---|
225 | initfile=initfile,
|
---|
226 | ignore=ignore,
|
---|
227 | dbfile=dbfile)
|
---|
228 | run(app, sb)
|
---|
229 | sb.save_views()
|
---|
230 |
|
---|
231 |
|
---|
232 | |
---|
233 |
|
---|
234 | if __name__ == '__main__':
|
---|
235 | main()
|
---|