blob: da9bbf9616db854b1b627a8b4c935d3f7c89524d [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
Barry Warsaw8a09e1c1998-10-20 20:45:46 +000066 optiondb = switchboard.optiondb()
Barry Warsawc078b031998-10-05 21:29:04 +000067 self.__red, self.__green, self.__blue = switchboard.current_rgb()
68 # GUI
69 root = self.__root = Toplevel(parent, class_='Pynche')
70 root.protocol('WM_DELETE_WINDOW', self.__withdraw)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000071 root.title('Pynche Details Window')
Barry Warsawc078b031998-10-05 21:29:04 +000072 root.iconname('Pynche Details Window')
73 root.bind('<Alt-q>', self.__quit)
74 root.bind('<Alt-Q>', self.__quit)
75 root.bind('<Alt-w>', self.__withdraw)
76 root.bind('<Alt-W>', self.__withdraw)
77 # accelerators
78 root.bind('<KeyPress-Left>', self.__minus1)
79 root.bind('<KeyPress-Right>', self.__plus1)
80 root.bind('<Control-KeyPress-Left>', self.__minus10)
81 root.bind('<Control-KeyPress-Right>', self.__plus10)
82 root.bind('<Shift-KeyPress-Left>', self.__minus25)
83 root.bind('<Shift-KeyPress-Right>', self.__plus25)
84 #
85 # color ties
86 frame = self.__frame = Frame(root)
87 frame.pack(expand=YES, fill=X)
Barry Warsaw332aa4c1998-10-06 18:29:22 +000088 self.__l1 = Label(frame, text='Move Sliders:')
Barry Warsawc9580431998-10-07 03:19:33 +000089 self.__l1.grid(row=1, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +000090 self.__rvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +000091 self.__rvar.set(optiondb.get('RSLIDER', 4))
Barry Warsawc078b031998-10-05 21:29:04 +000092 self.__radio1 = Checkbutton(frame, text='Red',
93 variable=self.__rvar,
94 command=self.__effect,
95 onvalue=4, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +000096 self.__radio1.grid(row=1, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +000097 self.__gvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +000098 self.__gvar.set(optiondb.get('GSLIDER', 2))
Barry Warsawc078b031998-10-05 21:29:04 +000099 self.__radio2 = Checkbutton(frame, text='Green',
100 variable=self.__gvar,
101 command=self.__effect,
102 onvalue=2, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000103 self.__radio2.grid(row=2, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000104 self.__bvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000105 self.__bvar.set(optiondb.get('BSLIDER', 1))
Barry Warsawc078b031998-10-05 21:29:04 +0000106 self.__radio3 = Checkbutton(frame, text='Blue',
107 variable=self.__bvar,
108 command=self.__effect,
109 onvalue=1, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000110 self.__radio3.grid(row=3, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000111 self.__l2 = Label(frame)
Barry Warsawc9580431998-10-07 03:19:33 +0000112 self.__l2.grid(row=4, column=1, sticky=W)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +0000113 self.__effect()
Barry Warsawc078b031998-10-05 21:29:04 +0000114 #
115 # Boundary behavior
116 self.__l3 = Label(frame, text='At boundary:')
Barry Warsawc9580431998-10-07 03:19:33 +0000117 self.__l3.grid(row=5, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +0000118 self.__boundvar = StringVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000119 self.__boundvar.set(optiondb.get('ATBOUND', STOP))
Barry Warsawc078b031998-10-05 21:29:04 +0000120 self.__omenu = OptionMenu(frame, self.__boundvar,
121 STOP, WRAP, RATIO, GRAV)
Barry Warsawc9580431998-10-07 03:19:33 +0000122 self.__omenu.grid(row=5, column=1, sticky=W)
123 self.__omenu.configure(width=17)
Barry Warsawc078b031998-10-05 21:29:04 +0000124 #
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000125 # Buttons
Barry Warsawc9580431998-10-07 03:19:33 +0000126 frame = self.__btnframe = Frame(frame)
127 frame.grid(row=0, column=0, columnspan=2, sticky='EW')
Barry Warsawc078b031998-10-05 21:29:04 +0000128 self.__down25 = Button(frame, text='-25',
129 command=self.__minus25)
130 self.__down10 = Button(frame, text='-10',
131 command=self.__minus10)
132 self.__down1 = Button(frame, text='-1',
133 command=self.__minus1)
134 self.__up1 = Button(frame, text='+1',
135 command=self.__plus1)
136 self.__up10 = Button(frame, text='+10',
137 command=self.__plus10)
138 self.__up25 = Button(frame, text='+25',
139 command=self.__plus25)
Barry Warsawc9580431998-10-07 03:19:33 +0000140 self.__down25.pack(expand=YES, fill=X, side=LEFT)
141 self.__down10.pack(expand=YES, fill=X, side=LEFT)
142 self.__down1.pack(expand=YES, fill=X, side=LEFT)
143 self.__up1.pack(expand=YES, fill=X, side=LEFT)
144 self.__up10.pack(expand=YES, fill=X, side=LEFT)
145 self.__up25.pack(expand=YES, fill=X, side=LEFT)
Barry Warsawc078b031998-10-05 21:29:04 +0000146
147 def __effect(self, event=None):
148 tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
149 if tie in (0, 1, 2, 4):
150 text = ''
151 else:
Barry Warsawae4ad6e1998-10-06 15:41:31 +0000152 text = '(= %s Level)' % {3: 'Cyan',
153 5: 'Magenta',
154 6: 'Yellow',
155 7: 'Grey'}[tie]
Barry Warsawc078b031998-10-05 21:29:04 +0000156 self.__l2.configure(text=text)
157
158 def __quit(self, event=None):
Barry Warsaw45c8d341998-10-06 19:48:35 +0000159 self.__root.quit()
Barry Warsawc078b031998-10-05 21:29:04 +0000160
161 def __withdraw(self, event=None):
162 self.__root.withdraw()
163
164 def deiconify(self, event=None):
165 self.__root.deiconify()
166
167 def __minus25(self, event=None):
168 self.__delta(-25)
169
170 def __minus10(self, event=None):
171 self.__delta(-10)
172
173 def __minus1(self, event=None):
174 self.__delta(-1)
175
176 def __plus1(self, event=None):
177 self.__delta(1)
178
179 def __plus10(self, event=None):
180 self.__delta(10)
181
182 def __plus25(self, event=None):
183 self.__delta(25)
184
185 def __delta(self, delta):
186 tie = []
187 if self.__rvar.get():
188 red = self.__red + delta
189 tie.append(red)
190 else:
191 red = self.__red
192 if self.__gvar.get():
193 green = self.__green + delta
194 tie.append(green)
195 else:
196 green = self.__green
197 if self.__bvar.get():
198 blue = self.__blue + delta
199 tie.append(blue)
200 else:
201 blue = self.__blue
202 # now apply at boundary behavior
203 atbound = self.__boundvar.get()
204 if atbound == STOP:
205 if red < 0 or green < 0 or blue < 0 or \
206 red > 255 or green > 255 or blue > 255:
207 # then
208 red, green, blue = self.__red, self.__green, self.__blue
209 elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
210 if red < 0:
211 red = red + 256
212 if green < 0:
213 green = green + 256
214 if blue < 0:
215 blue = blue + 256
216 if red > 255:
217 red = red - 256
218 if green > 255:
219 green = green - 256
220 if blue > 255:
221 blue = blue - 256
222 elif atbound == RATIO:
223 # for when 2 or 3 colors are tied together
224 dir = 0
225 for c in tie:
226 if c < 0:
227 dir = -1
228 elif c > 255:
229 dir = 1
230 if dir == -1:
231 delta = max(tie)
232 if self.__rvar.get():
233 red = red + 255 - delta
234 if self.__gvar.get():
235 green = green + 255 - delta
236 if self.__bvar.get():
237 blue = blue + 255 - delta
238 elif dir == 1:
239 delta = min(tie)
240 if self.__rvar.get():
241 red = red - delta
242 if self.__gvar.get():
243 green = green - delta
244 if self.__bvar.get():
245 blue = blue - delta
246 elif atbound == GRAV:
247 if red < 0:
248 red = 0
249 if green < 0:
250 green = 0
251 if blue < 0:
252 blue = 0
253 if red > 255:
254 red = 255
255 if green > 255:
256 green = 255
257 if blue > 255:
258 blue = 255
259 self.__sb.update_views(red, green, blue)
260 self.__root.update_idletasks()
261
262 def update_yourself(self, red, green, blue):
263 self.__red = red
264 self.__green = green
265 self.__blue = blue
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000266
267 def save_options(self, optiondb):
268 optiondb['RSLIDER'] = self.__rvar.get()
269 optiondb['GSLIDER'] = self.__gvar.get()
270 optiondb['BSLIDER'] = self.__bvar.get()
271 optiondb['ATBOUND'] = self.__boundvar.get()