exm_mainwindow.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. from PyQt5.QtWidgets import QMainWindow, QAction, QActionGroup, QGraphicsItem, QGraphicsView,\
  2. QTableWidgetItem, QInputDialog
  3. from PyQt5.QtGui import QIcon
  4. from PyQt5.QtCore import QStateMachine, QState, Qt
  5. from PyQt5.Qt import QApplication
  6. from sketchUI.ui import Ui_MainWindow
  7. from sketchUI.exm_scene import SketchScene, Mode
  8. from sketchUI import mvops
  9. from sketchUI.graphics_node_item import GraphicsNodeItem
  10. from sketchUI.graphics_edge_item import GraphicsEdgeItem
  11. from wrappers.modelverse import element_list_nice
  12. import commons
  13. from evolution.node_ops import NodeDelete, NodeAdd
  14. from evolution.attribute_ops import AttributeDelete
  15. class EXMMainWindow(QMainWindow, Ui_MainWindow):
  16. def __init__(self, model):
  17. QMainWindow.__init__(self)
  18. self.setupUi(self)
  19. self.setWindowTitle(model)
  20. self.menuActionVerify.setDisabled(True)
  21. self._cur_model = model
  22. self._scene = SketchScene(model, self)
  23. self._scene.setSceneRect(0, 0, 200, 200)
  24. self.graphicsView.setScene(self._scene)
  25. self._scene.set_mode(Mode.SELECT)
  26. self.populate_types()
  27. self.listWidget.itemDoubleClicked.connect(self._on_list_item_clicked)
  28. self._setup_toolbar()
  29. self._setup_statemachine()
  30. self.menuActionDeleteModel.triggered.connect(self._on_delete_model_clicked)
  31. # load model
  32. self._load_model()
  33. # setup log viewer
  34. self.plainTextEdit.setReadOnly(True)
  35. # setup table view for attributes
  36. self.tableWidget.setColumnCount(2)
  37. self.tableWidget.setHorizontalHeaderLabels(["Key", "Value"])
  38. self.tableWidget.horizontalHeader().setStretchLastSection(True)
  39. self.tableWidget.itemChanged.connect(self._on_attribute_edited)
  40. # lastly, run state machine
  41. self._statemachine.start()
  42. def populate_types(self):
  43. # sync example model types to list widget
  44. self.listWidget.clear()
  45. self.listWidget.addItems(commons.get_available_types())
  46. def _setup_toolbar(self):
  47. # create action buttons on toolbar
  48. self.select_action = QAction("Select", self)
  49. self.select_action.setIcon(QIcon("sketchUI/icons/select.png"))
  50. self.select_action.setCheckable(True)
  51. self.select_action.setChecked(True)
  52. self.connect_action = QAction("Connect", self)
  53. self.connect_action.setIcon(QIcon("sketchUI/icons/connect.png"))
  54. self.connect_action.setCheckable(True)
  55. self.line_action = QAction("Draw line", self)
  56. self.line_action.setIcon(QIcon("sketchUI/icons/line.png"))
  57. self.line_action.setCheckable(True)
  58. self.rect_action = QAction("Draw Rectangle", self)
  59. self.rect_action.setIcon(QIcon("sketchUI/icons/rect.png"))
  60. self.rect_action.setCheckable(True)
  61. self.circle_action = QAction("Draw Circle", self)
  62. self.circle_action.setIcon(QIcon("sketchUI/icons/circle.png"))
  63. self.circle_action.setCheckable(True)
  64. self.pen_action = QAction("Pen", self)
  65. self.pen_action.setIcon(QIcon("sketchUI/icons/pen.svg"))
  66. self.pen_action.setCheckable(True)
  67. self.pen_action.setEnabled(False)
  68. action_group = QActionGroup(self)
  69. action_group.setExclusive(True)
  70. action_group.addAction(self.select_action)
  71. action_group.addAction(self.connect_action)
  72. action_group.addAction(self.line_action)
  73. action_group.addAction(self.rect_action)
  74. action_group.addAction(self.circle_action)
  75. action_group.addAction(self.pen_action)
  76. for item in action_group.actions():
  77. self.toolBar.addAction(item)
  78. def _setup_statemachine(self):
  79. self._statemachine = QStateMachine()
  80. state_select = QState()
  81. state_connect = QState()
  82. state_draw_line = QState()
  83. state_draw_rect = QState()
  84. state_draw_circle = QState()
  85. state_draw_free = QState()
  86. # from select
  87. state_select.addTransition(self.select_action.triggered, state_select)
  88. state_select.addTransition(self.connect_action.triggered, state_connect)
  89. state_select.addTransition(self.line_action.triggered, state_draw_line)
  90. state_select.addTransition(self.rect_action.triggered, state_draw_rect)
  91. state_select.addTransition(self.circle_action.triggered, state_draw_circle)
  92. state_select.addTransition(self.pen_action.triggered, state_draw_free)
  93. state_select.entered.connect(self._state_select_entered)
  94. # from connect
  95. state_connect.addTransition(self.connect_action.triggered, state_connect)
  96. state_connect.addTransition(self.select_action.triggered, state_select)
  97. state_connect.addTransition(self.line_action.triggered, state_draw_line)
  98. state_connect.addTransition(self.rect_action.triggered, state_draw_rect)
  99. state_connect.addTransition(self.circle_action.triggered, state_draw_circle)
  100. state_connect.addTransition(self.pen_action.triggered, state_draw_free)
  101. state_connect.entered.connect(self._state_connect_entered)
  102. # from rectangle
  103. state_draw_rect.addTransition(self.rect_action.triggered, state_draw_rect)
  104. state_draw_rect.addTransition(self.connect_action.triggered, state_connect)
  105. state_draw_rect.addTransition(self.select_action.triggered, state_select)
  106. state_draw_rect.addTransition(self.line_action.triggered, state_draw_line)
  107. state_draw_rect.addTransition(self.circle_action.triggered, state_draw_circle)
  108. state_draw_rect.addTransition(self.pen_action.triggered, state_draw_free)
  109. state_draw_rect.entered.connect(self._state_draw_rect_entered)
  110. # from line
  111. state_draw_line.addTransition(self.line_action.triggered, state_draw_line)
  112. state_draw_line.addTransition(self.rect_action.triggered, state_draw_rect)
  113. state_draw_line.addTransition(self.connect_action.triggered, state_connect)
  114. state_draw_line.addTransition(self.select_action.triggered, state_select)
  115. state_draw_line.addTransition(self.circle_action.triggered, state_draw_circle)
  116. state_draw_line.addTransition(self.pen_action.triggered, state_draw_free)
  117. state_draw_line.entered.connect(self._state_draw_line_entered)
  118. # from circle
  119. state_draw_circle.addTransition(self.circle_action.triggered, state_draw_circle)
  120. state_draw_circle.addTransition(self.line_action.triggered, state_draw_line)
  121. state_draw_circle.addTransition(self.rect_action.triggered, state_draw_rect)
  122. state_draw_circle.addTransition(self.connect_action.triggered, state_connect)
  123. state_draw_circle.addTransition(self.select_action.triggered, state_select)
  124. state_draw_circle.addTransition(self.pen_action.triggered, state_draw_free)
  125. state_draw_circle.entered.connect(self._state_draw_circle_entered)
  126. # from freehand
  127. state_draw_free.addTransition(self.pen_action.triggered, state_draw_free)
  128. state_draw_free.addTransition(self.circle_action.triggered, state_draw_circle)
  129. state_draw_free.addTransition(self.line_action.triggered, state_draw_line)
  130. state_draw_free.addTransition(self.rect_action.triggered, state_draw_rect)
  131. state_draw_free.addTransition(self.connect_action.triggered, state_connect)
  132. state_draw_free.addTransition(self.select_action.triggered, state_select)
  133. state_draw_free.entered.connect(self._state_draw_free_entered)
  134. self._statemachine.addState(state_select)
  135. self._statemachine.addState(state_connect)
  136. self._statemachine.addState(state_draw_rect)
  137. self._statemachine.addState(state_draw_line)
  138. self._statemachine.addState(state_draw_circle)
  139. self._statemachine.addState(state_draw_free)
  140. self._statemachine.setInitialState(state_select)
  141. def _state_select_entered(self):
  142. self._scene.set_mode(Mode.SELECT)
  143. self._make_items_movable(True)
  144. self._enable_box_select(True)
  145. def _state_connect_entered(self):
  146. self._scene.set_mode(Mode.CONNECT)
  147. self._make_items_movable(False)
  148. self._enable_box_select(False)
  149. def _state_draw_rect_entered(self):
  150. self._scene.set_mode(Mode.RECT)
  151. self._make_items_movable(False)
  152. self._enable_box_select(False)
  153. def _state_draw_line_entered(self):
  154. self._scene.set_mode(Mode.LINE)
  155. self._make_items_movable(False)
  156. self._enable_box_select(False)
  157. def _state_draw_circle_entered(self):
  158. self._scene.set_mode(Mode.CIRCLE)
  159. self._make_items_movable(False)
  160. self._enable_box_select(False)
  161. def _state_draw_free_entered(self):
  162. self._scene.set_mode(Mode.FREE)
  163. self._make_items_movable(False)
  164. self._enable_box_select(False)
  165. def _make_items_movable(self, movable):
  166. for item in self._scene.items():
  167. if isinstance(item, GraphicsEdgeItem):
  168. # edges are never movable, but selectable
  169. item.setFlag(QGraphicsItem.ItemIsMovable, False)
  170. item.setFlag(QGraphicsItem.ItemIsSelectable, True)
  171. else:
  172. item.setFlag(QGraphicsItem.ItemIsMovable, movable)
  173. item.setFlag(QGraphicsItem.ItemIsSelectable, movable)
  174. def _enable_box_select(self, enable):
  175. if enable:
  176. self.graphicsView.setDragMode(QGraphicsView.RubberBandDrag)
  177. else:
  178. self.graphicsView.setDragMode(QGraphicsView.NoDrag)
  179. def _load_model(self):
  180. model = element_list_nice(self._cur_model)
  181. if not model:
  182. # empty
  183. return
  184. self._scene.clear()
  185. x_pos = -150
  186. y_pos = 70
  187. for item in model:
  188. typ = item["type"]
  189. if typ == "Node":
  190. # first, draw all nodes
  191. node_type = item["typeID"]
  192. self._add_node_to_scene(item["id"], node_type, x=x_pos, y=y_pos)
  193. x_pos += 80
  194. y_pos *= -1
  195. for item in model:
  196. typ = item["type"]
  197. if typ == "Edge":
  198. target = item["__target"]
  199. src = item["__source"]
  200. self._add_edge_to_scene(src, target, item["id"])
  201. def _add_node_to_scene(self, node_id, node_type, x=0, y=0):
  202. consyn = mvops.get_consyn_of(node_type)
  203. item = GraphicsNodeItem(node_id, node_type, consyn)
  204. item.setPos(x, y)
  205. item.setFlag(QGraphicsItem.ItemIsMovable, True)
  206. item.setFlag(QGraphicsItem.ItemIsSelectable, True)
  207. self._scene.addItem(item)
  208. def _add_edge_to_scene(self, from_id, to_id, edge_id):
  209. from_item = None
  210. to_item = None
  211. for item in self._scene.items():
  212. try:
  213. node_id = item.node_id
  214. except AttributeError:
  215. # no node item, continue
  216. continue
  217. if node_id == from_id:
  218. from_item = item
  219. continue
  220. if node_id == to_id:
  221. to_item = item
  222. self._scene.draw_edge(from_item, to_item, edge_id=edge_id)
  223. def _on_list_item_clicked(self, event):
  224. # add node to model
  225. self.plainTextEdit.appendPlainText("Adding node of type {} to model".format(event.text()))
  226. add_handler = NodeAdd()
  227. # ask if global or local add node
  228. scope, ok = QInputDialog.getItem(self.parent(), "Select scope", "Scope", ["Local", "Global"])
  229. if not ok:
  230. return
  231. if scope == "Global":
  232. add_handler.execute(self._cur_model, event.text(), local=False)
  233. else:
  234. add_handler.execute(self._cur_model, event.text(), local=True)
  235. # repair any possibly invalidated instance models
  236. add_handler.repair()
  237. # render node by reloading the whole model (since we can not get the node id of the newly added model)
  238. self._load_model()
  239. def _on_attribute_edited(self, item):
  240. # type: (QTableWidgetItem) -> None
  241. """
  242. An attribute value was edited.
  243. If the new entered value is empty, delete the attribute.
  244. """
  245. row = self.tableWidget.row(item)
  246. attr_key = self.tableWidget.item(row, 0).text()
  247. attr_val = self.tableWidget.item(row, 1).text()
  248. node = self._scene.selectedItems()[0]
  249. if not attr_val:
  250. # ask if global or local delete
  251. scope, ok = QInputDialog.getItem(self.parent(), "Select scope", "Scope", ["Local", "Global"])
  252. if not ok:
  253. return
  254. del_handler = AttributeDelete()
  255. self.plainTextEdit.appendPlainText("Deleting attribute {}".format(attr_key))
  256. if scope == "Global":
  257. del_handler.execute(self._cur_model, node.node_id, attr_key, local=False)
  258. else:
  259. del_handler.execute(self._cur_model, node.node_id, attr_key, local=True, check_if_last=True)
  260. # update view
  261. self.tableWidget.removeRow(row)
  262. else:
  263. self.plainTextEdit.appendPlainText("Updating value of attribute {} to {}".format(attr_key, attr_val))
  264. mvops.update_attribute_val(self._cur_model, node.node_id, attr_key, attr_val)
  265. def add_new_attribute(self, key, val="unknown"):
  266. """
  267. Adds a new attribute to the view with key "key" and optional val.
  268. """
  269. selected_node = self._scene.selectedItems()[0]
  270. self.plainTextEdit.appendPlainText("Adding new attribute with key {} to node {}".format(key, selected_node.get_type()))
  271. self.tableWidget.blockSignals(True)
  272. table_item_key = QTableWidgetItem(key)
  273. table_item_key.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
  274. table_item_val = QTableWidgetItem(val)
  275. cur_row_cnt = self.tableWidget.rowCount()
  276. self.tableWidget.insertRow(cur_row_cnt)
  277. self.tableWidget.setItem(cur_row_cnt, 0, table_item_key)
  278. self.tableWidget.setItem(cur_row_cnt, 1, table_item_val)
  279. self.tableWidget.blockSignals(False)
  280. def _on_delete_model_clicked(self, event):
  281. # delete the open example model by iteratively deleting all nodes
  282. self.plainTextEdit.appendPlainText("Deleting model ...")
  283. self.plainTextEdit.repaint()
  284. QApplication.setOverrideCursor(Qt.WaitCursor)
  285. delhander = NodeDelete()
  286. for item in self._scene.items():
  287. if not isinstance(item, GraphicsNodeItem):
  288. continue
  289. self.plainTextEdit.appendPlainText("Deleting node {}".format(item.node_id))
  290. self.plainTextEdit.repaint()
  291. delhander.execute(self._cur_model, item.node_id, local=True, check_if_last=True)
  292. # delete the empty model from the Modelverse
  293. if mvops.delete_example_model(self._cur_model):
  294. self.close()
  295. else:
  296. self.plainTextEdit.appendPlainText("Error: Delete failed")
  297. QApplication.restoreOverrideCursor()