exm_mainwindow.py 13 KB

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