]> granicus.if.org Git - python/commitdiff
Lots of changes to support loading alternative color name database.
authorBarry Warsaw <barry@python.org>
Mon, 26 Apr 1999 23:17:16 +0000 (23:17 +0000)
committerBarry Warsaw <barry@python.org>
Mon, 26 Apr 1999 23:17:16 +0000 (23:17 +0000)
You can switch database by just loading the new one; the list window
and nearest colors adapt to the new database.

Some reorganizing of code.  Also, the name of the database file is
stored in the ~/.pynche pickle.  If it can't be loaded, fallbacks are
used.

Tools/pynche/ChipViewer.py
Tools/pynche/ColorDB.py
Tools/pynche/ListViewer.py
Tools/pynche/Main.py
Tools/pynche/PyncheWidget.py
Tools/pynche/Switchboard.py

index 3556f6d8e44211fb0263830a3e66b7220306ab43..33d12dcd4ca677e4fccfb854ab0f0dddc5b72bc3 100644 (file)
@@ -49,9 +49,9 @@ class ChipWidget:
         if releasecmd:
             self.__chip.bind('<ButtonRelease-1>', releasecmd)
 
-    def set_color(self, color):
+    def set_color(self, color, colorname=None):
         self.__chip.config(background=color)
-        self.__name.config(text=color)
+        self.__name.config(text=colorname or color)
 
     def get_color(self):
         return self.__chip['background']
@@ -83,25 +83,27 @@ class ChipViewer:
                                     releasecmd = self.__buttonrelease)
 
     def update_yourself(self, red, green, blue):
-        # TBD: should exactname default to X11 color name if their is an exact
-        # match for the rgb triplet?  Part of me says it's nice to see both
-        # names for the color, the other part says that it's better to
-        # feedback the exact match.
+        # Selected always shows the #rrggbb name of the color, nearest always
+        # shows the name of the nearest color in the database.  TBD: 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)
-        try:
-            allcolors = self.__sb.colordb().find_byrgb(rgbtuple)
-            exactname = allcolors[0]
-        except ColorDB.BadColor:
-            exactname = ColorDB.triplet_to_rrggbb(rgbtuple)
-        nearest = self.__sb.colordb().nearest(red, green, blue)
-        self.__selected.set_color(exactname)
-        self.__nearest.set_color(nearest)
+        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, nearest)
 
     def __buttonpress(self, event=None):
         self.__nearest.press()
 
     def __buttonrelease(self, event=None):
         self.__nearest.release()
-        colorname = self.__nearest.get_color()
-        red, green, blue = self.__sb.colordb().find_byname(colorname)
+        rrggbb = self.__nearest.get_color()
+        red, green, blue = ColorDB.rrggbb_to_triplet(rrggbb)
         self.__sb.update_views(red, green, blue)
index 863688a5c73c38d60e3da4c541e5cbceb3c6c558..87c440608605e1a6090a62cf6d4c78653193c401 100644 (file)
@@ -35,7 +35,9 @@ DEFAULT_DB = None
 \f
 # generic class
 class ColorDB:
-    def __init__(self, fp, lineno):
+    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, 
@@ -54,6 +56,7 @@ class ColorDB:
            if not line:
                break
            # get this compiled regular expression from derived class
+##            print '%3d: %s' % (lineno, line[:-1])
            mo = self._re.match(line)
            if not mo:
                sys.stderr.write('Error in %s, line %d\n' % (fp.name, lineno))
@@ -62,9 +65,10 @@ class ColorDB:
            #
            # extract the red, green, blue, and name
            #
-           red, green, blue = map(int, mo.group('red', 'green', 'blue'))
-           name = mo.group('name')
+            red, green, blue = self._extractrgb(mo)
+            name = self._extractname(mo)
            keyname = string.lower(name)
+##            print keyname, '(%d, %d, %d)' % (red, green, blue)
            #
            # TBD: 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
@@ -81,13 +85,25 @@ class ColorDB:
            self.__byname[keyname] = key
            lineno = lineno + 1
 
