Home | History | Annotate | Download | only in pynche
      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 
     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 
     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 
     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 
    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 
    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 as 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 
    234 if __name__ == '__main__':
    235     main()
    236