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