+    # override in derived classes
+    def _extractrgb(self, mo):
+        return map(int, 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 = string.lower(name)
        try:
            return self.__byname[name]
@@ -95,9 +111,10 @@ class ColorDB:
            raise BadColor(name)
 
     def nearest(self, red, green, blue):
-       # TBD: use Voronoi diagrams, Delaunay triangulation, or octree for
-       # speeding up the locating of nearest point.  Exhaustive search is
-       # inefficient, but may be fast enough.
+        """Return the name of color nearest (red, green, blue)"""
+       # TBD: 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():
@@ -133,7 +150,29 @@ class ColorDB:
 \f
 class RGBColorDB(ColorDB):
     _re = re.compile(
-       '\s*(?P<red>\d+)\s+(?P<green>\d+)\s+(?P<blue>\d+)\s+(?P<name>.*)')
+        '\s*(?P<red>\d+)\s+(?P<green>\d+)\s+(?P<blue>\d+)\s+(?P<name>.*)')
+
+
+class HTML40DB(ColorDB):
+    _re = re.compile('(?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('(?P<name>(.+))\s+(?P<hexrgb>#[0-9a-fA-F]{6})')
+
+    def _extractname(self, mo):
+        return string.strip(mo.group('name'))
+
+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 string.upper(mo.group('hexrgb'))
 
 
 \f
@@ -141,30 +180,36 @@ class RGBColorDB(ColorDB):
 # expression, SCANLINES is the number of header lines to scan, and CLASS is
 # the class to instantiate if a match is found
 
-X_RGB_TXT = re.compile('XConsortium'), 1, RGBColorDB
+FILETYPES = [
+    (re.compile('XConsortium'), RGBColorDB),
+    (re.compile('HTML'), HTML40DB),
+    (re.compile('lightlink'), LightlinkDB),
+    (re.compile('Websafe'), WebsafeDB),
+    ]
 
-def get_colordb(file, filetype=X_RGB_TXT):
+def get_colordb(file, filetype=None):
     colordb = None
-    fp = None
-    typere, scanlines, class_ = filetype
+    fp = open(file)
     try:
-       try:
-           lineno = 0
-           fp = open(file)
-           while lineno < scanlines:
-               line = fp.readline()
-               if not line:
-                   break
-               mo = typere.search(line)
-               if mo:
-                   colordb = class_(fp, lineno)
-                   break
-               lineno = lineno + 1
-       except IOError:
-           pass
+        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:
-       if fp:
-           fp.close()
+        fp.close()
     # save a global copy
     global DEFAULT_DB
     DEFAULT_DB = colordb
@@ -175,6 +220,7 @@ def get_colordb(file, filetype=X_RGB_TXT):
 _namedict = {}
 def rrggbb_to_triplet(color, atoi=string.atoi):
     """Converts a #rrggbb color to the tuple (red, green, blue)."""
+    global _namedict
     rgbtuple = _namedict.get(color)
     if rgbtuple is None:
         if color[0] <> '#':
@@ -190,6 +236,7 @@ def rrggbb_to_triplet(color, atoi=string.atoi):
 _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
index eb43a928607424c67cdec3180616002febf773dd..424e462ce3898e1c71560ecbad8ea8674a30b7d6 100644 (file)
@@ -45,9 +45,29 @@ class ListViewer:
         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 = switchboard.colordb()
+        colordb = self.__sb.colordb()
+        canvas = self.__canvas
         row = 0
         widest = 0
         bboxes = self.__bboxes = []
@@ -63,7 +83,7 @@ class ListViewer:
             boxid = canvas.create_rectangle(3, row*20+3,
                                             textend+3, row*20 + 23,
                                             outline='',
-                                            tags=(exactcolor,))
+                                            tags=(exactcolor, 'all'))
             canvas.bind('<ButtonRelease>', self.__onrelease)
             bboxes.append(boxid)
             if textend+3 > widest:
@@ -74,22 +94,6 @@ class ListViewer:
         for box in bboxes:
             x1, y1, x2, y2 = canvas.coords(box)
             canvas.coords(box, x1, y1, widest, y2)
-        #
-        # 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 __onrelease(self, event=None):
         canvas = self.__canvas
@@ -164,3 +168,7 @@ class ListViewer:
 
     def save_options(self, optiondb):
         optiondb['UPONCLICK'] = self.__uoc.get()
+
+    def flush(self):
+        self.__canvas.delete('all')
+        self.__populate()
index 1ec738b111f7fe4fbeff03d61963dfba99380886..459aaa570bc3f47b1737049fa26220b55497f38f 100644 (file)
@@ -49,7 +49,7 @@ Where:
 
 """
 
-__version__ = '0.1'
+__version__ = '0.2'
 
 import sys
 import os
@@ -120,19 +120,27 @@ def initial_color(s, colordb):
 
 \f
 def build(master=None, initialcolor=None, initfile=None, ignore=None):
-    # create the windows and go
-    for f in RGB_TXT:
-       try:
-           colordb = ColorDB.get_colordb(f)
-            if colordb:
-                break
-       except IOError:
-           pass
-    else:
-        usage(1, 'No color database file found, see the -d option.')
-
     # create all output widgets
-    s = Switchboard(colordb, not ignore and initfile)
+    s = Switchboard(not ignore and initfile)
+
+    # load the color database
+    colordb = None
+    try:
+        dbfile = s.optiondb()['DBFILE']
+        colordb = ColorDB.get_colordb(dbfile)
+    except (KeyError, IOError):
+        # scoot through the files listed above to try to find a usable color
+        # database file
+        for f in RGB_TXT:
+            try:
+                colordb = ColorDB.get_colordb(f)
+                if colordb:
+                    break
+            except IOError:
+                pass
+    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)
index 5e691ece29a8ee73d11ab56d564b3def60e6952f..810b7ab6651ef4d470a97c78d53c85b1355e8079 100644 (file)
@@ -23,6 +23,7 @@ class PyncheWidget:
         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
@@ -51,8 +52,11 @@ class PyncheWidget:
         #
         # File menu
         #
+        filemenu = self.__filemenu = Menu(menubar, tearoff=0)
+        filemenu.add_command(label='Load palette...',
+                             command=self.__load,
+                             underline=0)
         if not modal:
-            filemenu = self.__filemenu = Menu(menubar, tearoff=0)
             filemenu.add_command(label='Quit',
                                  command=self.__quit,
                                  accelerator='Alt-Q',
@@ -66,7 +70,7 @@ class PyncheWidget:
                              underline=0)
         viewmenu.add_command(label='Color List Window...',
                              command=self.__popup_listwin,
-                             underline=0)
+                             underline=6)
         viewmenu.add_command(label='Details Window...',
                              command=self.__popup_details,
                              underline=0)
@@ -186,6 +190,33 @@ email:   bwarsaw@python.org''' % __version__)
             self.__sb.add_view(self.__detailswin)
         self.__detailswin.deiconify()
 
+    def __load(self, event=None):
+        import FileDialog
+        import ColorDB
+        while 1:
+            d = FileDialog.FileDialog(self.__root)
+            file = d.go(pattern='*.txt', key=self.__dialogstate)
+            if file is None:
+                # cancel button
+                return
+            try:
+                colordb = ColorDB.get_colordb(file)
+            except IOError:
+                tkMessageBox.showerror('Read error', '''\
+Could not open file for reading:
+%s''' % file)
+                continue
+            if colordb is None:
+                tkMessageBox.showerror('Unrecognized color file type', '''\
+Unrecognized color file type in file:
+%s''' % file)
+                continue
+            break
+        self.__sb.set_colordb(colordb)
+        if self.__listwin:
+            self.__listwin.flush()
+        self.__sb.update_views_current()
+
     def withdraw(self):
         self.__root.withdraw()
 
index 3b06f1118de063699f76e98c99d1dd00804c0ef5..9f2c64c2706fcae4b2dec671f560b5243bbd7b42 100644 (file)
@@ -17,9 +17,9 @@ from types import DictType
 import marshal
 
 class Switchboard:
-    def __init__(self, colordb, initfile):
+    def __init__(self, initfile):
         self.__initfile = initfile
-        self.__colordb = colordb
+        self.__colordb = None
         self.__optiondb = {}
         self.__views = []
         self.__red = 0
@@ -63,6 +63,9 @@ class Switchboard:
     def colordb(self):
         return self.__colordb
 
+    def set_colordb(self, colordb):
+        self.__colordb = colordb
+
     def optiondb(self):
         return self.__optiondb
 
@@ -74,6 +77,9 @@ class Switchboard:
         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: