main.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import matplotlib
  2. matplotlib.use("TkAgg")
  3. from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  4. from matplotlib.figure import Figure
  5. try:
  6. from Tkinter import *
  7. import tkSimpleDialog
  8. except ImportError:
  9. from tkinter import *
  10. import tkinter.simpledialog as tkSimpleDialog
  11. from PIL import Image, ImageTk
  12. if sys.version_info[0] < 3:
  13. from urllib2 import urlopen as urlopen
  14. from urllib2 import Request as Request
  15. from urllib import urlencode as urlencode
  16. else:
  17. from urllib.request import urlopen as urlopen
  18. from urllib.request import Request as Request
  19. from urllib.parse import urlencode as urlencode
  20. import json
  21. import time
  22. JUMP = 50
  23. MAX_WIDTH = 20 * JUMP
  24. MAX_HEIGHT = 20 * JUMP
  25. address = "http://127.0.0.1:8001"
  26. taskname = "test"
  27. root = Tk()
  28. canvas = Canvas(root, width=MAX_WIDTH, height=MAX_HEIGHT, bg="white")
  29. name = 0
  30. class FakeLayer():
  31. def __init__(self, address):
  32. self.types = {}
  33. self.sources = {}
  34. self.targets = {}
  35. self.attrs = {}
  36. def read_available_attributes(self, name):
  37. if self.types[name] == "const":
  38. return ["value"]
  39. else:
  40. return []
  41. def read_attribute(self, name, attr):
  42. return self.attr.get(name, {}).get(attr, None)
  43. def set_attribute(self, name, attr, value):
  44. self.attrs[name][attr] = value
  45. def instantiate_block(self, name, block_type):
  46. self.types[name] = block_type
  47. def instantiate_link(self, name, link_type, source, target):
  48. self.types[name] = link_type
  49. self.sources[name] = source
  50. self.targets[name] = target
  51. self.attrs[name] = {}
  52. def simulate(self):
  53. pass
  54. def step(self):
  55. pass
  56. def pause(self):
  57. pass
  58. attribute = []
  59. available_attrs = []
  60. simulation = []
  61. def poll(address):
  62. working_available_attrs = []
  63. working_simulation = None
  64. while 1:
  65. returnvalue = json.loads(urlopen(Request(address, urlencode({"op": "get_output", "taskname": taskname}))).read())
  66. print("Process " + str(returnvalue))
  67. if (returnvalue.startswith("AVAILABLE_ATTR_VALUE")):
  68. working_available_attrs.append([json.loads(returnvalue.split(" ", 1)[1]), None])
  69. elif (returnvalue.startswith("AVAILABLE_ATTR_TYPE")):
  70. working_available_attrs[-1][1] = json.loads(returnvalue.split(" ", 1)[1])
  71. elif (returnvalue.startswith("AVAILABLE_ATTR_END")):
  72. available_attrs.append(working_available_attrs)
  73. working_available_attrs = []
  74. elif (returnvalue.startswith("ATTR_VALUE")):
  75. v = returnvalue.split(" ", 1)[1]
  76. if v == "None":
  77. v = None
  78. else:
  79. v = json.loads(v)
  80. attribute.append(v)
  81. elif (returnvalue.startswith("SIM_TIME")):
  82. working_simulation = (json.loads(returnvalue.split(" ", 1)[1]), {})
  83. elif (returnvalue.startswith("SIM_PROBE")):
  84. blockname, blockvalue = returnvalue.split(" ", 1)[1].rsplit(" ", 1)
  85. working_simulation[1][json.loads(blockname)] = json.loads(blockvalue)
  86. elif (returnvalue.startswith("SIM_END")):
  87. simulation.append(working_simulation)
  88. working_simulation = None
  89. elif (returnvalue.startswith("CONFORMANCE_OK")):
  90. root.configure(background="grey")
  91. elif (returnvalue.startswith("CONFORMANCE_FAIL")):
  92. root.configure(background="red")
  93. elif (returnvalue.startswith("ALGEBRAIC_LOOP")):
  94. root.configure(background="blue")
  95. else:
  96. print("Error: got unknown result: " + returnvalue)
  97. class MvLayer():
  98. def __init__(self, address):
  99. import threading
  100. self.address = address
  101. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % taskname, "taskname": "task_manager"}))).read()
  102. thrd = threading.Thread(target=poll, args=[address])
  103. thrd.daemon = True
  104. thrd.start()
  105. def read_available_attributes(self, name):
  106. urlopen(Request(address, urlencode({"op": "set_input", "value": '"read_available_attributes"', "taskname": taskname}))).read()
  107. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  108. while not available_attrs:
  109. time.sleep(0.1)
  110. return available_attrs.pop(0)
  111. def read_attribute(self, name, attr):
  112. urlopen(Request(address, urlencode({"op": "set_input", "value": '"read_attribute"', "taskname": taskname}))).read()
  113. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  114. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % attr, "taskname": taskname}))).read()
  115. while not attribute:
  116. time.sleep(0.1)
  117. return attribute.pop(0)
  118. def set_attribute(self, name, attr, value):
  119. urlopen(Request(address, urlencode({"op": "set_input", "value": '"set_attribute"', "taskname": taskname}))).read()
  120. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % name, "taskname": taskname}))).read()
  121. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % attr, "taskname": taskname}))).read()
  122. urlopen(Request(address, urlencode({"op": "set_input", "value": json.dumps(value), "taskname": taskname}))).read()
  123. def instantiate_block(self, name, block_type):
  124. urlopen(Request(address, urlencode({"op": "set_input", "value": '"instantiate_node"', "taskname": taskname}))).read()
  125. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (block_type), "taskname": taskname}))).read()
  126. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  127. def instantiate_link(self, name, link_type, source, target):
  128. urlopen(Request(address, urlencode({"op": "set_input", "value": '"instantiate_association"', "taskname": taskname}))).read()
  129. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (link_type), "taskname": taskname}))).read()
  130. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (name), "taskname": taskname}))).read()
  131. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (source), "taskname": taskname}))).read()
  132. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (target), "taskname": taskname}))).read()
  133. def simulate(self):
  134. urlopen(Request(address, urlencode({"op": "set_input", "value": '"simulate"', "taskname": taskname}))).read()
  135. def step(self):
  136. urlopen(Request(address, urlencode({"op": "set_input", "value": '"step"', "taskname": taskname}))).read()
  137. def pause(self):
  138. urlopen(Request(address, urlencode({"op": "set_input", "value": '"pause"', "taskname": taskname}))).read()
  139. def delete(self, block):
  140. urlopen(Request(address, urlencode({"op": "set_input", "value": '"delete_element"', "taskname": taskname}))).read()
  141. urlopen(Request(address, urlencode({"op": "set_input", "value": '"%s"' % (block), "taskname": taskname}))).read()
  142. def lower(value):
  143. return value / JUMP * JUMP
  144. def upper(value):
  145. return (value / JUMP + 1) * JUMP
  146. def avg(a, b):
  147. return float(a + b) / 2
  148. class InterfaceCore():
  149. mode = ""
  150. drawn = set()
  151. refs = dict()
  152. #mv = MvLayer(address)
  153. mv = FakeLayer(address)
  154. def set_mode(self, mode):
  155. self.mode = mode
  156. def delete(self, x, y):
  157. lname = self.find((x, y))
  158. self.mv.delete(lname)
  159. for i in self.refs[lname]:
  160. canvas.delete(i)
  161. del self.refs[lname]
  162. self.drawn = set([e for e in self.drawn if e[4] != lname])
  163. def clicked(self, event):
  164. if self.find((event.x, event.y)):
  165. # Something already there, so don't add, but modify
  166. lname = self.find((event.x, event.y))
  167. attrs = self.mv.read_available_attributes(lname)
  168. print("Managing " + str(attrs))
  169. if not attrs:
  170. print("No attrs to manage!")
  171. for attr, t in attrs:
  172. print("Reading data from " + str(attr))
  173. old_value = self.mv.read_attribute(lname, attr)
  174. if old_value == "None":
  175. old_value = None
  176. new_value = tkSimpleDialog.askstring("Attribute modification", attr, initialvalue=old_value)
  177. if t == "Float":
  178. new_value = float(new_value)
  179. elif t == "String":
  180. new_value = str(new_value)
  181. else:
  182. print("Got unknown type: " + str(t))
  183. self.mv.set_attribute(lname, attr, new_value)
  184. elif self.mode not in ["AdditionBlock", "NegatorBlock", "ConstantBlock", "MultiplyBlock", "ConstantBlock", "InverseBlock", "DelayBlock", "IntegratorBlock", "DerivatorBlock", "ProbeBlock"]:
  185. print("Cannot create something not guaranteed to be block type!")
  186. else:
  187. global name
  188. x = event.x
  189. y = event.y
  190. self.mv.instantiate_block(str(name), self.mode)
  191. r = canvas.create_rectangle(lower(x), lower(y), upper(x), upper(y), fill="white")
  192. t = canvas.create_image(avg(lower(x), upper(x)), avg(lower(y), upper(y)), image=photos[self.mode])
  193. b = (lower(x), lower(y), upper(x), upper(y), str(name))
  194. self.drawn.add(b)
  195. self.refs[str(name)] = [r, t]
  196. name += 1
  197. def find(self, location):
  198. x, y = location
  199. for e in self.drawn:
  200. if (e[0] <= x and
  201. e[1] <= y and
  202. e[2] >= x and
  203. e[3] >= y):
  204. return e[4]
  205. print("Found nothing at that location!")
  206. return []
  207. def draw(self, start, end):
  208. source = self.find(start)
  209. target = self.find(end)
  210. print("Connect from %s to %s" % (source, target))
  211. if source and target:
  212. if self.mode not in ["Link", "InitialCondition"]:
  213. print("Cannot create something not guaranteed to be link type!")
  214. elif source == target:
  215. print("Cannot create link to self")
  216. else:
  217. global name
  218. self.mv.instantiate_link(str(name), self.mode, source, target)
  219. self.refs[str(name)] = [canvas.create_line(start[0], start[1], end[0], end[1], fill="black" if self.mode == "Link" else "red", arrow=LAST)]
  220. name += 1
  221. core = InterfaceCore()
  222. def clicked(event):
  223. core.clicked(event)
  224. def draw(event):
  225. global start_location
  226. start_location = (event.x, event.y)
  227. def release(event):
  228. core.draw(start_location, (event.x, event.y))
  229. def simulate():
  230. core.mv.simulate()
  231. def step():
  232. core.mv.step()
  233. def pause():
  234. core.mv.pause()
  235. def addition():
  236. core.set_mode("AdditionBlock")
  237. def negation():
  238. core.set_mode("NegatorBlock")
  239. def link():
  240. core.set_mode("Link")
  241. def multiply():
  242. core.set_mode("MultiplyBlock")
  243. def constant():
  244. core.set_mode("ConstantBlock")
  245. def inverse():
  246. core.set_mode("InverseBlock")
  247. def ic():
  248. core.set_mode("InitialCondition")
  249. def delay():
  250. core.set_mode("DelayBlock")
  251. def derivator():
  252. core.set_mode("DerivatorBlock")
  253. def integrator():
  254. core.set_mode("IntegratorBlock")
  255. def delete(event):
  256. core.delete(event.x, event.y)
  257. def probe():
  258. core.set_mode("ProbeBlock")
  259. images = {
  260. "ConstantBlock": Image.open("figures/constant.png"),
  261. "AdditionBlock": Image.open("figures/summation.png"),
  262. "NegatorBlock": Image.open("figures/negation.png"),
  263. "MultiplyBlock": Image.open("figures/multiply.png"),
  264. "InverseBlock": Image.open("figures/inverse.png"),
  265. "DerivatorBlock": Image.open("figures/derivative.png"),
  266. "IntegratorBlock": Image.open("figures/integrator.png"),
  267. "DelayBlock": Image.open("figures/delay.png"),
  268. "ProbeBlock": Image.open("figures/probe.png"),
  269. }
  270. hsize = 20
  271. for k, v in images.items():
  272. old_vsize, old_hsize = v.size
  273. vsize = int(old_vsize * hsize / float(old_hsize))
  274. images[k] = v.resize((vsize, hsize), Image.ANTIALIAS)
  275. photos = {k: ImageTk.PhotoImage(v) for k, v in images.items()}
  276. bound_functions = {
  277. "ConstantBlock": constant,
  278. "AdditionBlock": addition,
  279. "MultiplyBlock": multiply,
  280. "NegatorBlock": negation,
  281. "InverseBlock": inverse,
  282. "DerivatorBlock": derivator,
  283. "IntegratorBlock": integrator,
  284. "DelayBlock": delay,
  285. "ProbeBlock": probe,
  286. }
  287. buttons = []
  288. for k, v in bound_functions.items():
  289. buttons.append(Button(root, image=photos[k], command=v))
  290. buttons.extend([
  291. Button(root, text="Link", command=link, bg="blue"),
  292. Button(root, text="InitialCondition", command=ic, bg="blue"),
  293. Button(root, text="SIM", command=simulate, bg="green"),
  294. Button(root, text="STEP", command=step, bg="green"),
  295. Button(root, text="PAUSE", command=pause, bg="green"),
  296. ])
  297. for i, b in enumerate(buttons):
  298. b.grid(row=0, column=i)
  299. canvas.grid(row=1,column=0,columnspan=len(buttons))
  300. core.canvas = canvas
  301. for i in range(JUMP, MAX_HEIGHT, JUMP):
  302. canvas.create_line(0, i, MAX_HEIGHT, i, fill="grey")
  303. for i in range(JUMP, MAX_WIDTH, JUMP):
  304. canvas.create_line(i, 0, i, MAX_WIDTH, fill="grey")
  305. canvas.bind("<Button-1>", clicked)
  306. canvas.bind("<Button-2>", delete)
  307. canvas.bind("<Button-3>", draw)
  308. canvas.bind("<ButtonRelease-3>", release)
  309. visual = Toplevel(root)
  310. probes = {}
  311. values = {}
  312. # Example:
  313. # simulation = [(1, {"a": 1, "b": 2}), (2, {"a": 3}), (3, {"a": 4, "b": 6})]
  314. # Class from StackOverflow
  315. class VerticalScrolledFrame(Frame):
  316. """A pure Tkinter scrollable frame that actually works!
  317. * Use the 'interior' attribute to place widgets inside the scrollable frame
  318. * Construct and pack/place/grid normally
  319. * This frame only allows vertical scrolling
  320. """
  321. def __init__(self, parent, *args, **kw):
  322. Frame.__init__(self, parent, *args, **kw)
  323. # create a canvas object and a vertical scrollbar for scrolling it
  324. vscrollbar = Scrollbar(self, orient=VERTICAL)
  325. vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
  326. canvas = Canvas(self, bd=0, highlightthickness=0,
  327. yscrollcommand=vscrollbar.set)
  328. canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
  329. vscrollbar.config(command=canvas.yview)
  330. # reset the view
  331. canvas.xview_moveto(0)
  332. canvas.yview_moveto(0)
  333. # create a frame inside the canvas which will be scrolled with it
  334. self.interior = interior = Frame(canvas)
  335. interior_id = canvas.create_window(0, 0, window=interior,
  336. anchor=NW)
  337. # track changes to the canvas and frame width and sync them,
  338. # also updating the scrollbar
  339. def _configure_interior(event):
  340. # update the scrollbars to match the size of the inner frame
  341. size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
  342. canvas.config(scrollregion="0 0 %s %s" % size)
  343. if interior.winfo_reqwidth() != canvas.winfo_width():
  344. # update the canvas's width to fit the inner frame
  345. canvas.config(width=interior.winfo_reqwidth())
  346. interior.bind('<Configure>', _configure_interior)
  347. def _configure_canvas(event):
  348. if interior.winfo_reqwidth() != canvas.winfo_width():
  349. # update the inner frame's width to fill the canvas
  350. canvas.itemconfigure(interior_id, width=canvas.winfo_width())
  351. canvas.bind('<Configure>', _configure_canvas)
  352. frame = VerticalScrolledFrame(visual)
  353. frame.pack(fill=BOTH,expand=True)
  354. def update_graphs():
  355. while simulation:
  356. t, results = simulation.pop(0)
  357. for k, v in results.items():
  358. if k in probes:
  359. fcanvas, a = probes[k]
  360. else:
  361. f = Figure()
  362. a = f.add_subplot(111)
  363. a.plot([], [])
  364. print(k)
  365. f.suptitle(k)
  366. fcanvas = FigureCanvasTkAgg(f, frame.interior)
  367. fcanvas.show()
  368. fcanvas.get_tk_widget().pack()
  369. probes[k] = (fcanvas, a)
  370. values[k] = ([], [])
  371. values[k][0].append(t)
  372. values[k][1].append(v)
  373. a.clear()
  374. a.plot(values[k][0], values[k][1], linestyle="none", marker="o")
  375. fcanvas.draw()
  376. root.after(100, update_graphs)
  377. root.after(100, update_graphs)
  378. root.mainloop()