im_scene.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. from enum import Enum
  2. from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QTableWidgetItem, QInputDialog
  3. from PyQt5.Qt import Qt, QTransform, QApplication
  4. from sketchUI.graphics_edge_item import GraphicsEdgeItem
  5. from sketchUI.graphics_node_item import GraphicsNodeItem
  6. from sketchUI import mvops
  7. import commons
  8. class Mode(Enum):
  9. SELECT = 0
  10. CONNECT = 1
  11. class CustomScene(QGraphicsScene):
  12. def __init__(self, model, parent):
  13. QGraphicsScene.__init__(self)
  14. self._mode = None # set by mainwindow at start
  15. self.connect_from_item = None # store the item to draw the connecting line from
  16. self._cur_model = model # the mv model we operate on
  17. self._parent = parent # mainwindow parent
  18. def set_mode(self, mode):
  19. self._mode = mode
  20. def mousePressEvent(self, event):
  21. if event.button() == Qt.LeftButton and self._mode == Mode.CONNECT:
  22. item = self.itemAt(event.scenePos(), QTransform())
  23. if not item:
  24. return
  25. # store the potential item to be connected from
  26. self.connect_from_item = item
  27. if event.button() == Qt.LeftButton and self._mode == Mode.SELECT:
  28. # load attributes for selected node
  29. item = self.itemAt(event.scenePos(), QTransform())
  30. if not item:
  31. return
  32. elif isinstance(item, GraphicsNodeItem):
  33. self._parent.plainTextEdit.appendPlainText("Selected node {}:{}".format(item.node_id, item.get_type()))
  34. self.highlight_node(item.node_id, Qt.blue)
  35. # load node attributes and display them in table
  36. self._parent.tableWidget.setRowCount(0)
  37. self._parent.tableWidget.blockSignals(True)
  38. attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
  39. for attr in attrs:
  40. table_item_key = QTableWidgetItem(attr.key)
  41. table_item_key.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
  42. table_item_val = QTableWidgetItem(attr.val)
  43. cur_row_cnt = self._parent.tableWidget.rowCount()
  44. self._parent.tableWidget.insertRow(cur_row_cnt)
  45. self._parent.tableWidget.setItem(cur_row_cnt, 0, table_item_key)
  46. self._parent.tableWidget.setItem(cur_row_cnt, 1, table_item_val)
  47. self._parent.tableWidget.blockSignals(False)
  48. elif isinstance(item, GraphicsEdgeItem):
  49. self._parent.plainTextEdit.appendPlainText("Selected edge")
  50. else:
  51. pass
  52. QGraphicsScene.mousePressEvent(self, event)
  53. def mouseReleaseEvent(self, event):
  54. if event.button() == Qt.LeftButton and self._mode == Mode.CONNECT:
  55. item = self.itemAt(event.scenePos(), QTransform())
  56. if not item:
  57. return
  58. if item == self.connect_from_item:
  59. self._parent.plainTextEdit.appendPlainText("Error: Cannot connect same elements")
  60. return
  61. self.draw_edge(self.connect_from_item, item, is_new=True)
  62. if event.button() == Qt.LeftButton and self._mode == Mode.SELECT:
  63. item = self.itemAt(event.scenePos(), QTransform())
  64. if not item:
  65. return
  66. for item in self.items():
  67. if isinstance(item, GraphicsEdgeItem):
  68. item.redraw()
  69. QGraphicsScene.mouseReleaseEvent(self, event)
  70. def keyPressEvent(self, event):
  71. if not self._mode == Mode.SELECT:
  72. return
  73. # del deletes elements
  74. if event.key() == Qt.Key_Delete:
  75. self._handle_keypress_delete(self.selectedItems())
  76. elif event.key() == Qt.Key_A:
  77. self._handle_keypress_attribute(self.selectedItems())
  78. else:
  79. pass
  80. QGraphicsScene.keyPressEvent(self, event)
  81. def draw_edge(self, from_item, to_item, is_new, edge_id=None):
  82. # type: (GraphicsNodeItem, GraphicsNodeItem, bool, str) -> None
  83. """
  84. Draw edge on screen between the node items "from_item" and "to_item".
  85. If is_new, additionally instantiate the edge in the model (if supported).
  86. """
  87. from_type = from_item.get_type()
  88. to_type = to_item.get_type()
  89. if is_new:
  90. QApplication.setOverrideCursor(Qt.WaitCursor)
  91. if not commons.is_edge_supported(from_type, to_type):
  92. self._parent.plainTextEdit.appendPlainText("Error: Edge not supported between types {} and {}".format(from_type, to_type))
  93. QApplication.restoreOverrideCursor()
  94. return
  95. line = GraphicsEdgeItem(from_item, to_item, edge_id)
  96. line.setFlag(QGraphicsItem.ItemIsMovable, False)
  97. line.setFlag(QGraphicsItem.ItemIsSelectable, False)
  98. self.addItem(line)
  99. line.redraw()
  100. if is_new:
  101. edge_id = mvops.add_edge(self._cur_model, from_item.node_id, to_item.node_id)
  102. line.edge_id = edge_id
  103. self._parent.plainTextEdit.appendPlainText("Added edge between {} and {} to model".format(from_type, to_type))
  104. QApplication.restoreOverrideCursor()
  105. def _handle_keypress_delete(self, selected):
  106. if not len(selected) == 1:
  107. return
  108. item = selected[0]
  109. if isinstance(item, GraphicsNodeItem):
  110. # operation valid?
  111. if commons.is_type_mandatory(item.get_type()) and commons.count_occurences(item.get_type(), self._cur_model) == 1:
  112. self._parent.plainTextEdit.appendPlainText("Error: Type {} mandatory".format(item.get_type()))
  113. return
  114. # delete node in model (also deletes edges connected to it in model)
  115. self._parent.plainTextEdit.appendPlainText("Deleting node of type {}".format(item.get_type()))
  116. mvops.delete_node(self._cur_model, item.node_id)
  117. # in view, delete edges that were connected to this node as well
  118. # modelverse does this on its own so do not delete edges explicitly in model
  119. for edge in self.items():
  120. if not isinstance(edge, GraphicsEdgeItem):
  121. continue
  122. if edge.from_item.node_id == item.node_id or edge.to_item.node_id == item.node_id:
  123. self.removeItem(edge)
  124. self.removeItem(item)
  125. if isinstance(item, GraphicsEdgeItem):
  126. self._parent.plainTextEdit.appendPlainText("Deleting edge")
  127. mvops.delete_edge(self._cur_model, item.edge_id)
  128. self.removeItem(item)
  129. def _handle_keypress_attribute(self, selected):
  130. if not len(selected) == 1:
  131. return
  132. item = selected[0]
  133. if not isinstance(item, GraphicsNodeItem):
  134. return
  135. # ask user for key value
  136. key, ok = QInputDialog.getText(self._parent, "New attribute", "Key value")
  137. if not ok or not key:
  138. return
  139. # is operation supported?
  140. QApplication.setOverrideCursor(Qt.WaitCursor)
  141. self._parent.plainTextEdit.appendPlainText("Checking if attributing node is allowed ...".format(item.get_type()))
  142. if not commons.is_attribute_valid(item.get_type(), key):
  143. self._parent.plainTextEdit.appendPlainText("Error: Attribute {} not valid for type {}".format(key, item.get_type()))
  144. QApplication.restoreOverrideCursor()
  145. return
  146. QApplication.restoreOverrideCursor()
  147. self._parent.plainTextEdit.appendPlainText("Yes")
  148. # check if key value already used for this node
  149. attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
  150. for attr in attrs:
  151. if attr.key == key:
  152. self._parent.plainTextEdit.appendPlainText("Error: Already such a key for the node: {}".format(key))
  153. return
  154. self._parent.add_new_attribute(key)
  155. def highlight_node(self, node_id, color):
  156. for item in self.items():
  157. if not isinstance(item, GraphicsNodeItem):
  158. continue
  159. if item.node_id == node_id:
  160. item.set_highlighted(True, color)
  161. else:
  162. item.set_highlighted(False, color)
  163. item.update(item.boundingRect())