/Tools/pynche/DetailsViewer.py

http://unladen-swallow.googlecode.com/ · Python · 273 lines · 246 code · 8 blank · 19 comment · 15 complexity · 26f4d168673812e8e82e9d38cd1f9b05 MD5 · raw file

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