blob: 130e18fb3b64e4dc1bab3d3056e44fe54a7796bf [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
55import sys
56from Tkinter import *
Barry Warsawc078b031998-10-05 21:29:04 +000057
58STOP = 'Stop'
59WRAP = 'Wrap Around'
60RATIO = 'Preserve Distance'
61GRAV = 'Squash'
62
63
64class DetailsViewer:
65 def __init__(self, switchboard, parent=None):
66 self.__sb = switchboard
67 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)
88 self.__l1 = Label(frame, text='Color Ties:')
89 self.__l1.grid(row=0, column=0, columnspan=3, sticky=E)
90 self.__rvar = IntVar()
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000091 self.__rvar.set(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)
96 self.__radio1.grid(row=0, column=3, columnspan=3, sticky=W)
97 self.__gvar = IntVar()
Barry Warsaw7c51a9a1998-10-05 21:31:37 +000098 self.__gvar.set(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)
103 self.__radio2.grid(row=1, column=3, columnspan=3, sticky=W)
104 self.__bvar = IntVar()
105 self.__bvar.set(1)
106 self.__radio3 = Checkbutton(frame, text='Blue',
107 variable=self.__bvar,
108 command=self.__effect,
109 onvalue=1, offvalue=0)
110 self.__radio3.grid(row=2, column=3, columnspan=3, sticky=W)
111 self.__l2 = Label(frame)
112 self.__l2.grid(row=3, column=3, columnspan=3, 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:')
117 self.__l3.grid(row=4, column=0, columnspan=3, sticky=E)
118 self.__boundvar = StringVar()
119 self.__boundvar.set(STOP)
120 self.__omenu = OptionMenu(frame, self.__boundvar,
121 STOP, WRAP, RATIO, GRAV)
122 self.__omenu.grid(row=4, column=3, columnspan=3, sticky=W)
123 #
124 # Button
125 self.__down25 = Button(frame, text='-25',
126 command=self.__minus25)
127 self.__down10 = Button(frame, text='-10',
128 command=self.__minus10)
129 self.__down1 = Button(frame, text='-1',
130 command=self.__minus1)
131 self.__up1 = Button(frame, text='+1',
132 command=self.__plus1)
133 self.__up10 = Button(frame, text='+10',
134 command=self.__plus10)
135 self.__up25 = Button(frame, text='+25',
136 command=self.__plus25)
137 self.__down25.grid(row=5, column=0)
138 self.__down10.grid(row=5, column=1)
139 self.__down1.grid(row=5, column=2)
140 self.__up1.grid(row=5, column=3)
141 self.__up10.grid(row=5, column=4)
142 self.__up25.grid(row=5, column=5)
143
144 def __effect(self, event=None):
145 tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
146 if tie in (0, 1, 2, 4):
147 text = ''
148 else:
149 text = '(%s)' % {3: 'Cyan',
150 5: 'Magenta',
151 6: 'Yellow',
152 7: 'Grey'}[tie]
153 self.__l2.configure(text=text)
154
155 def __quit(self, event=None):
156 sys.exit(0)
157
158 def __withdraw(self, event=None):
159 self.__root.withdraw()
160
161 def deiconify(self, event=None):
162 self.__root.deiconify()
163
164 def __minus25(self, event=None):
165 self.__delta(-25)
166
167 def __minus10(self, event=None):
168 self.__delta(-10)
169
170 def __minus1(self, event=None):
171 self.__delta(-1)
172
173 def __plus1(self, event=None):
174 self.__delta(1)
175
176 def __plus10(self, event=None):
177 self.__delta(10)
178
179 def __plus25(self, event=None):
180 self.__delta(25)
181
182 def __delta(self, delta):
183 tie = []
184 if self.__rvar.get():
185 red = self.__red + delta
186 tie.append(red)
187 else:
188 red = self.__red
189 if self.__gvar.get():
190 green = self.__green + delta
191 tie.append(green)
192 else:
193 green = self.__green
194 if self.__bvar.get():
195 blue = self.__blue + delta
196 tie.append(blue)
197 else:
198 blue = self.__blue
199 # now apply at boundary behavior
200 atbound = self.__boundvar.get()
201 if atbound == STOP:
202 if red < 0 or green < 0 or blue < 0 or \
203 red > 255 or green > 255 or blue > 255:
204 # then
205 red, green, blue = self.__red, self.__green, self.__blue
206 elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
207 if red < 0:
208 red = red + 256
209 if green < 0:
210 green = green + 256
211 if blue < 0:
212 blue = blue + 256
213 if red > 255:
214 red = red - 256
215 if green > 255:
216 green = green - 256
217 if blue > 255:
218 blue = blue - 256
219 elif atbound == RATIO:
220 # for when 2 or 3 colors are tied together
221 dir = 0
222 for c in tie:
223 if c < 0:
224 dir = -1
225 elif c > 255:
226 dir = 1
227 if dir == -1:
228 delta = max(tie)
229 if self.__rvar.get():
230 red = red + 255 - delta
231 if self.__gvar.get():
232 green = green + 255 - delta
233 if self.__bvar.get():
234 blue = blue + 255 - delta
235 elif dir == 1:
236 delta = min(tie)
237 if self.__rvar.get():
238 red = red - delta
239 if self.__gvar.get():
240 green = green - delta
241 if self.__bvar.get():
242 blue = blue - delta
243 elif atbound == GRAV:
244 if red < 0:
245 red = 0
246 if green < 0:
247 green = 0
248 if blue < 0:
249 blue = 0
250 if red > 255:
251 red = 255
252 if green > 255:
253 green = 255
254 if blue > 255:
255 blue = 255
256 self.__sb.update_views(red, green, blue)
257 self.__root.update_idletasks()
258
259 def update_yourself(self, red, green, blue):
260 self.__red = red
261 self.__green = green
262 self.__blue = blue