blob: 3fb8a7da47643a7d305b270fa1095074bf45c7ca [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
Barry Warsawf3ebf901999-04-27 18:54:12 +000062ADDTOVIEW = 'Details Window...'
63
Barry Warsawc078b031998-10-05 21:29:04 +000064
65class DetailsViewer:
Barry Warsawca07ba01998-10-22 03:25:59 +000066 def __init__(self, switchboard, master=None):
Barry Warsawc078b031998-10-05 21:29:04 +000067 self.__sb = switchboard
Barry Warsaw8a09e1c1998-10-20 20:45:46 +000068 optiondb = switchboard.optiondb()
Barry Warsawc078b031998-10-05 21:29:04 +000069 self.__red, self.__green, self.__blue = switchboard.current_rgb()
70 # GUI
Barry Warsawca07ba01998-10-22 03:25:59 +000071 root = self.__root = Toplevel(master, class_='Pynche')
72 root.protocol('WM_DELETE_WINDOW', self.withdraw)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000073 root.title('Pynche Details Window')
Barry Warsawc078b031998-10-05 21:29:04 +000074 root.iconname('Pynche Details Window')
75 root.bind('<Alt-q>', self.__quit)
76 root.bind('<Alt-Q>', self.__quit)
Barry Warsawca07ba01998-10-22 03:25:59 +000077 root.bind('<Alt-w>', self.withdraw)
78 root.bind('<Alt-W>', self.withdraw)
Barry Warsawc078b031998-10-05 21:29:04 +000079 # 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)
Barry Warsaw332aa4c1998-10-06 18:29:22 +000090 self.__l1 = Label(frame, text='Move Sliders:')
Barry Warsawc9580431998-10-07 03:19:33 +000091 self.__l1.grid(row=1, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +000092 self.__rvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +000093 self.__rvar.set(optiondb.get('RSLIDER', 4))
Barry Warsawc078b031998-10-05 21:29:04 +000094 self.__radio1 = Checkbutton(frame, text='Red',
95 variable=self.__rvar,
96 command=self.__effect,
97 onvalue=4, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +000098 self.__radio1.grid(row=1, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +000099 self.__gvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000100 self.__gvar.set(optiondb.get('GSLIDER', 2))
Barry Warsawc078b031998-10-05 21:29:04 +0000101 self.__radio2 = Checkbutton(frame, text='Green',
102 variable=self.__gvar,
103 command=self.__effect,
104 onvalue=2, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000105 self.__radio2.grid(row=2, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000106 self.__bvar = IntVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000107 self.__bvar.set(optiondb.get('BSLIDER', 1))
Barry Warsawc078b031998-10-05 21:29:04 +0000108 self.__radio3 = Checkbutton(frame, text='Blue',
109 variable=self.__bvar,
110 command=self.__effect,
111 onvalue=1, offvalue=0)
Barry Warsawc9580431998-10-07 03:19:33 +0000112 self.__radio3.grid(row=3, column=1, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000113 self.__l2 = Label(frame)
Barry Warsawc9580431998-10-07 03:19:33 +0000114 self.__l2.grid(row=4, column=1, sticky=W)
Barry Warsaw7c51a9a1998-10-05 21:31:37 +0000115 self.__effect()
Barry Warsawc078b031998-10-05 21:29:04 +0000116 #
117 # Boundary behavior
118 self.__l3 = Label(frame, text='At boundary:')
Barry Warsawc9580431998-10-07 03:19:33 +0000119 self.__l3.grid(row=5, column=0, sticky=E)
Barry Warsawc078b031998-10-05 21:29:04 +0000120 self.__boundvar = StringVar()
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000121 self.__boundvar.set(optiondb.get('ATBOUND', STOP))
Barry Warsawc078b031998-10-05 21:29:04 +0000122 self.__omenu = OptionMenu(frame, self.__boundvar,
123 STOP, WRAP, RATIO, GRAV)
Barry Warsawc9580431998-10-07 03:19:33 +0000124 self.__omenu.grid(row=5, column=1, sticky=W)
125 self.__omenu.configure(width=17)
Barry Warsawc078b031998-10-05 21:29:04 +0000126 #
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000127 # Buttons
Barry Warsawc9580431998-10-07 03:19:33 +0000128 frame = self.__btnframe = Frame(frame)
129 frame.grid(row=0, column=0, columnspan=2, sticky='EW')
Barry Warsawc078b031998-10-05 21:29:04 +0000130 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)
Barry Warsawc9580431998-10-07 03:19:33 +0000142 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)
Barry Warsawc078b031998-10-05 21:29:04 +0000148
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:
Barry Warsawae4ad6e1998-10-06 15:41:31 +0000154 text = '(= %s Level)' % {3: 'Cyan',
155 5: 'Magenta',
156 6: 'Yellow',
157 7: 'Grey'}[tie]
Barry Warsawc078b031998-10-05 21:29:04 +0000158 self.__l2.configure(text=text)
159
160 def __quit(self, event=None):
Barry Warsaw45c8d341998-10-06 19:48:35 +0000161 self.__root.quit()
Barry Warsawc078b031998-10-05 21:29:04 +0000162
Barry Warsawca07ba01998-10-22 03:25:59 +0000163 def withdraw(self, event=None):
Barry Warsawc078b031998-10-05 21:29:04 +0000164 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 = red + 256
214 if green < 0:
215 green = green + 256
216 if blue < 0:
217 blue = blue + 256
218 if red > 255:
219 red = red - 256
220 if green > 255:
221 green = green - 256
222 if blue > 255:
223 blue = 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
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000268
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()