blob: 44f006a7e05442e40c2455177c4b19990548a982 [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 Warsaw35ae8641998-02-13 21:28:47 +0000132 text='128',
133 tags=self._TAG)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000134 return arrow, text
135
136 def _x(self):
137 coords = self._canvas.bbox(self._TAG)
138 assert coords
139 return coords[2] - 6 # TBD: kludge
140
141
142
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000143class StripWidget:
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000144 _CHIPHEIGHT = 50
145 _CHIPWIDTH = 10
146 _NUMCHIPS = 40
147
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000148 def __init__(self, switchboard,
149 parent = None,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000150 chipwidth = _CHIPWIDTH,
151 chipheight = _CHIPHEIGHT,
152 numchips = _NUMCHIPS,
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000153 generator = None,
154 axis = None,
Barry Warsawd3441651998-10-01 03:08:07 +0000155 label = '',
Barry Warsaw5b678391998-10-06 16:13:35 +0000156 uwdvar = None,
157 hexvar = None):
Barry Warsaw5177c481998-09-28 21:01:55 +0000158 # instance variables
159 self.__generator = generator
160 self.__axis = axis
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000161 self.__numchips = numchips
Barry Warsaw5177c481998-09-28 21:01:55 +0000162 assert self.__axis in (0, 1, 2)
Barry Warsawd3441651998-10-01 03:08:07 +0000163 self.__uwd = uwdvar
Barry Warsaw5b678391998-10-06 16:13:35 +0000164 self.__hexp = hexvar
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000165 # the last chip selected
Barry Warsawee6d8a51998-03-17 15:59:26 +0000166 self.__lastchip = None
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000167 self.__sb = switchboard
Barry Warsaw5177c481998-09-28 21:01:55 +0000168
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000169 canvaswidth = numchips * (chipwidth + 1)
Barry Warsawbc689301998-02-17 03:09:40 +0000170 canvasheight = chipheight + 43 # TBD: Kludge
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000171
Barry Warsawbc689301998-02-17 03:09:40 +0000172 # create the canvas and pack it
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000173 canvas = self.__canvas = Canvas(
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000174 parent,
175 width=canvaswidth,
Barry Warsawbc689301998-02-17 03:09:40 +0000176 height=canvasheight,
177## borderwidth=2,
178## relief=GROOVE
179 )
180
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000181 canvas.pack()
Barry Warsawee6d8a51998-03-17 15:59:26 +0000182 canvas.bind('<ButtonPress-1>', self.__select_chip)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000183 canvas.bind('<ButtonRelease-1>', self.__select_chip)
Barry Warsawee6d8a51998-03-17 15:59:26 +0000184 canvas.bind('<B1-Motion>', self.__select_chip)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000185
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000186 # Load a proc into the Tcl interpreter. This is used in the
187 # set_color() method to speed up setting the chip colors.
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000188 canvas.tk.eval(TCLPROC)
Barry Warsaw4c2fab51998-02-18 16:22:22 +0000189
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000190 # create the color strip
191 chips = self.__chips = []
192 x = 1
193 y = 30
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000194 tags = ('chip',)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000195 for c in range(self.__numchips):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000196 color = 'grey'
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000197 rect = canvas.create_rectangle(
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000198 x, y, x+chipwidth, y+chipheight,
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000199 fill=color, outline=color,
200 tags=tags)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000201 x = x + chipwidth + 1 # for outline
Barry Warsawf67a50c1998-02-18 00:05:59 +0000202 chips.append(color)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000203
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000204 # create the strip label
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000205 self.__label = canvas.create_text(
Barry Warsawbc689301998-02-17 03:09:40 +0000206 3, y + chipheight + 8,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000207 text=label,
Barry Warsawbc689301998-02-17 03:09:40 +0000208 anchor=W)
209
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000210 # create the arrow and text item
211 chipx = self.__arrow_x(0)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000212 self.__leftarrow = LeftArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000213
214 chipx = self.__arrow_x(len(chips) - 1)
Barry Warsaw4435d5a1998-02-18 17:00:24 +0000215 self.__rightarrow = RightArrow(canvas, chipx)
Barry Warsaw9d882bc1998-02-12 19:51:57 +0000216
Barry Warsawbc689301998-02-17 03:09:40 +0000217 def __arrow_x(self, chipnum):
Barry Warsawf67a50c1998-02-18 00:05:59 +0000218 coords = self.__canvas.coords(chipnum+1)
Barry Warsawbc689301998-02-17 03:09:40 +0000219 assert coords
220 x0, y0, x1, y1 = coords
221 return (x1 + x0) / 2.0
222
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000223 # Invoked when one of the chips is clicked. This should just tell the
224 # switchboard to set the color on all the output components
Barry Warsawbc689301998-02-17 03:09:40 +0000225 def __select_chip(self, event=None):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000226 x = event.x
227 y = event.y
228 canvas = self.__canvas
229 chip = canvas.find_overlapping(x, y, x, y)
230 if chip and (1 <= chip[0] <= self.__numchips):
231 color = self.__chips[chip[0]-1]
232 red, green, blue = ColorDB.rrggbb_to_triplet(color)
233 etype = int(event.type)
Barry Warsawd3441651998-10-01 03:08:07 +0000234 if (etype == BTNUP or self.__uwd.get()):
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000235 # update everyone
236 self.__sb.update_views(red, green, blue)
237 else:
238 # just track the arrows
239 self.__trackarrow(chip[0], (red, green, blue))
Barry Warsawbc689301998-02-17 03:09:40 +0000240
Barry Warsawee6d8a51998-03-17 15:59:26 +0000241 def __trackarrow(self, chip, rgbtuple):
242 # invert the last chip
243 if self.__lastchip is not None:
244 color = self.__canvas.itemcget(self.__lastchip, 'fill')
245 self.__canvas.itemconfigure(self.__lastchip, outline=color)
246 self.__lastchip = chip
Barry Warsawf67a50c1998-02-18 00:05:59 +0000247 # get the arrow's text
248 coloraxis = rgbtuple[self.__axis]
Barry Warsaw5b678391998-10-06 16:13:35 +0000249 if self.__hexp.get():
250 # hex
251 text = hex(coloraxis)
252 else:
253 # decimal
254 text = repr(coloraxis)
Barry Warsawf67a50c1998-02-18 00:05:59 +0000255 # move the arrow, and set it's text
256 if coloraxis <= 128:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000257 # use the left arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000258 self.__leftarrow.set_text(text)
259 self.__leftarrow.move_to(self.__arrow_x(chip-1))
260 self.__rightarrow.move_to(-100)
261 else:
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000262 # use the right arrow
Barry Warsawf67a50c1998-02-18 00:05:59 +0000263 self.__rightarrow.set_text(text)
264 self.__rightarrow.move_to(self.__arrow_x(chip-1))
265 self.__leftarrow.move_to(-100)
266 # and set the chip's outline
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000267 brightness = ColorDB.triplet_to_brightness(rgbtuple)
Barry Warsaw26f4b5d1998-09-28 22:52:02 +0000268 if brightness <= 128:
Barry Warsawf67a50c1998-02-18 00:05:59 +0000269 outline = 'white'
270 else:
271 outline = 'black'
272 self.__canvas.itemconfigure(chip, outline=outline)
Barry Warsaw70787ed1998-03-16 23:08:53 +0000273
Barry Warsawee6d8a51998-03-17 15:59:26 +0000274
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000275 def update_yourself(self, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000276 assert self.__generator
277 i = 1
278 chip = 0
279 chips = self.__chips = []
280 tclcmd = []
281 tk = self.__canvas.tk
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000282 # get the red, green, and blue components for all chips
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000283 for t in self.__generator(self.__numchips, red, green, blue):
Barry Warsawee6d8a51998-03-17 15:59:26 +0000284 rrggbb = ColorDB.triplet_to_rrggbb(t)
285 chips.append(rrggbb)
286 tred, tgreen, tblue = t
287 if tred <= red and tgreen <= green and tblue <= blue:
288 chip = i
289 i = i + 1
290 # call the raw tcl script
291 colors = string.join(chips)
292 tk.eval('setcolor %s {%s}' % (self.__canvas._w, colors))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000293 # move the arrows around
Barry Warsaw6a3ea741998-09-28 20:58:06 +0000294 self.__trackarrow(chip, (red, green, blue))
Barry Warsawee6d8a51998-03-17 15:59:26 +0000295
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000296
297class StripViewer:
298 def __init__(self, switchboard, parent=None):
299 self.__sb = switchboard
Barry Warsawd7d47251998-09-29 17:36:43 +0000300 # create a frame inside the parent
Barry Warsaw65f0b6b1998-10-01 14:26:04 +0000301 self.__frame = Frame(parent) #, relief=GROOVE, borderwidth=2)
302 self.__frame.grid(row=1, column=0, columnspan=2, sticky='EW')
Barry Warsawd3441651998-10-01 03:08:07 +0000303 uwd = BooleanVar()
Barry Warsaw5b678391998-10-06 16:13:35 +0000304 hexp = BooleanVar()
Barry Warsawd7d47251998-09-29 17:36:43 +0000305 self.__reds = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000306 generator=constant_cyan_generator,
307 axis=0,
Barry Warsawd3441651998-10-01 03:08:07 +0000308 label='Red Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000309 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000310
Barry Warsawd7d47251998-09-29 17:36:43 +0000311 self.__greens = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000312 generator=constant_magenta_generator,
313 axis=1,
Barry Warsawd3441651998-10-01 03:08:07 +0000314 label='Green Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000315 uwdvar=uwd, hexvar=hexp)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000316
Barry Warsawd7d47251998-09-29 17:36:43 +0000317 self.__blues = StripWidget(switchboard, self.__frame,
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000318 generator=constant_yellow_generator,
319 axis=2,
Barry Warsawd3441651998-10-01 03:08:07 +0000320 label='Blue Variations',
Barry Warsaw5b678391998-10-06 16:13:35 +0000321 uwdvar=uwd, hexvar=hexp)
322
323 frame = self.__frame1 = Frame(self.__frame)
324 frame.pack()
325
326 self.__uwd = Checkbutton(frame,
Barry Warsawd3441651998-10-01 03:08:07 +0000327 text='Update while dragging',
328 variable=uwd)
Barry Warsaw5b678391998-10-06 16:13:35 +0000329 self.__uwd.grid(row=0, column=0, sticky=W)
330
331 self.__hex = Checkbutton(frame,
332 text='Hexadecimal',
333 variable=hexp,
334 command=self.__togglehex)
335 self.__hex.grid(row=1, column=0, sticky=W)
336
Barry Warsaw65f0b6b1998-10-01 14:26:04 +0000337 self.__div = Frame(self.__frame,
338 height=2,
339 borderwidth=2,
340 relief=RAISED)
341 self.__div.pack(expand=1, fill=X)
Barry Warsaw0dc9c921998-09-28 22:42:44 +0000342
343 def update_yourself(self, red, green, blue):
344 self.__reds.update_yourself(red, green, blue)
345 self.__greens.update_yourself(red, green, blue)
346 self.__blues.update_yourself(red, green, blue)
Barry Warsaw5b678391998-10-06 16:13:35 +0000347
348 def __togglehex(self, event=None):
349 red, green, blue = self.__sb.current_rgb()
350 self.update_yourself(red, green, blue)