blob: 35e4853a29fad31fdf8d85f4e4680ac08b3c5952 [file] [log] [blame]
Barry Warsawc078b031998-10-05 21:29:04 +00001"""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 Preseve 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
Barry Warsawc078b031998-10-05 21:29:04 +000055from Tkinter import *
Barry Warsawc078b031998-10-05 21:29:04 +000056
57STOP = 'Stop'
58WRAP = 'Wrap Around'
59RATIO = 'Preserve Distance'
60GRAV = 'Squash'
61
62
63class DetailsViewer:
64 def __init__(self, switchboard, parent=None):
65 self.__sb = switchboard
66 self.__red, self.__green, self.__blue = switchboard.current_rgb()
67 # GUI
68 root = self.__root = Toplevel(parent, class_='Pynche')
69 root.protocol('WM_DELETE_WINDOW', self.__withdraw)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000070 root.title('Pynche Details Window')
Barry Warsawc078b031998-10-05 21:29:04 +000071 root.iconname('Pynche Details Window')
72 root.bind('<Alt-q>', self.__quit)
73 root.bind('<Alt-Q>', self.__quit)
74 root.bind('<Alt-w>', self.__withdraw)
75 root.bind('<Alt-W>', self.__withdraw)
76 # accelerators
77 root.bind('<KeyPress-Left>', self.__minus1)
78 root.bind('<KeyPress-Right>', self.__plus1)
79 root.bind('<Control-KeyPress-Left>', self.__minus10)
80 root.bind('<Control-KeyPress-Right>', self.__plus10)
81 root.bind('<Shift-KeyPress-Left>', self.__minus25)
82 root.bind('<Shift-KeyPress-Right>', self.__plus25)
83 #
84 # color ties
85 frame = self.__frame = Frame(root)
86 frame.pack(expand=YES, fill=X)
Barry Warsaw332aa4c1998-10-06 18:29:22 +000087 self.__l1 = Label(frame, text='Move Sliders:')
Barry Warsawc9580431998-10-07 03:19:33 +000088 self.__l1.grid(row=1, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +000089 self.__rvar = IntVar()
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000090 self.__rvar.set(4)
Barry Warsawc078b031998-10-05 21:29:04 +000091 self.__radio1 = Checkbutton(frame, text='Red',
92 variable=self.__rvar,
93 command=self.__effect,
94 onvalue=4, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +000095 self.__radio1.grid(row=1, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +000096 self.__gvar = IntVar()
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000097 self.__gvar.set(2)
Barry Warsawc078b031998-10-05 21:29:04 +000098 self.__radio2 = Checkbutton(frame, text='Green',
99 variable=self.__gvar,
100 command=self.__effect,
101 onvalue=2, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000102 self.__radio2.grid(row=2, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000103 self.__bvar = IntVar()
104 self.__bvar.set(1)
105 self.__radio3 = Checkbutton(frame, text='Blue',
106 variable=self.__bvar,
107 command=self.__effect,
108 onvalue=1, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000109 self.__radio3.grid(row=3, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000110 self.__l2 = Label(frame)
Barry Warsawc9580431998-10-07 03:19:33 +0000111 self.__l2.grid(row=4, column=1, sticky=W)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +0000112 self.__effect()
Barry Warsawc078b031998-10-05 21:29:04 +0000113 #
114 # Boundary behavior
115 self.__l3 = Label(frame, text='At boundary:')
Barry Warsawc9580431998-10-07 03:19:33 +0000116 self.__l3.grid(row=5, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +0000117 self.__boundvar = StringVar()
118 self.__boundvar.set(STOP)
119 self.__omenu = OptionMenu(frame, self.__boundvar,
120 STOP, WRAP, RATIO, GRAV)
Barry Warsawc9580431998-10-07 03:19:33 +0000121 self.__omenu.grid(row=5, column=1, sticky=W)
122 self.__omenu.configure(width=17)
Barry Warsawc078b031998-10-05 21:29:04 +0000123 #
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000124 # Buttons
Barry Warsawc9580431998-10-07 03:19:33 +0000125 frame = self.__btnframe = Frame(frame)
126 frame.grid(row=0, column=0, columnspan=2, sticky='EW')
Barry Warsawc078b031998-10-05 21:29:04 +0000127 self.__down25 = Button(frame, text='-25',
128 command=self.__minus25)
129 self.__down10 = Button(frame, text='-10',
130 command=self.__minus10)
131 self.__down1 = Button(frame, text='-1',
132 command=self.__minus1)
133 self.__up1 = Button(frame, text='+1',
134 command=self.__plus1)
135 self.__up10 = Button(frame, text='+10',
136 command=self.__plus10)
137 self.__up25 = Button(frame, text='+25',
138 command=self.__plus25)
Barry Warsawc9580431998-10-07 03:19:33 +0000139 self.__down25.pack(expand=YES, fill=X, side=LEFT)
140 self.__down10.pack(expand=YES, fill=X, side=LEFT)
141 self.__down1.pack(expand=YES, fill=X, side=LEFT)
142 self.__up1.pack(expand=YES, fill=X, side=LEFT)
143 self.__up10.pack(expand=YES, fill=X, side=LEFT)
144 self.__up25.pack(expand=YES, fill=X, side=LEFT)
Barry Warsawc078b031998-10-05 21:29:04 +0000145
146 def __effect(self, event=None):
147 tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
148 if tie in (0, 1, 2, 4):
149 text = ''
150 else:
Barry Warsawae4ad6e1998-10-06 15:41:31 +0000151 text = '(= %s Level)' % {3: 'Cyan',
152 5: 'Magenta',
153 6: 'Yellow',
154 7: 'Grey'}[tie]
Barry Warsawc078b031998-10-05 21:29:04 +0000155 self.__l2.configure(text=text)
156
157 def __quit(self, event=None):
Barry Warsaw45c8d341998-10-06 19:48:35 +0000158 self.__root.quit()
Barry Warsawc078b031998-10-05 21:29:04 +0000159
160 def __withdraw(self, event=None):
161 self.__root.withdraw()
162
163 def deiconify(self, event=None):
164 self.__root.deiconify()
165
166 def __minus25(self, event=None):
167 self.__delta(-25)
168
169 def __minus10(self, event=None):
170 self.__delta(-10)
171
172 def __minus1(self, event=None):
173 self.__delta(-1)
174
175 def __plus1(self, event=None):
176 self.__delta(1)
177
178 def __plus10(self, event=None):
179 self.__delta(10)
180
181 def __plus25(self, event=None):
182 self.__delta(25)
183
184 def __delta(self, delta):
185 tie = []
186 if self.__rvar.get():
187 red = self.__red + delta
188 tie.append(red)
189 else:
190 red = self.__red
191 if self.__gvar.get():
192 green = self.__green + delta
193 tie.append(green)
194 else:
195 green = self.__green
196 if self.__bvar.get():
197 blue = self.__blue + delta
198 tie.append(blue)
199 else:
200 blue = self.__blue
201 # now apply at boundary behavior
202 atbound = self.__boundvar.get()
203 if atbound == STOP:
204 if red < 0 or green < 0 or blue < 0 or \
205 red > 255 or green > 255 or blue > 255:
206 # then
207 red, green, blue = self.__red, self.__green, self.__blue
208 elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
209 if red < 0:
210 red = red + 256
211 if green < 0:
212 green = green + 256
213 if blue < 0:
214 blue = blue + 256
215 if red > 255:
216 red = red - 256
217 if green > 255:
218 green = green - 256
219 if blue > 255:
220 blue = blue - 256
221 elif atbound == RATIO:
222 # for when 2 or 3 colors are tied together
223 dir = 0
224 for c in tie:
225 if c < 0:
226 dir = -1
227 elif c > 255:
228 dir = 1
229 if dir == -1:
230 delta = max(tie)
231 if self.__rvar.get():
232 red = red + 255 - delta
233 if self.__gvar.get():
234 green = green + 255 - delta
235 if self.__bvar.get():
236 blue = blue + 255 - delta
237 elif dir == 1:
238 delta = min(tie)
239 if self.__rvar.get():
240 red = red - delta
241 if self.__gvar.get():
242 green = green - delta
243 if self.__bvar.get():
244 blue = blue - delta
245 elif atbound == GRAV:
246 if red < 0:
247 red = 0
248 if green < 0:
249 green = 0
250 if blue < 0:
251 blue = 0
252 if red > 255:
253 red = 255
254 if green > 255:
255 green = 255
256 if blue > 255:
257 blue = 255
258 self.__sb.update_views(red, green, blue)
259 self.__root.update_idletasks()
260
261 def update_yourself(self, red, green, blue):
262 self.__red = red
263 self.__green = green
264 self.__blue = blue