1 | """DetailsViewer class.
|
---|
2 |
|
---|
3 | This class implements a pure input window which allows you to meticulously
|
---|
4 | edit the current color. You have both mouse control of the color (via the
|
---|
5 | buttons along the bottom row), and there are keyboard bindings for each of the
|
---|
6 | increment/decrement buttons.
|
---|
7 |
|
---|
8 | The top three check buttons allow you to specify which of the three color
|
---|
9 | variations are tied together when incrementing and decrementing. Red, green,
|
---|
10 | and blue are self evident. By tying together red and green, you can modify
|
---|
11 | the yellow level of the color. By tying together red and blue, you can modify
|
---|
12 | the magenta level of the color. By tying together green and blue, you can
|
---|
13 | modify the cyan level, and by tying all three together, you can modify the
|
---|
14 | grey level.
|
---|
15 |
|
---|
16 | The behavior at the boundaries (0 and 255) are defined by the `At boundary'
|
---|
17 | option 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 |
|
---|
42 | The following key bindings can be used as accelerators. Note that Pynche can
|
---|
43 | fall behind if you hold the key down as a key repeat:
|
---|
44 |
|
---|
45 | Left arrow == -1
|
---|
46 | Right arrow == +1
|
---|
47 |
|
---|
48 | Control + Left == -10
|
---|
49 | Control + Right == 10
|
---|
50 |
|
---|
51 | Shift + Left == -25
|
---|
52 | Shift + Right == +25
|
---|
53 | """
|
---|
54 |
|
---|
55 | from Tkinter import *
|
---|
56 |
|
---|
57 | STOP = 'Stop'
|
---|
58 | WRAP = 'Wrap Around'
|
---|
59 | RATIO = 'Preserve Distance'
|
---|
60 | GRAV = 'Squash'
|
---|
61 |
|
---|
62 | ADDTOVIEW = 'Details Window...'
|
---|
63 |
|
---|
64 | |
---|
65 |
|
---|
66 | class DetailsViewer:
|
---|
67 | def __init__(self, switchboard, master=None):
|
---|
68 | self.__sb = switchboard
|
---|
69 | optiondb = switchboard.optiondb()
|
---|
70 | self.__red, self.__green, self.__blue = switchboard.current_rgb()
|
---|
71 | # GUI
|
---|
72 | root = self.__root = Toplevel(master, class_='Pynche')
|
---|
73 | root.protocol('WM_DELETE_WINDOW', self.withdraw)
|
---|
74 | root.title('Pynche Details Window')
|
---|
75 | root.iconname('Pynche Details Window')
|
---|
76 | root.bind('<Alt-q>', self.__quit)
|
---|
77 | root.bind('<Alt-Q>', self.__quit)
|
---|
78 | root.bind('<Alt-w>', self.withdraw)
|
---|
79 | root.bind('<Alt-W>', self.withdraw)
|
---|
80 | # accelerators
|
---|
81 | root.bind('<KeyPress-Left>', self.__minus1)
|
---|
82 | root.bind('<KeyPress-Right>', self.__plus1)
|
---|
83 | root.bind('<Control-KeyPress-Left>', self.__minus10)
|
---|
84 | root.bind('<Control-KeyPress-Right>', self.__plus10)
|
---|
85 | root.bind('<Shift-KeyPress-Left>', self.__minus25)
|
---|
86 | root.bind('<Shift-KeyPress-Right>', self.__plus25)
|
---|
87 | #
|
---|
88 | # color ties
|
---|
89 | frame = self.__frame = Frame(root)
|
---|
90 | frame.pack(expand=YES, fill=X)
|
---|
91 | self.__l1 = Label(frame, text='Move Sliders:')
|
---|
92 | self.__l1.grid(row=1, column=0, sticky=E)
|
---|
93 | self.__rvar = IntVar()
|
---|
94 | self.__rvar.set(optiondb.get('RSLIDER', 4))
|
---|
95 | self.__radio1 = Checkbutton(frame, text='Red',
|
---|
96 | variable=self.__rvar,
|
---|
97 | command=self.__effect,
|
---|
98 | onvalue=4, offvalue=0)
|
---|
99 | self.__radio1.grid(row=1, column=1, sticky=W)
|
---|
100 | self.__gvar = IntVar()
|
---|
101 | self.__gvar.set(optiondb.get('GSLIDER', 2))
|
---|
102 | self.__radio2 = Checkbutton(frame, text='Green',
|
---|
103 | variable=self.__gvar,
|
---|
104 | command=self.__effect,
|
---|
105 | onvalue=2, offvalue=0)
|
---|
106 | self.__radio2.grid(row=2, column=1, sticky=W)
|
---|
107 | self.__bvar = IntVar()
|
---|
108 | self.__bvar.set(optiondb.get('BSLIDER', 1))
|
---|
109 | self.__radio3 = Checkbutton(frame, text='Blue',
|
---|
110 | variable=self.__bvar,
|
---|
111 | command=self.__effect,
|
---|
112 | onvalue=1, offvalue=0)
|
---|
113 | self.__radio3.grid(row=3, column=1, sticky=W)
|
---|
114 | self.__l2 = Label(frame)
|
---|
115 | self.__l2.grid(row=4, column=1, sticky=W)
|
---|
116 | self.__effect()
|
---|
117 | #
|
---|
118 | # Boundary behavior
|
---|
119 | self.__l3 = Label(frame, text='At boundary:')
|
---|
120 | self.__l3.grid(row=5, column=0, sticky=E)
|
---|
121 | self.__boundvar = StringVar()
|
---|
122 | self.__boundvar.set(optiondb.get('ATBOUND', STOP))
|
---|
123 | self.__omenu = OptionMenu(frame, self.__boundvar,
|
---|
124 | STOP, WRAP, RATIO, GRAV)
|
---|
125 | self.__omenu.grid(row=5, column=1, sticky=W)
|
---|
126 | self.__omenu.configure(width=17)
|
---|
127 | #
|
---|
128 | # Buttons
|
---|
129 | frame = self.__btnframe = Frame(frame)
|
---|
130 | frame.grid(row=0, column=0, columnspan=2, sticky='EW')
|
---|
131 | self.__down25 = Button(frame, text='-25',
|
---|
132 | command=self.__minus25)
|
---|
133 | self.__down10 = Button(frame, text='-10',
|
---|
134 | command=self.__minus10)
|
---|
135 | self.__down1 = Button(frame, text='-1',
|
---|
136 | command=self.__minus1)
|
---|
137 | self.__up1 = Button(frame, text='+1',
|
---|
138 | command=self.__plus1)
|
---|
139 | self.__up10 = Button(frame, text='+10',
|
---|
140 | command=self.__plus10)
|
---|
141 | self.__up25 = Button(frame, text='+25',
|
---|
142 | command=self.__plus25)
|
---|
143 | self.__down25.pack(expand=YES, fill=X, side=LEFT)
|
---|
144 | self.__down10.pack(expand=YES, fill=X, side=LEFT)
|
---|
145 | self.__down1.pack(expand=YES, fill=X, side=LEFT)
|
---|
146 | self.__up1.pack(expand=YES, fill=X, side=LEFT)
|
---|
147 | self.__up10.pack(expand=YES, fill=X, side=LEFT)
|
---|
148 | self.__up25.pack(expand=YES, fill=X, side=LEFT)
|
---|
149 |
|
---|
150 | def __effect(self, event=None):
|
---|
151 | tie = self.__rvar.get() + self.__gvar.get() + self.__bvar.get()
|
---|
152 | if tie in (0, 1, 2, 4):
|
---|
153 | text = ''
|
---|
154 | else:
|
---|
155 | text = '(= %s Level)' % {3: 'Cyan',
|
---|
156 | 5: 'Magenta',
|
---|
157 | 6: 'Yellow',
|
---|
158 | 7: 'Grey'}[tie]
|
---|
159 | self.__l2.configure(text=text)
|
---|
160 |
|
---|
161 | def __quit(self, event=None):
|
---|
162 | self.__root.quit()
|
---|
163 |
|
---|
164 | def withdraw(self, event=None):
|
---|
165 | self.__root.withdraw()
|
---|
166 |
|
---|
167 | def deiconify(self, event=None):
|
---|
168 | self.__root.deiconify()
|
---|
169 |
|
---|
170 | def __minus25(self, event=None):
|
---|
171 | self.__delta(-25)
|
---|
172 |
|
---|
173 | def __minus10(self, event=None):
|
---|
174 | self.__delta(-10)
|
---|
175 |
|
---|
176 | def __minus1(self, event=None):
|
---|
177 | self.__delta(-1)
|
---|
178 |
|
---|
179 | def __plus1(self, event=None):
|
---|
180 | self.__delta(1)
|
---|
181 |
|
---|
182 | def __plus10(self, event=None):
|
---|
183 | self.__delta(10)
|
---|
184 |
|
---|
185 | def __plus25(self, event=None):
|
---|
186 | self.__delta(25)
|
---|
187 |
|
---|
188 | def __delta(self, delta):
|
---|
189 | tie = []
|
---|
190 | if self.__rvar.get():
|
---|
191 | red = self.__red + delta
|
---|
192 | tie.append(red)
|
---|
193 | else:
|
---|
194 | red = self.__red
|
---|
195 | if self.__gvar.get():
|
---|
196 | green = self.__green + delta
|
---|
197 | tie.append(green)
|
---|
198 | else:
|
---|
199 | green = self.__green
|
---|
200 | if self.__bvar.get():
|
---|
201 | blue = self.__blue + delta
|
---|
202 | tie.append(blue)
|
---|
203 | else:
|
---|
204 | blue = self.__blue
|
---|
205 | # now apply at boundary behavior
|
---|
206 | atbound = self.__boundvar.get()
|
---|
207 | if atbound == STOP:
|
---|
208 | if red < 0 or green < 0 or blue < 0 or \
|
---|
209 | red > 255 or green > 255 or blue > 255:
|
---|
210 | # then
|
---|
211 | red, green, blue = self.__red, self.__green, self.__blue
|
---|
212 | elif atbound == WRAP or (atbound == RATIO and len(tie) < 2):
|
---|
213 | if red < 0:
|
---|
214 | red += 256
|
---|
215 | if green < 0:
|
---|
216 | green += 256
|
---|
217 | if blue < 0:
|
---|
218 | blue += 256
|
---|
219 | if red > 255:
|
---|
220 | red -= 256
|
---|
221 | if green > 255:
|
---|
222 | green -= 256
|
---|
223 | if blue > 255:
|
---|
224 | blue -= 256
|
---|
225 | elif atbound == RATIO:
|
---|
226 | # for when 2 or 3 colors are tied together
|
---|
227 | dir = 0
|
---|
228 | for c in tie:
|
---|
229 | if c < 0:
|
---|
230 | dir = -1
|
---|
231 | elif c > 255:
|
---|
232 | dir = 1
|
---|
233 | if dir == -1:
|
---|
234 | delta = max(tie)
|
---|
235 | if self.__rvar.get():
|
---|
236 | red = red + 255 - delta
|
---|
237 | if self.__gvar.get():
|
---|
238 | green = green + 255 - delta
|
---|
239 | if self.__bvar.get():
|
---|
240 | blue = blue + 255 - delta
|
---|
241 | elif dir == 1:
|
---|
242 | delta = min(tie)
|
---|
243 | if self.__rvar.get():
|
---|
244 | red = red - delta
|
---|
245 | if self.__gvar.get():
|
---|
246 | green = green - delta
|
---|
247 | if self.__bvar.get():
|
---|
248 | blue = blue - delta
|
---|
249 | elif atbound == GRAV:
|
---|
250 | if red < 0:
|
---|
251 | red = 0
|
---|
252 | if green < 0:
|
---|
253 | green = 0
|
---|
254 | if blue < 0:
|
---|
255 | blue = 0
|
---|
256 | if red > 255:
|
---|
257 | red = 255
|
---|
258 | if green > 255:
|
---|
259 | green = 255
|
---|
260 | if blue > 255:
|
---|
261 | blue = 255
|
---|
262 | self.__sb.update_views(red, green, blue)
|
---|
263 | self.__root.update_idletasks()
|
---|
264 |
|
---|
265 | def update_yourself(self, red, green, blue):
|
---|
266 | self.__red = red
|
---|
267 | self.__green = green
|
---|
268 | self.__blue = blue
|
---|
269 |
|
---|
270 | def save_options(self, optiondb):
|
---|
271 | optiondb['RSLIDER'] = self.__rvar.get()
|
---|
272 | optiondb['GSLIDER'] = self.__gvar.get()
|
---|
273 | optiondb['BSLIDER'] = self.__bvar.get()
|
---|
274 | optiondb['ATBOUND'] = self.__boundvar.get()
|
---|