blob: b82e0080cecd897ade959d8e3292c16d902db459 [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:')
88 self.__l1.grid(row=1, column=0, columnspan=3, 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 Warsaw332aa4c1998-10-06 18:29:22 +000095 self.__radio1.grid(row=1, column=3, columnspan=3, 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 Warsaw332aa4c1998-10-06 18:29:22 +0000102 self.__radio2.grid(row=2, column=3, columnspan=3, 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 Warsaw332aa4c1998-10-06 18:29:22 +0000109 self.__radio3.grid(row=3, column=3, columnspan=3, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000110 self.__l2 = Label(frame)
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000111 self.__l2.grid(row=4, column=3, columnspan=3, 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 Warsaw332aa4c1998-10-06 18:29:22 +0000116 self.__l3.grid(row=5, column=0, columnspan=3, 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 Warsaw332aa4c1998-10-06 18:29:22 +0000121 self.__omenu.grid(row=5, column=3, columnspan=3, sticky=W)
Barry Warsawc078b031998-10-05 21:29:04 +0000122 #
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000123 # Buttons
Barry Warsawc078b031998-10-05 21:29:04 +0000124 self.__down25 = Button(frame, text='-25',
125 command=self.__minus25)
126 self.__down10 = Button(frame, text='-10',
127 command=self.__minus10)
128 self.__down1 = Button(frame, text='-1',
129 command=self.__minus1)
130 self.__up1 = Button(frame, text='+1',
131 command=self.__plus1)
132 self.__up10 = Button(frame, text='+10',
133 command=self.__plus10)
134 self.__up25 = Button(frame, text='+25',
135 command=self.__plus25)
Barry Warsaw332aa4c1998-10-06 18:29:22 +0000136 self.__down25.grid(row=0, column=0)
137 self.__down10.grid(row=0, column=1)
138 self.__down1.grid(row=0, column=2)
139 self.__up1.grid(row=0, column=3)
140 self.__up10.grid(row=0, column=4)
141 self.__up25.grid(row=0, column=5)
Barry Warsawc078b031998-10-05 21:29:04 +0000142
143 def __effect(self, event=None):
144 tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
145 if tie in (0, 1, 2, 4):
146 text = ''
147 else:
Barry Warsawae4ad6e1998-10-06 15:41:31 +0000148 text = '(= %s Level)' % {3: 'Cyan',
149 5: 'Magenta',
150 6: 'Yellow',
151 7: 'Grey'}[tie]
Barry Warsawc078b031998-10-05 21:29:04 +0000152 self.__l2.configure(text=text)
153
154 def __quit(self, event=None):
Barry Warsaw45c8d341998-10-06 19:48:35 +0000155 self.__root.quit()
Barry Warsawc078b031998-10-05 21:29:04 +0000156
157 def __withdraw(self, event=None):
158 self.__root.withdraw()
159
160 def deiconify(self, event=None):
161 self.__root.deiconify()
162
163 def __minus25(self, event=None):
164 self.__delta(-25)
165
166 def __minus10(self, event=None):
167 self.__delta(-10)
168
169 def __minus1(self, event=None):
170 self.__delta(-1)
171
172 def __plus1(self, event=None):
173 self.__delta(1)
174
175 def __plus10(self, event=None):
176 self.__delta(10)
177
178 def __plus25(self, event=None):
179 self.__delta(25)
180
181 def __delta(self, delta):
182 tie = []
183 if self.__rvar.get():
184 red = self.__red + delta
185 tie.append(red)
186 else:
187 red = self.__red
188 if self.__gvar.get():
189 green = self.__green + delta
190 tie.append(green)
191 else:
192 green = self.__green
193 if self.__bvar.get():
194 blue = self.__blue + delta
195 tie.append(blue)
196 else:
197 blue = self.__blue
198 # now apply at boundary behavior
199 atbound = self.__boundvar.get()
200 if atbound == STOP:
201 if red < 0 or green < 0 or blue < 0 or \
202 red > 255 or green > 255 or blue > 255:
203 # then
204 red, green, blue = self.__red, self.__green, self.__blue
205 elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
206 if red < 0:
207 red = red + 256
208 if green < 0:
209 green = green + 256
210 if blue < 0:
211 blue = blue + 256
212 if red > 255:
213 red = red - 256
214 if green > 255:
215 green = green - 256
216 if blue > 255:
217 blue = blue - 256
218 elif atbound == RATIO:
219 # for when 2 or 3 colors are tied together
220 dir = 0
221 for c in tie:
222 if c < 0:
223 dir = -1
224 elif c > 255:
225 dir = 1
226 if dir == -1:
227 delta = max(tie)
228 if self.__rvar.get():
229 red = red + 255 - delta
230 if self.__gvar.get():
231 green = green + 255 - delta
232 if self.__bvar.get():
233 blue = blue + 255 - delta
234 elif dir == 1:
235 delta = min(tie)
236 if self.__rvar.get():
237 red = red - delta
238 if self.__gvar.get():
239 green = green - delta
240 if self.__bvar.get():
241 blue = blue - delta
242 elif atbound == GRAV:
243 if red < 0:
244 red = 0
245 if green < 0:
246 green = 0
247 if blue < 0:
248 blue = 0
249 if red > 255:
250 red = 255
251 if green > 255:
252 green = 255
253 if blue > 255:
254 blue = 255
255 self.__sb.update_views(red, green, blue)
256 self.__root.update_idletasks()
257
258 def update_yourself(self, red, green, blue):
259 self.__red = red
260 self.__green = green
261 self.__blue = blue