blob: 84fe741e7e135bf78fc8b747f6eee5ce6ff35eef [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 Warsawf67a50c1998-02-18 00:05:59 +000027import string
Barry Warsaw9d882bc1998-02-12 19:51:57 +000028from Tkinter import *
Barry Warsaw9d882bc1998-02-12 19:51:57 +000029import ColorDB
30
Barry Warsaw4c2fab51998-02-18 16:22:22 +000031# Load this script into the Tcl interpreter and call it in
32# StripWidget.set_color(). This is about as fast as it can be with the
33# current _tkinter.c interface, which doesn't support Tcl Objects.
34TCLPROC = '''\
35proc setcolor {canv colors} {
36 set i 1
37 foreach c $colors {
38 $canv itemconfigure $i -fill $c -outline $c
39 incr i
40 }
41}
42'''
43
Barry Warsawee6d8a51998-03-17 15:59:26 +000044# Tcl event types
45BTNDOWN = 4
46BTNUP = 5
47BTNDRAG = 6
48
Barry Warsaw4c2fab51998-02-18 16:22:22 +000049
Barry Warsaw0dc9c921998-09-28 22:42:44 +000050def constant(numchips):
51 step = 255.0 / (numchips - 1)
52 start = 0.0
53 seq = []
54 while numchips > 0:
55 seq.append(int(start))
56 start = start + step
57 numchips = numchips - 1
58 return seq
59
60# red variations, green+blue = cyan constant
61def constant_cyan_generator(numchips, red, green, blue):
62 seq = constant(numchips)
63 return map(None, seq, [green] * numchips, [blue] * numchips)
64
65# green variations, red+blue = magenta constant
66def constant_magenta_generator(numchips, red, green, blue):
67 seq = constant(numchips)
68 return map(None, [red] * numchips, seq, [blue] * numchips)
69
70# blue variations, red+green = yellow constant
71def constant_yellow_generator(numchips, red, green, blue):
72 seq = constant(numchips)
73 return map(None, [red] * numchips, [green] * numchips, seq)
74
75
76
Barry Warsawf67a50c1998-02-18 00:05:59 +000077
Barry Warsaw9d882bc1998-02-12 19:51:57 +000078class LeftArrow:
79 _ARROWWIDTH = 30
80 _ARROWHEIGHT = 15
81 _YOFFSET = 13
82 _TEXTYOFFSET = 1
83 _TAG = ('leftarrow',)
84
85 def __init__(self, canvas, x):
86 self._canvas = canvas
87 self.__arrow, self.__text = self._create(x)
88 self.move_to(x)
89
90 def _create(self, x):
91 arrow = self._canvas.create_line(
92 x, self._ARROWHEIGHT + self._YOFFSET,
93 x, self._YOFFSET,
94 x + self._ARROWWIDTH, self._YOFFSET,
95 arrow='first',
96 width=3.0,
97 tags=self._TAG)
98 text = self._canvas.create_text(
99 x + self._ARROWWIDTH + 13,
100 self._ARROWHEIGHT - self._TEXTYOFFSET,
Barry Warsaw35ae8641998-02-13 21:28:47 +0000101 tags=self._TAG,
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000102 text='128')
103 return arrow, text
104
105 def _x(self):
106 coords = self._canvas.coords(self._TAG)
107 assert coords
108 return coords[0]
109
110 def move_to(self, x):
111 deltax = x - self._x()
112 self._canvas.move(self._TAG, deltax, 0)
113
Barry Warsaw35ae8641998-02-13 21:28:47 +0000114 def set_text(self, text):
115 self._canvas.itemconfigure(self.__text, text=text)
116
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000117
118class RightArrow(LeftArrow):
119 _TAG = ('rightarrow',)
120
121 def _create(self, x):
122 arrow = self._canvas.create_line(
123 x, self._YOFFSET,
124 x + self._ARROWWIDTH, self._YOFFSET,
125 x + self._ARROWWIDTH, self._ARROWHEIGHT + self._YOFFSET,
126 arrow='last',
127 width=3.0,
128 tags=self._TAG)
129 text = self._canvas.create_text(
Barry Warsaw35ae8641998-02-13 21:28:47 +0000130 x - self._ARROWWIDTH + 15, # TBD: kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000131 self._ARROWHEIGHT - self._TEXTYOFFSET,
Barry Warsaw84f52e01998-10-06 23:04:55 +0000132 justify=RIGHT,
Barry Warsaw35ae8641998-02-13 21:28:47 +0000133 text='128',
134 tags=self._TAG)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000135 return arrow, text
136
137 def _x(self):
138 coords = self._canvas.bbox(self._TAG)
139 assert coords
140 return coords[2] - 6 # TBD: kludge
141
142
143
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000144class StripWidget:
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000145 _CHIPHEIGHT = 50
146 _CHIPWIDTH = 10
147 _NUMCHIPS = 40
148
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000149 def __init__(self, switchboard,
150 parent = None,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000151 chipwidth = _CHIPWIDTH,
152 chipheight = _CHIPHEIGHT,
153 numchips = _NUMCHIPS,
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000154 generator = None,
155 axis = None,
Barry Warsawd3441651998-10-01 03:08:07 +0000156 label = '',
Barry Warsaw5b678391998-10-06 16:13:35 +0000157 uwdvar = None,
158 hexvar = None):
Barry Warsaw5177c481998-09-28 21:01:55 +0000159 # instance variables
160 self.__generator = generator
161 self.__axis = axis
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000162 self.__numchips = numchips
Barry Warsaw5177c481998-09-28 21:01:55 +0000163 assert self.__axis in (0, 1, 2)
Barry Warsawd3441651998-10-01 03:08:07 +0000164 self.__uwd = uwdvar
Barry Warsaw5b678391998-10-06 16:13:35 +0000165 self.__hexp = hexvar
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000166 # the last chip selected
Barry Warsawee6d8a51998-03-17 15:59:26 +0000167 self.__lastchip = None
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000168 self.__sb = switchboard
Barry Warsaw5177c481998-09-28 21:01:55 +0000169
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000170 canvaswidth = numchips * (chipwidth + 1)
Barry Warsawbc689301998-02-17 03:09:40 +0000171 canvasheight = chipheight + 43 # TBD: Kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000172
Barry Warsawbc689301998-02-17 03:09:40 +0000173 # create the canvas and pack it
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000174 canvas = self.__canvas = Canvas(
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000175 parent,
176 width=canvaswidth,
Barry Warsawbc689301998-02-17 03:09:40 +0000177 height=canvasheight,
178## borderwidth=2,
179## relief=GROOVE
180 )
181
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000182 canvas.pack()
Barry Warsawee6d8a51998-03-17 15:59:26 +0000183 canvas.bind('<ButtonPress-1>', self.__select_chip)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000184 canvas.bind('<ButtonRelease-1>', self.__select_chip)
Barry Warsawee6d8a51998-03-17 15:59:26 +0000185 canvas.bind('<B1-Motion>', self.__select_chip)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000186
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000187 # Load a proc into the Tcl interpreter. This is used in the
188 # set_color() method to speed up setting the chip colors.
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000189 canvas.tk.eval(TCLPROC)
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000190
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000191 # create the color strip
192 chips = self.__chips = []
193 x = 1
194 y = 30
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000195 tags = ('chip',)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000196 for c in range(self.__numchips):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000197 color = 'grey'
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000198 rect = canvas.create_rectangle(
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000199 x, y, x+chipwidth, y+chipheight,
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000200 fill=color, outline=color,
201 tags=tags)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000202 x = x + chipwidth + 1 # for outline
Barry Warsawf67a50c1998-02-18 00:05:59 +0000203 chips.append(color)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000204
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000205 # create the strip label
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000206 self.__label = canvas.create_text(
Barry Warsawbc689301998-02-17 03:09:40 +0000207 3, y + chipheight + 8,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000208 text=label,
Barry Warsawbc689301998-02-17 03:09:40 +0000209 anchor=W)
210
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000211 # create the arrow and text item
212 chipx = self.__arrow_x(0)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000213 self.__leftarrow = LeftArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000214
215 chipx = self.__arrow_x(len(chips) - 1)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000216 self.__rightarrow = RightArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000217
Barry Warsawbc689301998-02-17 03:09:40 +0000218 def __arrow_x(self, chipnum):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000219 coords = self.__canvas.coords(chipnum+1)
Barry Warsawbc689301998-02-17 03:09:40 +0000220 assert coords
221 x0, y0, x1, y1 = coords
222 return (x1 + x0) / 2.0
223
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000224 # Invoked when one of the chips is clicked. This should just tell the
225 # switchboard to set the color on all the output components
Barry Warsawbc689301998-02-17 03:09:40 +0000226 def __select_chip(self, event=None):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000227 x = event.x
228 y = event.y
229 canvas = self.__canvas
230 chip = canvas.find_overlapping(x, y, x, y)
231 if chip and (1 <= chip[0] <= self.__numchips):
232 color = self.__chips[chip[0]-1]
233 red, green, blue = ColorDB.rrggbb_to_triplet(color)
234 etype = int(event.type)
Barry Warsawd3441651998-10-01 03:08:07 +0000235 if (etype == BTNUP or self.__uwd.get()):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000236 # update everyone
237 self.__sb.update_views(red, green, blue)
238 else:
239 # just track the arrows
240 self.__trackarrow(chip[0], (red, green, blue))
Barry Warsawbc689301998-02-17 03:09:40 +0000241
Barry Warsawee6d8a51998-03-17 15:59:26 +0000242 def __trackarrow(self, chip, rgbtuple):
243 # invert the last chip
244 if self.__lastchip is not None:
245 color = self.__canvas.itemcget(self.__lastchip, 'fill')
246 self.__canvas.itemconfigure(self.__lastchip, outline=color)
247 self.__lastchip = chip
Barry Warsawf67a50c1998-02-18 00:05:59 +0000248 # get the arrow's text
249 coloraxis = rgbtuple[self.__axis]
Barry Warsaw5b678391998-10-06 16:13:35 +0000250 if self.__hexp.get():
251 # hex
252 text = hex(coloraxis)
253 else:
254 # decimal
255 text = repr(coloraxis)
Barry Warsawf67a50c1998-02-18 00:05:59 +0000256 # move the arrow, and set it's text
257 if coloraxis <= 128:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000258 # use the left arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000259 self.__leftarrow.set_text(text)
260 self.__leftarrow.move_to(self.__arrow_x(chip-1))
261 self.__rightarrow.move_to(-100)
262 else:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000263 # use the right arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000264 self.__rightarrow.set_text(text)
265 self.__rightarrow.move_to(self.__arrow_x(chip-1))
266 self.__leftarrow.move_to(-100)
267 # and set the chip's outline
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000268 brightness = ColorDB.triplet_to_brightness(rgbtuple)
Barry Warsaw26f4b5d1998-09-28 22:52:02 +0000269 if brightness <= 128:
Barry Warsawf67a50c1998-02-18 00:05:59 +0000270 outline = 'white'
271 else:
272 outline = 'black'
273 self.__canvas.itemconfigure(chip, outline=outline)
Barry Warsaw70787ed1998-03-16 23:08:53 +0000274
Barry Warsawee6d8a51998-03-17 15:59:26 +0000275
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000276 def update_yourself(self, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000277 assert self.__generator
278 i = 1
279 chip = 0
280 chips = self.__chips = []
281 tclcmd = []
282 tk = self.__canvas.tk
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000283 # get the red, green, and blue components for all chips
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000284 for t in self.__generator(self.__numchips, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000285 rrggbb = ColorDB.triplet_to_rrggbb(t)
286 chips.append(rrggbb)
287 tred, tgreen, tblue = t
288 if tred <= red and tgreen <= green and tblue <= blue:
289 chip = i
290 i = i + 1
291 # call the raw tcl script
292 colors = string.join(chips)
293 tk.eval('setcolor %s {%s}' % (self.__canvas._w, colors))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000294 # move the arrows around
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000295 self.__trackarrow(chip, (red, green, blue))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000296
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000297
298class StripViewer:
299 def __init__(self, switchboard, parent=None):
300 self.__sb = switchboard
Barry Warsawd7d47251998-09-29 17:36:43 +0000301 # create a frame inside the parent
Barry Warsaw65f0b6b1998-10-01 14:26:04 +0000302 self.__frame = Frame(parent) #, relief=GROOVE, borderwidth=2)
303 self.__frame.grid(row=1, column=0, columnspan=2, sticky='EW')
Barry Warsawd3441651998-10-01 03:08:07 +0000304 uwd = BooleanVar()
Barry Warsaw5b678391998-10-06 16:13:35 +0000305 hexp = BooleanVar()
Barry Warsawd7d47251998-09-29 17:36:43 +0000306 self.__reds = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000307 generator=constant_cyan_generator,
308 axis=0,
Barry Warsawd3441651998-10-01 03:08:07 +0000309 label='Red Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000310 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000311
Barry Warsawd7d47251998-09-29 17:36:43 +0000312 self.__greens = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000313 generator=constant_magenta_generator,
314 axis=1,
Barry Warsawd3441651998-10-01 03:08:07 +0000315 label='Green Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000316 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000317
Barry Warsawd7d47251998-09-29 17:36:43 +0000318 self.__blues = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000319 generator=constant_yellow_generator,
320 axis=2,
Barry Warsawd3441651998-10-01 03:08:07 +0000321 label='Blue Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000322 uwdvar=uwd, hexvar=hexp)
323
324 frame = self.__frame1 = Frame(self.__frame)
325 frame.pack()
326
327 self.__uwd = Checkbutton(frame,
Barry Warsawd3441651998-10-01 03:08:07 +0000328 text='Update while dragging',
329 variable=uwd)
Barry Warsaw5b678391998-10-06 16:13:35 +0000330 self.__uwd.grid(row=0, column=0, sticky=W)
331
332 self.__hex = Checkbutton(frame,
333 text='Hexadecimal',
334 variable=hexp,
335 command=self.__togglehex)
336 self.__hex.grid(row=1, column=0, sticky=W)
337
Barry Warsaw65f0b6b1998-10-01 14:26:04 +0000338 self.__div = Frame(self.__frame,
339 height=2,
340 borderwidth=2,
341 relief=RAISED)
342 self.__div.pack(expand=1, fill=X)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000343
344 def update_yourself(self, red, green, blue):
345 self.__reds.update_yourself(red, green, blue)
346 self.__greens.update_yourself(red, green, blue)
347 self.__blues.update_yourself(red, green, blue)
Barry Warsaw5b678391998-10-06 16:13:35 +0000348
349 def __togglehex(self, event=None):
350 red, green, blue = self.__sb.current_rgb()
351 self.update_yourself(red, green, blue)