123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- from Tkinter import *
- from PIL import Image, ImageTk
- import tkSimpleDialog
- import urllib
- import urllib2
- import json
- import time
- sys.path.append("interface/HUTN")
- sys.path.append("wrappers")
- from modelverse import *
- address = "http://127.0.0.1:8001"
- taskname = str(random.random())
- MAX_WIDTH = 500
- MAX_HEIGHT = 500
- MM_RENDERED = "MM_rendered_graphical"
- def rendered_formula(model_name, mapper_name):
- return "__RENDERED_%s__%s" % (model_name, mapper_name)
- init()
- login("admin", "admin")
- root = Tk()
- class Window(object):
- def __init__(self):
- self.canvas = Canvas(root, width=MAX_WIDTH, height=MAX_HEIGHT, bg="white")
- self.selected_model = StringVar(root)
- self.selected_mapper = StringVar(root)
- mm_buttons = []
- available_models = model_list()
- available_mappers = []
- self.model_options = OptionMenu(root, self.selected_model, *[i[0] for i in available_models])
- self.mapper_options = OptionMenu(root, self.selected_mapper, "", *available_mappers)
- self.reset_optionmenu(self.mapper_options, [], self.selected_mapper)
- bound_functions = {
- "open": self.open_model,
- "render": self.render_model,
- "instantiate": self.instantiate_model,
- "verify": self.verify_model,
- }
- buttons = []
- buttons.append(self.model_options)
- buttons.append(self.mapper_options)
- self.current_element = None
- for k, v in bound_functions.items():
- buttons.append(Button(root, text=k, command=v))
- for i, b in enumerate(buttons):
- b.grid(row=0, column=i, sticky="WENS")
- self.mm_buttons = []
- self.CS_to_AS = {}
- self.AS_to_CS = {}
- self.AS_to_TK = {}
- self.TK_to_CS = {}
- self.canvas.grid(row=2,column=0,columnspan=len(buttons))
- self.canvas.bind("<Button-1>", self.left_clicked)
- self.canvas.bind("<B1-Motion>", self.left_drag)
- self.canvas.bind("<ButtonRelease-1>", self.left_released)
- self.canvas.bind("<Button-2>", self.middle_clicked)
- self.canvas.bind("<Button-3>", self.right_clicked)
- def find_AS(self, x, y):
- # Find the group at this location
- def in_bbox(coord, bbox):
- if bbox[0] <= coord[0] <= bbox[2] and bbox[1] <= coord[1] <= bbox[3]:
- return True
- for elem in self.canvas.find_all():
- if in_bbox((x, y), self.canvas.bbox(elem)):
- return self.CS_to_AS[self.TK_to_CS[elem]]
- def move_group(self, elem, coords):
- # Change coordinates of all enclosed elements!
- # elem is an AS id of the group
- m_x, m_y = coords
- o_x, o_y = self.previous_coords
- d_x = m_x - o_x
- d_y = m_y - o_y
- for tkinter_obj in self.AS_to_TK[elem]:
- obj_coords = self.canvas.coords(tkinter_obj)
- if len(obj_coords) == 2:
- c_x, c_y = obj_coords
- self.canvas.coords(tkinter_obj, (c_x + d_x, c_y + d_y))
- elif len(obj_coords) == 4:
- c_x, c_y, c_x2, c_y2 = obj_coords
- self.canvas.coords(tkinter_obj, (c_x + d_x, c_y + d_y, c_x2 + d_x, c_y2 + d_y))
- def left_clicked(self, evt):
- # Left click means we select an element for dragging
- x, y = evt.x, evt.y
- self.original_coords = x, y
- self.previous_coords = x, y
- self.current_element = self.find_AS(x, y)
- def left_drag(self, evt):
- # Left drag means we are dragging something, but only if something is selected
- if self.current_element is not None:
- x, y = evt.x, evt.y
- self.move_group(self.current_element, (x, y))
- self.previous_coords = x, y
- def left_released(self, evt):
- # Left click means we select an element for dragging
- if self.current_element is not None:
- x, y = evt.x, evt.y
- d_x = x - self.original_coords[0]
- d_y = y - self.original_coords[1]
- self.move_group(self.current_element, (x, y))
- attrs = read_attrs(rendered_formula(self.selected_model.get(), self.selected_mapper.get()), self.AS_to_CS[self.current_element])
- # Send new values to Modelverse!
- attr_assign(rendered_formula(self.selected_model.get(), self.selected_mapper.get()), self.AS_to_CS[self.current_element], "x", attrs["x"] + d_x)
- attr_assign(rendered_formula(self.selected_model.get(), self.selected_mapper.get()), self.AS_to_CS[self.current_element], "y", attrs["y"] + d_y)
- def middle_clicked(self, evt):
- # Middle clicked means we are modifying attributes of something
- x, y = evt.x, evt.y
- # Read available attrs and their values
- as_id = self.find_AS(x, y)
- attrs = read_attrs(self.selected_model.get(), as_id)
- attrs = {k: json.dumps(attrs[k]) for k in attrs}
- # Prompt for new values
- d = PromptDialog(root, attrs)
- if d.result is not None:
- # Change all attributes that have changed and send to Modelverse
- # TODO
- #TODO think about type checking
- for k, v in d.result.items():
- v = json.loads(v)
- if v is None:
- attr_delete(self.selected_model.get(), as_id, k)
- else:
- attr_assign(self.selected_model.get(), as_id, k, v)
- def visualize(self, model):
- cache = {}
- hierarchy = {}
- parent = {}
- self.canvas.delete("all")
- self.CS_to_AS = {}
- self.AS_to_CS = {}
- self.AS_to_TK = {}
- self.TK_to_CS = {}
- def findXY(element_id):
- x = cache[element_id]["x"]
- y = cache[element_id]["y"]
- while element_id in parent:
- # Has a parent (group), so increment the XY values and keep going
- element_id = parent[element_id]
- x += cache[element_id]["x"]
- y += cache[element_id]["y"]
- return x, y
- def findGroup(element_id):
- try:
- while cache[element_id]["__asid"] is None:
- element_id = parent[element_id]
- return element_id
- except KeyError:
- # No configured __asid found in hierarchy
- print("ERROR")
- return None
- def findASID(element_id):
- return cache[findGroup(element_id)]["__asid"]
- for element in model:
- # Determine type of element
- cache[element["id"]] = element
- t = element["type"]
- if t == "contains":
- # Cache the containment links as well
- parent[element["__target"]] = element["__source"]
- hierarchy.setdefault(element["__source"], []).append(element["__target"])
- # Caches filled, so go over all elements, ignoring groups, and draw them relative to their enclosing groups
- for element in model:
- t = element["type"]
- if t in ["Rectangle", "Ellipse", "Line", "Text", "Figure"]:
- x, y = findXY(element["id"])
- as_ID = findASID(element["id"])
- cs_ID = element["id"]
- group_ID = findGroup(element["id"])
- elif t in ["Group", "contains", "renders"]:
- continue
- else:
- raise Exception(str(element))
- if t == "Rectangle":
- fillColour = element["fillColour"]
- width = element["width"]
- height = element["height"]
- lineWidth = element["lineWidth"]
- lineColour = element["lineColour"]
- tkinter_elem = self.canvas.create_rectangle(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
- elif t == "Ellipse":
- fillColour = element["fillColour"]
- width = element["width"]
- height = element["height"]
- lineWidth = element["lineWidth"]
- lineColour = element["lineColour"]
- tkinter_elem = self.canvas.create_ellipse(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
- elif t == "Line":
- targetX = element["targetX"]
- targetY = element["targetY"]
- lineWidth = element["lineWidth"]
- lineColour = element["lineColour"]
- tkinter_elem = self.canvas.create_line(x, y, targetX, targetY, fill=lineColour, width=lineWidth)
- elif t == "Text":
- lineWidth = element["lineWidth"]
- lineColour = element["lineColour"]
- text = element["text"]
- tkinter_elem = self.canvas.create_text(x, y, fill=lineColour, text=text)
- elif t == "Figure":
- # Fetch associated SVG figure
- raise NotImplementedError()
- self.TK_to_CS[tkinter_elem] = cs_ID
- self.CS_to_AS[cs_ID] = as_ID
- self.AS_to_CS[as_ID] = group_ID
- self.AS_to_TK.setdefault(as_ID, set([])).add(tkinter_elem)
- def reset_optionmenu(self, optionmenu, options, var):
- menu = optionmenu.children["menu"]
- menu.delete(0, "end")
- var.set("")
- for option in options:
- menu.add_command(label=option, command=lambda value=option: var.set(value))
- def read_available_mappers(self, instance_model):
- # Get instance model's type first, as transformations are defined on the type
- models = dict(model_list())
- type_model = models[instance_model]
- mappers = transformation_between(type_model, MM_RENDERED)
- return mappers
- def right_clicked(self, evt):
- element_id = instantiate(self.selected_model.get(), self.current_element)
- elements = self.render_model()
- for f in elements:
- if "__asid" in f:
- if f["__asid"] == element_id:
- attr_assign(rendered_formula(self.selected_model.get(), self.selected_mapper.get()), f["id"], "x", evt.x)
- attr_assign(rendered_formula(self.selected_model.get(), self.selected_mapper.get()), f["id"], "y", evt.y)
- self.render_model()
- def select_element(self, t):
- def set_elem():
- self.current_element = t
- return set_elem
- def render_model(self):
- import json
- rendered = json.loads(model_render(self.selected_model.get(), self.selected_mapper.get()))
- self.visualize(rendered)
- return rendered
- def open_model(self):
- available_mappers = self.read_available_mappers(self.selected_model.get())
- self.reset_optionmenu(self.mapper_options, available_mappers, self.selected_mapper)
- available_types = [i[0] for i in types_full(self.selected_model.get()) if i[1] == "Class"]
- for button in self.mm_buttons:
- button.destroy()
- self.mm_buttons = []
- for i, t in enumerate(available_types):
- self.mm_buttons.append(Button(root, text=t, command=self.select_element(t)))
- self.mm_buttons[-1].grid(row=1, column=i)
- def instantiate_model(self):
- d = PromptDialog(root, ["Name:"])
- if d.result is not None:
- name = str(d.result["Name:"])
- model_add(name, self.selected_model.get())
- self.reset_optionmenu(self.available_models, model_list())
- self.selected_model.set(name)
- self.open_model()
- def verify_model(self):
- print(verify(self.selected_model.get()))
- class PromptDialog(tkSimpleDialog.Dialog):
- def __init__(self, master, query):
- self.query = query
- tkSimpleDialog.Dialog.__init__(self, master)
- def body(self, master):
- self.entries = {}
- for i, q in enumerate(self.query.items()):
- Label(master, text=q[0]).grid(row=i, column=0)
- self.entries[q[0]] = Entry(master)
- if q[1] is not None:
- self.entries[q[0]].insert(END, q[1])
- self.entries[q[0]].grid(row=i, column=1)
- return None
- def apply(self):
- self.result = {i: self.entries[i].get() for i in self.query}
- window = Window()
- root.mainloop()
|