exm_mainwindow.py 13 KB

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