blob: b190482a05514b8bb76a6038a12515e14b0517f6 [file] [log] [blame]
Barry Warsaw4638c5b1998-10-02 16:20:14 +00001"""Strip viewer and related widgets.
2
3The classes in this file implement the StripViewer shown in the top two thirds
4of the main Pynche window. It consists of three StripWidgets which display
5the variations in red, green, and blue respectively of the currently selected
6r/g/b color value.
7
8Each StripWidget shows the color variations that are reachable by varying an
9axis of the currently selected color. So for example, if the color is
10
11 (R,G,B)=(127,163,196)
12
13then the Red variations show colors from (0,163,196) to (255,163,196), the
14Green variations show colors from (127,0,196) to (127,255,196), and the Blue
15variations show colors from (127,163,0) to (127,163,255).
16
17The selected color is always visible in all three StripWidgets, and in fact
18each StripWidget highlights the selected color, and has an arrow pointing to
19the selected chip, which includes the value along that particular axis.
20
21Clicking on any chip in any StripWidget selects that color, and updates all
22arrows and other windows. By toggling on Update while dragging, Pynche will
23select the color under the cursor while you drag it, but be forewarned that
24this can be slow.
25"""
26
Barry Warsaw9d882bc1998-02-12 19:51:57 +000027from Tkinter import *
Barry Warsaw9d882bc1998-02-12 19:51:57 +000028import ColorDB
29
Barry Warsaw4c2fab51998-02-18 16:22:22 +000030# Load this script into the Tcl interpreter and call it in
31# StripWidget.set_color(). This is about as fast as it can be with the
32# current _tkinter.c interface, which doesn't support Tcl Objects.
33TCLPROC = '''\
34proc setcolor {canv colors} {
35 set i 1
36 foreach c $colors {
37 $canv itemconfigure $i -fill $c -outline $c
38 incr i
39 }
40}
41'''
42
Barry Warsawee6d8a51998-03-17 15:59:26 +000043# Tcl event types
44BTNDOWN = 4
45BTNUP = 5
46BTNDRAG = 6
47
Barry Warsaw8e4fa072001-07-10 21:44:24 +000048SPACE = ' '
Barry Warsaw4c2fab51998-02-18 16:22:22 +000049
Barry Warsaw8e4fa072001-07-10 21:44:24 +000050
51
Barry Warsaw0dc9c921998-09-28 22:42:44 +000052def constant(numchips):
53 step = 255.0 / (numchips - 1)
54 start = 0.0
55 seq = []
56 while numchips > 0:
57 seq.append(int(start))
58 start = start + step
59 numchips = numchips - 1
60 return seq
61
62# red variations, green+blue = cyan constant
Barry Warsawef300921998-12-15 01:04:38 +000063def constant_red_generator(numchips, red, green, blue):
64 seq = constant(numchips)
65 return map(None, [red] * numchips, seq, seq)
66
67# green variations, red+blue = magenta constant
68def constant_green_generator(numchips, red, green, blue):
69 seq = constant(numchips)
70 return map(None, seq, [green] * numchips, seq)
71
72# blue variations, red+green = yellow constant
73def constant_blue_generator(numchips, red, green, blue):
74 seq = constant(numchips)
75 return map(None, seq, seq, [blue] * numchips)
76
77# red variations, green+blue = cyan constant
Barry Warsaw0dc9c921998-09-28 22:42:44 +000078def constant_cyan_generator(numchips, red, green, blue):
79 seq = constant(numchips)
80 return map(None, seq, [green] * numchips, [blue] * numchips)
81
82# green variations, red+blue = magenta constant
83def constant_magenta_generator(numchips, red, green, blue):
84 seq = constant(numchips)
85 return map(None, [red] * numchips, seq, [blue] * numchips)
86
87# blue variations, red+green = yellow constant
88def constant_yellow_generator(numchips, red, green, blue):
89 seq = constant(numchips)
90 return map(None, [red] * numchips, [green] * numchips, seq)
91
92
93
Barry Warsaw9d882bc1998-02-12 19:51:57 +000094class LeftArrow:
95 _ARROWWIDTH = 30
96 _ARROWHEIGHT = 15
97 _YOFFSET = 13
98 _TEXTYOFFSET = 1
99 _TAG = ('leftarrow',)
100
101 def __init__(self, canvas, x):
102 self._canvas = canvas
103 self.__arrow, self.__text = self._create(x)
104 self.move_to(x)
105
106 def _create(self, x):
107 arrow = self._canvas.create_line(
108 x, self._ARROWHEIGHT + self._YOFFSET,
109 x, self._YOFFSET,
110 x + self._ARROWWIDTH, self._YOFFSET,
111 arrow='first',
112 width=3.0,
113 tags=self._TAG)
114 text = self._canvas.create_text(
115 x + self._ARROWWIDTH + 13,
116 self._ARROWHEIGHT - self._TEXTYOFFSET,
Barry Warsaw35ae8641998-02-13 21:28:47 +0000117 tags=self._TAG,
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000118 text='128')
119 return arrow, text
120
121 def _x(self):
122 coords = self._canvas.coords(self._TAG)
123 assert coords
124 return coords[0]
125
126 def move_to(self, x):
127 deltax = x - self._x()
128 self._canvas.move(self._TAG, deltax, 0)
129
Barry Warsaw35ae8641998-02-13 21:28:47 +0000130 def set_text(self, text):
131 self._canvas.itemconfigure(self.__text, text=text)
132
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000133
134class RightArrow(LeftArrow):
135 _TAG = ('rightarrow',)
136
137 def _create(self, x):
138 arrow = self._canvas.create_line(
139 x, self._YOFFSET,
140 x + self._ARROWWIDTH, self._YOFFSET,
141 x + self._ARROWWIDTH, self._ARROWHEIGHT + self._YOFFSET,
142 arrow='last',
143 width=3.0,
144 tags=self._TAG)
145 text = self._canvas.create_text(
Barry Warsaw8e4fa072001-07-10 21:44:24 +0000146 x - self._ARROWWIDTH + 15, # BAW: kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000147 self._ARROWHEIGHT - self._TEXTYOFFSET,
Barry Warsaw84f52e01998-10-06 23:04:55 +0000148 justify=RIGHT,
Barry Warsaw35ae8641998-02-13 21:28:47 +0000149 text='128',
150 tags=self._TAG)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000151 return arrow, text
152
153 def _x(self):
154 coords = self._canvas.bbox(self._TAG)
155 assert coords
Barry Warsaw8e4fa072001-07-10 21:44:24 +0000156 return coords[2] - 6 # BAW: kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000157
158
159
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000160class StripWidget:
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000161 _CHIPHEIGHT = 50
162 _CHIPWIDTH = 10
163 _NUMCHIPS = 40
164
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000165 def __init__(self, switchboard,
Barry Warsawca07ba01998-10-22 03:25:59 +0000166 master = None,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000167 chipwidth = _CHIPWIDTH,
168 chipheight = _CHIPHEIGHT,
169 numchips = _NUMCHIPS,
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000170 generator = None,
171 axis = None,
Barry Warsawd3441651998-10-01 03:08:07 +0000172 label = '',
Barry Warsaw5b678391998-10-06 16:13:35 +0000173 uwdvar = None,
174 hexvar = None):
Barry Warsaw5177c481998-09-28 21:01:55 +0000175 # instance variables
176 self.__generator = generator
177 self.__axis = axis
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000178 self.__numchips = numchips
Barry Warsaw5177c481998-09-28 21:01:55 +0000179 assert self.__axis in (0, 1, 2)
Barry Warsawd3441651998-10-01 03:08:07 +0000180 self.__uwd = uwdvar
Barry Warsaw5b678391998-10-06 16:13:35 +0000181 self.__hexp = hexvar
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000182 # the last chip selected
Barry Warsawee6d8a51998-03-17 15:59:26 +0000183 self.__lastchip = None
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000184 self.__sb = switchboard
Barry Warsaw5177c481998-09-28 21:01:55 +0000185
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000186 canvaswidth = numchips * (chipwidth + 1)
Barry Warsaw8e4fa072001-07-10 21:44:24 +0000187 canvasheight = chipheight + 43 # BAW: Kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000188
Barry Warsawbc689301998-02-17 03:09:40 +0000189 # create the canvas and pack it
Barry Warsawca07ba01998-10-22 03:25:59 +0000190 canvas = self.__canvas = Canvas(master,
191 width=canvaswidth,
192 height=canvasheight,
193## borderwidth=2,
194## relief=GROOVE
195 )
Barry Warsawbc689301998-02-17 03:09:40 +0000196
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000197 canvas.pack()
Barry Warsawee6d8a51998-03-17 15:59:26 +0000198 canvas.bind('<ButtonPress-1>', self.__select_chip)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000199 canvas.bind('<ButtonRelease-1>', self.__select_chip)
Barry Warsawee6d8a51998-03-17 15:59:26 +0000200 canvas.bind('<B1-Motion>', self.__select_chip)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000201
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000202 # Load a proc into the Tcl interpreter. This is used in the
203 # set_color() method to speed up setting the chip colors.
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000204 canvas.tk.eval(TCLPROC)
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000205
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000206 # create the color strip
207 chips = self.__chips = []
208 x = 1
209 y = 30
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000210 tags = ('chip',)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000211 for c in range(self.__numchips):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000212 color = 'grey'
Barry Warsaw741eae02001-04-18 03:51:55 +0000213 canvas.create_rectangle(
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000214 x, y, x+chipwidth, y+chipheight,
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000215 fill=color, outline=color,
216 tags=tags)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000217 x = x + chipwidth + 1 # for outline
Barry Warsawf67a50c1998-02-18 00:05:59 +0000218 chips.append(color)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000219
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000220 # create the strip label
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000221 self.__label = canvas.create_text(
Barry Warsawbc689301998-02-17 03:09:40 +0000222 3, y + chipheight + 8,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000223 text=label,
Barry Warsawbc689301998-02-17 03:09:40 +0000224 anchor=W)
225
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000226 # create the arrow and text item
227 chipx = self.__arrow_x(0)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000228 self.__leftarrow = LeftArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000229
230 chipx = self.__arrow_x(len(chips) - 1)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000231 self.__rightarrow = RightArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000232
Barry Warsawbc689301998-02-17 03:09:40 +0000233 def __arrow_x(self, chipnum):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000234 coords = self.__canvas.coords(chipnum+1)
Barry Warsawbc689301998-02-17 03:09:40 +0000235 assert coords
236 x0, y0, x1, y1 = coords
237 return (x1 + x0) / 2.0
238
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000239 # Invoked when one of the chips is clicked. This should just tell the
240 # switchboard to set the color on all the output components
Barry Warsawbc689301998-02-17 03:09:40 +0000241 def __select_chip(self, event=None):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000242 x = event.x
243 y = event.y
244 canvas = self.__canvas
245 chip = canvas.find_overlapping(x, y, x, y)
246 if chip and (1 <= chip[0] <= self.__numchips):
247 color = self.__chips[chip[0]-1]
248 red, green, blue = ColorDB.rrggbb_to_triplet(color)
249 etype = int(event.type)
Barry Warsawd3441651998-10-01 03:08:07 +0000250 if (etype == BTNUP or self.__uwd.get()):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000251 # update everyone
252 self.__sb.update_views(red, green, blue)
253 else:
254 # just track the arrows
255 self.__trackarrow(chip[0], (red, green, blue))
Barry Warsawbc689301998-02-17 03:09:40 +0000256
Barry Warsawee6d8a51998-03-17 15:59:26 +0000257 def __trackarrow(self, chip, rgbtuple):
258 # invert the last chip
259 if self.__lastchip is not None:
260 color = self.__canvas.itemcget(self.__lastchip, 'fill')
261 self.__canvas.itemconfigure(self.__lastchip, outline=color)
262 self.__lastchip = chip
Barry Warsawf67a50c1998-02-18 00:05:59 +0000263 # get the arrow's text
264 coloraxis = rgbtuple[self.__axis]
Barry Warsaw5b678391998-10-06 16:13:35 +0000265 if self.__hexp.get():
266 # hex
267 text = hex(coloraxis)
268 else:
269 # decimal
270 text = repr(coloraxis)
Barry Warsawf67a50c1998-02-18 00:05:59 +0000271 # move the arrow, and set it's text
272 if coloraxis <= 128:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000273 # use the left arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000274 self.__leftarrow.set_text(text)
275 self.__leftarrow.move_to(self.__arrow_x(chip-1))
276 self.__rightarrow.move_to(-100)
277 else:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000278 # use the right arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000279 self.__rightarrow.set_text(text)
280 self.__rightarrow.move_to(self.__arrow_x(chip-1))
281 self.__leftarrow.move_to(-100)
282 # and set the chip's outline
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000283 brightness = ColorDB.triplet_to_brightness(rgbtuple)
Barry Warsaw26f4b5d1998-09-28 22:52:02 +0000284 if brightness <= 128:
Barry Warsawf67a50c1998-02-18 00:05:59 +0000285 outline = 'white'
286 else:
287 outline = 'black'
288 self.__canvas.itemconfigure(chip, outline=outline)
Barry Warsaw70787ed1998-03-16 23:08:53 +0000289
Barry Warsawee6d8a51998-03-17 15:59:26 +0000290
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000291 def update_yourself(self, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000292 assert self.__generator
293 i = 1
294 chip = 0
295 chips = self.__chips = []
Barry Warsawee6d8a51998-03-17 15:59:26 +0000296 tk = self.__canvas.tk
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000297 # get the red, green, and blue components for all chips
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000298 for t in self.__generator(self.__numchips, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000299 rrggbb = ColorDB.triplet_to_rrggbb(t)
300 chips.append(rrggbb)
301 tred, tgreen, tblue = t
302 if tred <= red and tgreen <= green and tblue <= blue:
303 chip = i
304 i = i + 1
305 # call the raw tcl script
Barry Warsaw8e4fa072001-07-10 21:44:24 +0000306 colors = SPACE.join(chips)
Barry Warsawee6d8a51998-03-17 15:59:26 +0000307 tk.eval('setcolor %s {%s}' % (self.__canvas._w, colors))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000308 # move the arrows around
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000309 self.__trackarrow(chip, (red, green, blue))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000310
Barry Warsawef300921998-12-15 01:04:38 +0000311 def set(self, label, generator):
312 self.__canvas.itemconfigure(self.__label, text=label)
313 self.__generator = generator
314
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000315
316class StripViewer:
Barry Warsawca07ba01998-10-22 03:25:59 +0000317 def __init__(self, switchboard, master=None):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000318 self.__sb = switchboard
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000319 optiondb = switchboard.optiondb()
Barry Warsawef300921998-12-15 01:04:38 +0000320 # create a frame inside the master.
321 frame = Frame(master, relief=RAISED, borderwidth=1)
322 frame.grid(row=1, column=0, columnspan=2, sticky='NSEW')
323 # create the options to be used later
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000324 uwd = self.__uwdvar = BooleanVar()
325 uwd.set(optiondb.get('UPWHILEDRAG', 0))
326 hexp = self.__hexpvar = BooleanVar()
327 hexp.set(optiondb.get('HEXSTRIP', 0))
Barry Warsawef300921998-12-15 01:04:38 +0000328 # create the red, green, blue strips inside their own frame
329 frame1 = Frame(frame)
330 frame1.pack(expand=YES, fill=BOTH)
331 self.__reds = StripWidget(switchboard, frame1,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000332 generator=constant_cyan_generator,
333 axis=0,
Barry Warsawd3441651998-10-01 03:08:07 +0000334 label='Red Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000335 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000336
Barry Warsawef300921998-12-15 01:04:38 +0000337 self.__greens = StripWidget(switchboard, frame1,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000338 generator=constant_magenta_generator,
339 axis=1,
Barry Warsawd3441651998-10-01 03:08:07 +0000340 label='Green Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000341 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000342
Barry Warsawef300921998-12-15 01:04:38 +0000343 self.__blues = StripWidget(switchboard, frame1,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000344 generator=constant_yellow_generator,
345 axis=2,
Barry Warsawd3441651998-10-01 03:08:07 +0000346 label='Blue Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000347 uwdvar=uwd, hexvar=hexp)
348
Barry Warsawef300921998-12-15 01:04:38 +0000349 # create a frame to contain the controls
350 frame2 = Frame(frame)
351 frame2.pack(expand=YES, fill=BOTH)
352 frame2.columnconfigure(0, weight=20)
353 frame2.columnconfigure(2, weight=20)
Barry Warsaw5b678391998-10-06 16:13:35 +0000354
Barry Warsawef300921998-12-15 01:04:38 +0000355 padx = 8
Barry Warsaw5b678391998-10-06 16:13:35 +0000356
Barry Warsawef300921998-12-15 01:04:38 +0000357 # create the black button
358 blackbtn = Button(frame2,
Barry Warsawf5e98571999-04-27 15:56:02 +0000359 text='Black',
Barry Warsawef300921998-12-15 01:04:38 +0000360 command=self.__toblack)
361 blackbtn.grid(row=0, column=0, rowspan=2, sticky=W, padx=padx)
362
363 # create the controls
364 uwdbtn = Checkbutton(frame2,
365 text='Update while dragging',
366 variable=uwd)
367 uwdbtn.grid(row=0, column=1, sticky=W)
368 hexbtn = Checkbutton(frame2,
369 text='Hexadecimal',
370 variable=hexp,
371 command=self.__togglehex)
372 hexbtn.grid(row=1, column=1, sticky=W)
373
374 # XXX: ignore this feature for now; it doesn't work quite right yet
375
376## gentypevar = self.__gentypevar = IntVar()
377## self.__variations = Radiobutton(frame,
378## text='Variations',
379## variable=gentypevar,
380## value=0,
381## command=self.__togglegentype)
382## self.__variations.grid(row=0, column=1, sticky=W)
383## self.__constants = Radiobutton(frame,
384## text='Constants',
385## variable=gentypevar,
386## value=1,
387## command=self.__togglegentype)
388## self.__constants.grid(row=1, column=1, sticky=W)
389
390 # create the white button
391 whitebtn = Button(frame2,
Barry Warsawf5e98571999-04-27 15:56:02 +0000392 text='White',
Barry Warsawef300921998-12-15 01:04:38 +0000393 command=self.__towhite)
394 whitebtn.grid(row=0, column=2, rowspan=2, sticky=E, padx=padx)
Barry Warsaw5b678391998-10-06 16:13:35 +0000395
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000396 def update_yourself(self, red, green, blue):
397 self.__reds.update_yourself(red, green, blue)
398 self.__greens.update_yourself(red, green, blue)
399 self.__blues.update_yourself(red, green, blue)
Barry Warsaw5b678391998-10-06 16:13:35 +0000400
401 def __togglehex(self, event=None):
402 red, green, blue = self.__sb.current_rgb()
403 self.update_yourself(red, green, blue)
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000404
Barry Warsaw741eae02001-04-18 03:51:55 +0000405## def __togglegentype(self, event=None):
406## which = self.__gentypevar.get()
407## if which == 0:
408## self.__reds.set(label='Red Variations',
409## generator=constant_cyan_generator)
410## self.__greens.set(label='Green Variations',
411## generator=constant_magenta_generator)
412## self.__blues.set(label='Blue Variations',
413## generator=constant_yellow_generator)
414## elif which == 1:
415## self.__reds.set(label='Red Constant',
416## generator=constant_red_generator)
417## self.__greens.set(label='Green Constant',
418## generator=constant_green_generator)
419## self.__blues.set(label='Blue Constant',
420## generator=constant_blue_generator)
421## else:
422## assert 0
423## self.__sb.update_views_current()
Barry Warsawef300921998-12-15 01:04:38 +0000424
425 def __toblack(self, event=None):
426 self.__sb.update_views(0, 0, 0)
427
428 def __towhite(self, event=None):
429 self.__sb.update_views(255, 255, 255)
430
Barry Warsaw8a09e1c1998-10-20 20:45:46 +0000431 def save_options(self, optiondb):
432 optiondb['UPWHILEDRAG'] = self.__uwdvar.get()
433 optiondb['HEXSTRIP'] = self.__hexpvar.get()