Home | History | Annotate | Download | only in pynche
      1 """DetailsViewer class.
      2 
      3 This class implements a pure input window which allows you to meticulously
      4 edit the current color.  You have both mouse control of the color (via the
      5 buttons along the bottom row), and there are keyboard bindings for each of the
      6 increment/decrement buttons.
      7 
      8 The top three check buttons allow you to specify which of the three color
      9 variations are tied together when incrementing and decrementing.  Red, green,
     10 and blue are self evident.  By tying together red and green, you can modify
     11 the yellow level of the color.  By tying together red and blue, you can modify
     12 the magenta level of the color.  By tying together green and blue, you can
     13 modify the cyan level, and by tying all three together, you can modify the
     14 grey level.
     15 
     16 The behavior at the boundaries (0 and 255) are defined by the `At boundary'
     17 option menu:
     18 
     19     Stop
     20         When the increment or decrement would send any of the tied variations
     21         out of bounds, the entire delta is discarded.
     22 
     23     Wrap Around
     24         When the increment or decrement would send any of the tied variations
     25         out of bounds, the out of bounds variation is wrapped around to the
     26         other side.  Thus if red were at 238 and 25 were added to it, red
     27         would have the value 7.
     28 
     29     Preserve Distance
     30         When the increment or decrement would send any of the tied variations
     31         out of bounds, all tied variations are wrapped as one, so as to
     32         preserve the distance between them.  Thus if green and blue were tied,
     33         and green was at 238 while blue was at 223, and an increment of 25
     34         were applied, green would be at 15 and blue would be at 0.
     35 
     36     Squash
     37         When the increment or decrement would send any of the tied variations
     38         out of bounds, the out of bounds variation is set to the ceiling of
     39         255 or floor of 0, as appropriate.  In this way, all tied variations
     40         are squashed to one edge or the other.
     41 
     42 The following key bindings can be used as accelerators.  Note that Pynche can
     43 fall behind if you hold the key down as a key repeat:
     44 
     45 Left arrow == -1
     46 Right arrow == +1
     47 
     48 Control + Left == -10
     49 Control + Right == 10
     50 
     51 Shift + Left == -25
     52 Shift + Right == +25
     53 """
     54 
     55 from Tkinter import *
     56 
     57 STOP = 'Stop'
     58 WRAP = 'Wrap Around'
     59 RATIO = 'Preserve Distance'
     60 GRAV = 'Squash'
     61 
     62 ADDTOVIEW = 'Details Window...'
     63 
     64 
     66 class DetailsViewer:
     67     def __init__(self, switchboard, master=None):
     68         self.__sb = switchboard
     69         optiondb = switchboard.optiondb()
     70         self.__red, self.__green, self.__blue = switchboard.current_rgb()
     71         # GUI
     72         root = self.__root = Toplevel(master, class_='Pynche')
     73         root.protocol('WM_DELETE_WINDOW', self.withdraw)
     74         root.title('Pynche Details Window')
     75         root.iconname('Pynche Details Window')
     76         root.bind('<Alt-q>', self.__quit)
     77         root.bind('<Alt-Q>', self.__quit)
     78         root.bind('<Alt-w>', self.withdraw)
     79         root.bind('<Alt-W>', self.withdraw)
     80         # accelerators
     81         root.bind('<KeyPress-Left>', self.__minus1)
     82         root.bind('<KeyPress-Right>', self.__plus1)
     83         root.bind('<Control-KeyPress-Left>', self.__minus10)
     84         root.bind('<Control-KeyPress-Right>', self.__plus10)
     85         root.bind('<Shift-KeyPress-Left>', self.__minus25)
     86         root.bind('<Shift-KeyPress-Right>', self.__plus25)
     87         #
     88         # color ties
     89         frame = self.__frame = Frame(root)
     90         frame.pack(expand=YES, fill=X)
     91         self.__l1 = Label(frame, text='Move Sliders:')
     92         self.__l1.grid(row=1, column=0, sticky=E)
     93         self.__rvar = IntVar()
     94         self.__rvar.set(optiondb.get('RSLIDER', 4))
     95         self.__radio1 = Checkbutton(frame, text='Red',
     96                                     variable=self.__rvar,
     97                                     command=self.__effect,
     98                                     onvalue=4, offvalue=0)
     99         self.__radio1.grid(row=1, column=1, sticky=W)
    100         self.__gvar = IntVar()
    101         self.__gvar.set(optiondb.get('GSLIDER', 2))
    102         self.__radio2 = Checkbutton(frame, text='Green',
    103                                     variable=self.__gvar,
    104                                     command=self.__effect,
    105                                     onvalue=2, offvalue=0)
    106         self.__radio2.grid(row=2, column=1, sticky=W)
    107         self.__bvar = IntVar()
    108         self.__bvar.set(optiondb.get('BSLIDER', 1))
    109         self.__radio3 = Checkbutton(frame, text='Blue',
    110                                     variable=self.__bvar,
    111                                     command=self.__effect,
    112                                     onvalue=1, offvalue=0)
    113         self.__radio3.grid(row=3, column=1, sticky=W)
    114         self.__l2 = Label(frame)
    115         self.__l2.grid(row=4, column=1, sticky=W)
    116         self.__effect()
    117         #
    118         # Boundary behavior
    119         self.__l3 = Label(frame, text='At boundary:')
    120         self.__l3.grid(row=5, column=0, sticky=E)
    121         self.__boundvar = StringVar()
    122         self.__boundvar.set(optiondb.get('ATBOUND', STOP))
    123         self.__omenu = OptionMenu(frame, self.__boundvar,
    124                                   STOP, WRAP, RATIO, GRAV)
    125         self.__omenu.grid(row=5, column=1, sticky=W)
    126         self.__omenu.configure(width=17)
    127         #
    128         # Buttons
    129         frame = self.__btnframe = Frame(frame)
    130         frame.grid(row=0, column=0, columnspan=2, sticky='EW')
    131         self.__down25 = Button(frame, text='-25',
    132                                command=self.__minus25)
    133         self.__down10 = Button(frame, text='-10',
    134                                command=self.__minus10)
    135         self.__down1 = Button(frame, text='-1',
    136                               command=self.__minus1)
    137         self.__up1 = Button(frame, text='+1',
    138                             command=self.__plus1)
    139         self.__up10 = Button(frame, text='+10',
    140                              command=self.__plus10)
    141         self.__up25 = Button(frame, text='+25',
    142                              command=self.__plus25)
    143         self.__down25.pack(expand=YES, fill=X, side=LEFT)
    144         self.__down10.pack(expand=YES, fill=X, side=LEFT)
    145         self.__down1.pack(expand=YES, fill=X, side=LEFT)
    146         self.__up1.pack(expand=YES, fill=X, side=LEFT)
    147         self.__up10.pack(expand=YES, fill=X, side=LEFT)
    148         self.__up25.pack(expand=YES, fill=X, side=LEFT)
    149 
    150     def __effect(self, event=None):
    151         tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
    152         if tie in (0, 1, 2, 4):
    153             text = ''
    154         else:
    155             text = '(= %s Level)' % {3: 'Cyan',
    156                                      5: 'Magenta',
    157                                      6: 'Yellow',
    158                                      7: 'Grey'}[tie]
    159         self.__l2.configure(text=text)
    160 
    161     def __quit(self, event=None):
    162         self.__root.quit()
    163 
    164     def withdraw(self, event=None):
    165         self.__root.withdraw()
    166 
    167     def deiconify(self, event=None):
    168         self.__root.deiconify()
    169 
    170     def __minus25(self, event=None):
    171         self.__delta(-25)
    172 
    173     def __minus10(self, event=None):
    174         self.__delta(-10)
    175 
    176     def __minus1(self, event=None):
    177         self.__delta(-1)
    178 
    179     def __plus1(self, event=None):
    180         self.__delta(1)
    181 
    182     def __plus10(self, event=None):
    183         self.__delta(10)
    184 
    185     def __plus25(self, event=None):
    186         self.__delta(25)
    187 
    188     def __delta(self, delta):
    189         tie = []
    190         if self.__rvar.get():
    191             red = self.__red + delta
    192             tie.append(red)
    193         else:
    194             red = self.__red
    195         if self.__gvar.get():
    196             green = self.__green + delta
    197             tie.append(green)
    198         else:
    199             green = self.__green
    200         if self.__bvar.get():
    201             blue = self.__blue + delta
    202             tie.append(blue)
    203         else:
    204             blue = self.__blue
    205         # now apply at boundary behavior
    206         atbound = self.__boundvar.get()
    207         if atbound == STOP:
    208             if red < 0 or green < 0 or blue < 0 or \
    209                red > 255 or green > 255 or blue > 255:
    210                 # then
    211                 red, green, blue = self.__red, self.__green, self.__blue
    212         elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
    213             if red < 0:
    214                 red += 256
    215             if green < 0:
    216                 green += 256
    217             if blue < 0:
    218                 blue += 256
    219             if red > 255:
    220                 red -= 256
    221             if green > 255:
    222                 green -= 256
    223             if blue > 255:
    224                 blue -= 256
    225         elif atbound == RATIO:
    226             # for when 2 or 3 colors are tied together
    227             dir = 0
    228             for c in tie:
    229                 if c < 0:
    230                     dir = -1
    231                 elif c > 255:
    232                     dir = 1
    233             if dir == -1:
    234                 delta = max(tie)
    235                 if self.__rvar.get():
    236                     red = red + 255 - delta
    237                 if self.__gvar.get():
    238                     green = green + 255 - delta
    239                 if self.__bvar.get():
    240                     blue = blue + 255 - delta
    241             elif dir == 1:
    242                 delta = min(tie)
    243                 if self.__rvar.get():
    244                     red = red - delta
    245                 if self.__gvar.get():
    246                     green = green - delta
    247                 if self.__bvar.get():
    248                     blue = blue - delta
    249         elif atbound == GRAV:
    250             if red < 0:
    251                 red = 0
    252             if green < 0:
    253                 green = 0
    254             if blue < 0:
    255                 blue = 0
    256             if red > 255:
    257                 red = 255
    258             if green > 255:
    259                 green = 255
    260             if blue > 255:
    261                 blue = 255
    262         self.__sb.update_views(red, green, blue)
    263         self.__root.update_idletasks()
    264 
    265     def update_yourself(self, red, green, blue):
    266         self.__red = red
    267         self.__green = green
    268         self.__blue = blue
    269 
    270     def save_options(self, optiondb):
    271         optiondb['RSLIDER'] = self.__rvar.get()
    272         optiondb['GSLIDER'] = self.__gvar.get()
    273         optiondb['BSLIDER'] = self.__bvar.get()
    274         optiondb['ATBOUND'] = self.__boundvar.get()
    275