mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			433 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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()
 |