sccd_widget.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. '''
  2. Created on 31-jul.-2014
  3. @author: Simon
  4. '''
  5. from PIL import Image, ImageTk
  6. import Tkinter as tk
  7. import atexit
  8. from sccd.runtime.statecharts_core import Event
  9. class SCCDWidget:
  10. controller = None
  11. def __init__(self, configure_later=False):
  12. if not configure_later:
  13. self.set_bindable_and_tagorid(None, None)
  14. def set_bindable_and_tagorid(self, bindable=None, tagorid=None):
  15. if bindable is None:
  16. bindable = self
  17. self.bindable = bindable
  18. self.mytagorid = tagorid
  19. if isinstance(self, tk.Toplevel):
  20. self.protocol("WM_DELETE_WINDOW", self.window_close)
  21. if tagorid is not None:
  22. if not isinstance(tagorid, list):
  23. tagorid = [tagorid]
  24. for t in tagorid:
  25. self.bindable.tag_bind(t, "<Button>", self.on_click)
  26. self.bindable.tag_bind(t, "<Double-Button>", self.on_dbclick)
  27. self.bindable.tag_bind(t, "<ButtonRelease>", self.on_release)
  28. self.bindable.tag_bind(t, "<Motion>", self.on_motion)
  29. self.bindable.tag_bind(t, "<Enter>", self.on_enter)
  30. self.bindable.tag_bind(t, "<Leave>", self.on_leave)
  31. self.bindable.tag_bind(t, "<Key>", self.on_key)
  32. self.bindable.tag_bind(t, "<KeyRelease>", self.on_key_release)
  33. else:
  34. self.bindable.bind("<Button>", self.on_click)
  35. self.bindable.bind("<ButtonRelease>", self.on_release)
  36. self.bindable.bind("<Motion>", self.on_motion)
  37. self.bindable.bind("<Enter>", self.on_enter)
  38. self.bindable.bind("<Leave>", self.on_leave)
  39. self.bindable.bind("<Key>", self.on_key)
  40. self.bindable.bind("<KeyRelease>", self.on_key_release)
  41. self.bindable.bind("<Shift-Up>",self.zoom_in)
  42. self.bindable.bind("<Shift-Down>", self.zoom_out)
  43. self.bindable.bind("<MouseWheel>", self.scroller)
  44. self.last_x = 50
  45. self.last_y = 50
  46. self.event_delta = 0
  47. self.zoomer_count = 0
  48. self.selected_type = None
  49. def scroller(self, event):
  50. self.event_delta = event.delta
  51. event_name= "scroll_mousewheel"
  52. self.last_x = event.x
  53. self.last_y = event.y
  54. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  55. return "break"
  56. def zoom_in(self, event):
  57. event_name = "zoomer_event"
  58. self.last_x = event.x
  59. self.last_y = event.y
  60. if self.zoomer_count <=6:
  61. self.event_delta = 120
  62. self.zoomer_count +=1
  63. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  64. return "break"
  65. def zoom_out(self, event):
  66. event_name = "zoomer_event"
  67. self.last_x = event.x
  68. self.last_y = event.y
  69. if self.zoomer_count >=-6:
  70. self.event_delta = -120
  71. self.zoomer_count -=1
  72. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  73. return "break"
  74. def on_dbclick(self, event):
  75. event_name = None
  76. if event.num == 1:
  77. event_name = "left-dbclick"
  78. elif event.num == 2:
  79. event_name = "middle-dbclick"
  80. elif event.num == 3:
  81. event_name = "right-dbclick"
  82. if event_name:
  83. self.last_x = event.x
  84. self.last_y = event.y
  85. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  86. return "break"
  87. def on_click(self, event):
  88. event_name = None
  89. if event.num == 1:
  90. event_name = "left-click"
  91. elif event.num == 2:
  92. event_name = "middle-click"
  93. elif event.num == 3:
  94. event_name = "right-click"
  95. if event_name:
  96. self.last_x = event.x
  97. self.last_y = event.y
  98. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  99. return "break"
  100. def on_release(self, event):
  101. event_name = None
  102. if event.num == 1:
  103. event_name = "left-release"
  104. elif event.num == 2:
  105. event_name = "middle-release"
  106. elif event.num == 3:
  107. event_name = "right-release"
  108. if event_name:
  109. self.last_x = event.x
  110. self.last_y = event.y
  111. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  112. return "break"
  113. def on_motion(self, event):
  114. self.last_x = event.x
  115. self.last_y = event.y
  116. SCCDWidget.controller.addInput(Event("motion", "input", [id(self)]))
  117. return "break"
  118. def on_enter(self, event):
  119. SCCDWidget.controller.addInput(Event("enter", "input", [id(self)]))
  120. return "break"
  121. def on_leave(self, event):
  122. SCCDWidget.controller.addInput(Event("leave", "input", [id(self)]))
  123. return "break"
  124. def on_key(self, event):
  125. event_name = None
  126. if event.keysym == 'Escape':
  127. event_name = "escape"
  128. elif event.keysym == 'Return':
  129. event_name = "return"
  130. elif event.keysym == 'Delete':
  131. event_name = "delete"
  132. elif event.keysym == 'Shift_L':
  133. event_name = "shift"
  134. elif event.keysym == 'Control_L':
  135. event_name = "control"
  136. elif event.keysym == 'Left':
  137. event_name = "left_key"
  138. elif event.keysym == 'Right':
  139. event_name = "right_key"
  140. elif event.keysym == 'Up':
  141. event_name = "up_key"
  142. elif event.keysym == 'Down':
  143. event_name = "down_key"
  144. if event_name:
  145. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  146. return "break"
  147. def on_key_release(self, event):
  148. event_name = None
  149. if event.keysym == 'Escape':
  150. event_name = "escape-release"
  151. elif event.keysym == 'Return':
  152. event_name = "return-release"
  153. elif event.keysym == 'Delete':
  154. event_name = "delete-release"
  155. elif event.keysym == 'Shift_L':
  156. event_name = "shift-release"
  157. elif event.keysym == 'Control_L':
  158. event_name = "control-release"
  159. if event_name:
  160. SCCDWidget.controller.addInput(Event(event_name, "input", [id(self)]))
  161. return "break"
  162. def window_close(self):
  163. SCCDWidget.controller.addInput(Event("window-close", "input", [id(self)]))
  164. class HorizontalScrolledFrame(tk.Frame):
  165. def __init__(self, parent, *args, **kw):
  166. tk.Frame.__init__(self, parent, *args, **kw)
  167. # create a canvas object and a horizontal scrollbar for scrolling it
  168. hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
  169. hscrollbar.pack(fill=tk.X, side=tk.BOTTOM, expand=tk.FALSE)
  170. self.canvas = tk.Canvas(self, bd=0, highlightthickness=0,
  171. xscrollcommand=hscrollbar.set, height=78)
  172. self.canvas.pack(side=tk.LEFT, fill=tk.X, expand=tk.TRUE)
  173. hscrollbar.config(command=self.canvas.xview)
  174. # reset the view
  175. self.canvas.xview_moveto(0)
  176. self.canvas.yview_moveto(0)
  177. # create a frame inside the canvas which will be scrolled with it
  178. self.interior = interior = tk.Frame(self.canvas)
  179. self.canvas.create_window(0, 0, window=interior, anchor=tk.NW)
  180. # track changes to the canvas and frame width and sync them,
  181. # also updating the scrollbar
  182. def _configure_interior(event):
  183. # update the scrollbars to match the size of the inner frame
  184. size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
  185. self.canvas.config(scrollregion="0 0 %s %s" % size)
  186. interior.bind('<Configure>', _configure_interior)
  187. class VerticalScrolledFrame(tk.Frame):
  188. def __init__(self, parent, *args, **kw):
  189. tk.Frame.__init__(self, parent, *args, **kw)
  190. vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
  191. vscrollbar.pack(fill=tk.Y, side=tk.RIGHT, expand=tk.FALSE)
  192. canvas = tk.Canvas(self, bd=0, highlightthickness=0,
  193. yscrollcommand=vscrollbar.set, width=100)
  194. canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
  195. vscrollbar.config(command=canvas.yview)
  196. canvas.xview_moveto(0)
  197. canvas.yview_moveto(0)
  198. self.interior = interior = tk.Frame(canvas)
  199. interior_id = canvas.create_window(0, 0, window=interior,
  200. anchor=tk.NW)
  201. def _configure_interior(event):
  202. size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
  203. canvas.config(scrollregion="0 0 %s %s" % size)
  204. if interior.winfo_reqwidth() != canvas.winfo_width():
  205. canvas.config(width=interior.winfo_reqwidth())
  206. interior.bind('<Configure>', _configure_interior)
  207. def _configure_canvas(event):
  208. if interior.winfo_reqwidth() != canvas.winfo_width():
  209. canvas.itemconfigure(interior_id, width=canvas.winfo_width())
  210. canvas.bind('<Configure>', _configure_canvas)
  211. return
  212. class VerticallyScrolledWindow(tk.Frame):
  213. def __init__(self, root):
  214. tk.Frame.__init__(self, root)
  215. defaultbg = root.cget('bg')
  216. self.canvas = tk.Canvas(root, borderwidth=0, background=defaultbg)
  217. self.frame = tk.Frame(self.canvas, background=defaultbg)
  218. self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
  219. self.canvas.configure(yscrollcommand=self.vsb.set)
  220. self.vsb.pack(side="right", fill="y")
  221. self.canvas.pack(side="left", fill="both", expand=True)
  222. self.canvas.create_window((4,4), window=self.frame, anchor="nw",
  223. tags="self.frame")
  224. self.frame.bind("<Configure>", self.onFrameConfigure)
  225. self.frame.bind_all("<MouseWheel>", self.scroll)
  226. def scroll(self, event):
  227. self.canvas.yview_scroll(-1 * (event.delta / 120), "units")
  228. def onFrameConfigure(self, event):
  229. '''Reset the scroll region to encompass the inner frame'''
  230. self.canvas.configure(scrollregion=self.canvas.bbox("all"))
  231. class Visual(object):
  232. def get_params(self):
  233. raise NotImplementedError()
  234. class TextVisual(Visual):
  235. def __init__(self, text):
  236. super(TextVisual, self).__init__()
  237. self.text = text
  238. def get_params(self):
  239. return {'text': self.text}
  240. class ImageVisual(Visual):
  241. def __init__(self, img_loc):
  242. super(ImageVisual, self).__init__()
  243. self.img = ImageTk.PhotoImage(Image.open(img_loc).resize((32, 32), Image.ANTIALIAS))
  244. def get_params(self):
  245. return {'image': self.img}
  246. class ToolTip:
  247. def __init__(self, widget, text):
  248. self.widget = widget
  249. self.tipwindow = None
  250. self.id = None
  251. self.x = self.y = 0
  252. self.text = text
  253. def showtip(self):
  254. "Display text in tooltip window"
  255. if self.tipwindow or not self.text:
  256. return
  257. x, y, cx, cy = self.widget.bbox("insert")
  258. x = x + cx + self.widget.winfo_rootx() + 27
  259. y = y + cy + self.widget.winfo_rooty() - 20
  260. self.tipwindow = tw = tk.Toplevel(self.widget)
  261. tw.wm_overrideredirect(1)
  262. tw.wm_geometry("+%d+%d" % (x, y))
  263. try:
  264. # For Mac OS
  265. tw.tk.call("::tk::unsupported::MacWindowStyle",
  266. "style", tw._w,
  267. "help", "noActivates")
  268. except tk.TclError:
  269. pass
  270. label = tk.Label(tw, text=self.text, justify=tk.LEFT,
  271. background="#ffffe0", relief=tk.SOLID, borderwidth=1,
  272. font=("tahoma", "8", "normal"))
  273. label.pack(ipadx=1)
  274. def hidetip(self):
  275. tw = self.tipwindow
  276. self.tipwindow = None
  277. if tw:
  278. tw.destroy()