exm_mainwindow.py 14 KB

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