automatascene.py
Go to the documentation of this file.
1 '''
2  Copyright (C) 1997-2017 JDERobot Developers Team
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Library General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, see <http://www.gnu.org/licenses/>.
16 
17  Authors : Okan Asik (asik.okan@gmail.com)
18 
19  '''
20 from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QAction, QMenu
21 from PyQt5.QtCore import Qt, pyqtSignal
22 from PyQt5.QtWidgets import QGraphicsScene, QAction, QMenu
23 from visualstates.core.state import State
24 from visualstates.gui.state.codedialog import CodeDialog
25 from visualstates.gui.state.idtextboxgraphicsitem import IdTextBoxGraphicsItem
26 from visualstates.gui.state.renamediaolog import RenameDialog
27 from visualstates.gui.transition.transitioncodedialog import TransitionCodeDialog
28 from visualstates.gui.transition.transitiontype import TransitionType
29 from visualstates.core.namespace import Namespace
30 from visualstates.core.transition import Transition
31 from visualstates.gui.automata.optype import OpType
32 from visualstates.gui.state.guistate import StateGraphicsItem
33 from visualstates.gui.transition.guitransition import TransitionGraphicsItem
34 
35 
36 class AutomataScene(QGraphicsScene):
37  # signals
38  stateInserted = pyqtSignal('QGraphicsItem')
39  stateRemoved = pyqtSignal('QGraphicsItem')
40  stateImported = pyqtSignal()
41  transitionInserted = pyqtSignal('QGraphicsItem')
42  transitionRemoved = pyqtSignal('QGraphicsItem')
43  stateNameChangedSignal = pyqtSignal('QGraphicsItem')
44  activeStateChanged = pyqtSignal()
45  activeNamespaceChanged = pyqtSignal()
46 
47  def __init__(self, parent=None):
48  super(QGraphicsScene, self).__init__(parent)
49 
50  self.operationType = None
51  self.operationData = None
52 
53  # transition origin and destination
54  self.origin = None
55  self.destination = None
56 
57  self.stateIndex = 0
58  self.transitionIndex = 0
59 
60  self.prevOperationType = None
62 
63  # the active state whose children will be drawn on the graphicsview
64  self.activeState = None
65  self.activeNamespace = None
66 
67  self.createActions()
68 
69  self.selectedState = None
70  self.selectedTransition = None
71  self.contextPosition = None
72 
73  self.copiedState = None
74 
75  def createActions(self):
76 
77  # state actions
78  self.renameStateAction = QAction('Rename', self)
79  self.renameStateAction.triggered.connect(self.renameState)
80 
81  self.stateCodeAction = QAction('Code', self)
82  self.stateCodeAction.triggered.connect(self.editStateCode)
83 
84  self.makeInitialAction = QAction('Make Initial', self)
85  self.makeInitialAction.triggered.connect(self.makeInitial)
86 
87  self.copyStateAction = QAction('Copy', self)
88  self.copyStateAction.triggered.connect(self.copyState)
89 
90  self.removeStateAction = QAction('Remove', self)
91  self.removeStateAction.triggered.connect(self.removeState)
92 
93  self.pasteStateAction = QAction('Paste', self)
94  self.pasteStateAction.triggered.connect(self.pasteState)
95 
96  self.importStateAction = QAction('Import', self)
97  self.importStateAction.triggered.connect(self.importState)
98 
99  # transition actions
100  self.renameTransitionAction = QAction('Rename', self)
101  self.renameTransitionAction.triggered.connect(self.renameTransition)
102 
103  self.transitionCodeAction = QAction('Code', self)
104  self.transitionCodeAction.triggered.connect(self.editTransitionCode)
105 
106  self.removeTransitionAction = QAction('Remove', self)
107  self.removeTransitionAction.triggered.connect(self.removeTransition)
108 
109  def renameState(self):
110  dialog = RenameDialog('Rename', self.selectedState.stateData.name)
111  dialog.move(self.contextPosition)
112  dialog.nameChanged.connect(self.externalStateNameChanged)
113  dialog.exec_()
114 
115  def editStateCode(self, state):
116  dialog = CodeDialog('State Code', self.selectedState.stateData.code)
117  dialog.move(self.contextPosition)
118  dialog.codeChanged.connect(self.stateCodeChanged)
119  dialog.exec_()
120 
121  def makeInitial(self):
122  # there could be only one initial state
123  for child in self.activeState.getChildren():
124  child.setInitial(False)
125  child.getGraphicsItem().setInitial(False)
126 
127  self.selectedState.setInitial(True)
128  self.selectedState.stateData.setInitial(True)
129 
130  def copyState(self):
131  self.copiedState = self.selectedState.stateData.getNewCopy()
132 
133  def pasteState(self):
134  self.copiedState.id = self.getStateIndex()
135  self.copiedState.x = self.currentScenePos.x()
136  self.copiedState.y = self.currentScenePos.y()
137  self.addStateItem(self.copiedState.getGraphicsItem())
138  self.copyState()
139 
140  def removeState(self):
141  self.removeStateItem(self.selectedState)
142 
143  def importState(self):
144  item = self.getParentItem(self.selectedState)
145  if isinstance(item, StateGraphicsItem):
146  self.setActiveState(item.stateData)
147  self.stateImported.emit()
148 
149  def renameTransition(self):
150  dialog = RenameDialog('Rename', self.selectedTransition.transitionData.name)
151  dialog.move(self.contextPosition)
152  dialog.nameChanged.connect(self.externalTransitionNameChanged)
153  dialog.exec_()
154 
156  dialog = TransitionCodeDialog('Transition Code', self.selectedTransition.transitionData)
157  dialog.move(self.contextPosition)
158  dialog.codeChanged.connect(self.transitionCodeChanged)
159  dialog.exec_()
160 
161  def removeTransition(self):
162  self.removeItem(self.selectedTransition)
163  transition = self.selectedTransition.transitionData
164  transition.origin.removeOriginTransition(transition)
165  transition.destination.removeDestTransition(transition)
166 
167  def mousePressEvent(self, qGraphicsSceneMouseEvent):
168  QGraphicsScene.mousePressEvent(self, qGraphicsSceneMouseEvent)
169 
170  def addTransitionItem(self, tranItem, isInsertion=True):
171  if not tranItem in self.items():
172  self.addItem(tranItem)
173  if isInsertion:
174  self.transitionInserted.emit(tranItem)
175 
176  def addStateItem(self, stateItem, isInsertion=True):
177  stateItem.stateNameChanged.connect(self.stateNameChanged)
178  stateItem.stateTextEditStarted.connect(self.stateTextEditStarted)
179  stateItem.stateTextEditFinished.connect(self.stateTextEditFinished)
180 
181  if not stateItem in self.items():
182  self.addItem(stateItem)
183  self.activeState.addChild(stateItem.stateData)
184  if len(self.activeState.getChildren()) == 1:
185  self.selectedState = stateItem
186  self.makeInitial()
187 
188  if isInsertion:
189  self.stateInserted.emit(stateItem)
190 
191  def removeStateItem(self, stateItem):
192  stateItem.stateNameChanged.disconnect(self.stateNameChanged)
193  stateItem.stateTextEditStarted.disconnect(self.stateTextEditStarted)
194  stateItem.stateTextEditFinished.disconnect(self.stateTextEditFinished)
195 
196  for tran in stateItem.stateData.getOriginTransitions():
197  print('removing origin tran:' + tran.name)
198  tran.destination.removeDestTransition(tran)
199  self.removeItem(tran.getGraphicsItem())
200  self.transitionRemoved.emit(tran.getGraphicsItem())
201 
202  for tran in stateItem.stateData.getDestTransitions():
203  print('removing destination tran:' + tran.name)
204  tran.origin.removeOriginTransition(tran)
205  self.removeItem(tran.getGraphicsItem())
206  self.transitionRemoved.emit(tran.getGraphicsItem())
207 
208  if self.origin == stateItem:
209  self.origin = None
210 
211  self.removeItem(stateItem)
212  self.activeState.removeChild(stateItem.stateData)
213 
214  self.stateRemoved.emit(stateItem)
215 
216  def mouseReleaseEvent(self, qGraphicsSceneMouseEvent):
217  # if we were editing the state text next mouse release should disable text editing
218  # and should not add a new state or transition
219  if self.stateTextEditingStarted:
220  self.stateTextEditingStarted = False
221  QGraphicsScene.mouseReleaseEvent(self, qGraphicsSceneMouseEvent)
222  return
223 
224  if self.operationType == OpType.ADDSTATE and qGraphicsSceneMouseEvent.button() == Qt.LeftButton:
225  selectedItems = self.items(qGraphicsSceneMouseEvent.scenePos())
226  if len(selectedItems) == 0:
227  # Create New Namespace for State
228  namespace = Namespace('', '')
229  sIndex = self.getStateIndex()
230  state = State(sIndex, 'state' + str(sIndex), False, namespace, self.activeState)
231  state.setPos(qGraphicsSceneMouseEvent.scenePos().x(),
232  qGraphicsSceneMouseEvent.scenePos().y())
233  self.addStateItem(state.getGraphicsItem())
234 
235  self.origin = None
236 
237  elif self.operationType == OpType.ADDTRANSITION and qGraphicsSceneMouseEvent.button() == Qt.LeftButton:
238  selectedItems = self.items(qGraphicsSceneMouseEvent.scenePos())
239  if len(selectedItems) > 0:
240  # get the parent
241  item = self.getParentItem(selectedItems[0])
242  if isinstance(item, StateGraphicsItem):
243  if self.origin != None:
244  self.destination = item
245  tIndex = self.getTransitionIndex()
246  tran = Transition(tIndex, 'transition ' + str(tIndex),
247  self.origin.stateData, self.destination.stateData)
248  self.addTransitionItem(tran.getGraphicsItem())
249  self.origin = None
250 
251  else:
252  self.origin = item
253  else:
254  self.origin = None
255  else:
256  self.origin = None
257 
258  else:
259  if self.operationType == OpType.OPENAUTOMATA:
260  self.operationType = self.prevOperationType
261 
262  QGraphicsScene.mouseReleaseEvent(self, qGraphicsSceneMouseEvent)
263 
264  def contextMenuEvent(self, qGraphicsSceneContextMenuEvent):
265  selectedItems = self.items(qGraphicsSceneContextMenuEvent.scenePos())
266  if len(selectedItems) > 0:
267  item = self.getParentItem(selectedItems[0])
268  if isinstance(item, StateGraphicsItem):
269  self.showStateContextMenu(item, qGraphicsSceneContextMenuEvent)
270  elif isinstance(item, TransitionGraphicsItem):
271  self.showTransitionContextMenu(item, qGraphicsSceneContextMenuEvent)
272  elif len(selectedItems) == 0:
273  self.showSceneContextMenu(qGraphicsSceneContextMenuEvent)
274 
275  def mouseDoubleClickEvent(self, qGraphicsSceneMouseEvent):
276  selectedItems = self.items(qGraphicsSceneMouseEvent.scenePos())
277  if len(selectedItems) > 0:
278  if isinstance(selectedItems[0], IdTextBoxGraphicsItem):
279  QGraphicsScene.mouseDoubleClickEvent(self, qGraphicsSceneMouseEvent)
280  pass
281  else:
282  item = self.getParentItem(selectedItems[0])
283  if isinstance(item, StateGraphicsItem):
284  self.setActiveState(item.stateData)
285  QGraphicsScene.mouseDoubleClickEvent(self, qGraphicsSceneMouseEvent)
286 
287  self.prevOperationType = self.operationType
288  self.operationType = OpType.OPENAUTOMATA
289 
290  def showStateContextMenu(self, stateItem, qEvent):
291  cMenu = QMenu()
292  cMenu.addAction(self.renameStateAction)
293  cMenu.addAction(self.stateCodeAction)
294  cMenu.addAction(self.makeInitialAction)
295  cMenu.addAction(self.copyStateAction)
296  cMenu.addAction(self.removeStateAction)
297  cMenu.addAction(self.importStateAction)
298  self.selectedState = stateItem
299  self.contextPosition = qEvent.screenPos()
300  action = cMenu.exec_(qEvent.screenPos())
301 
302  def showTransitionContextMenu(self, tranItem, qEvent):
303  cMenu = QMenu()
304  cMenu.addAction(self.renameTransitionAction)
305  cMenu.addAction(self.transitionCodeAction)
306  cMenu.addAction(self.removeTransitionAction)
307  self.selectedTransition = tranItem
308  self.contextPosition = qEvent.screenPos()
309  action = cMenu.exec_(qEvent.screenPos())
310 
311  def showSceneContextMenu(self, qEvent):
312  cMenu = QMenu()
313  cMenu.addAction(self.pasteStateAction)
314  self.currentScenePos = qEvent.scenePos()
315  action = cMenu.exec_(qEvent.screenPos())
316 
317  def setOperationType(self, type):
318  self.operationType = type
319 
320  def getStateIndex(self):
321  self.stateIndex += 1
322  return self.stateIndex
323 
325  self.transitionIndex += 1
326  return self.transitionIndex
327 
328  def getParentItem(self, item):
329  while item.parentItem() is not None:
330  item = item.parentItem()
331  return item
332 
333  def externalStateNameChanged(self, newName):
334  self.selectedState.textGraphics.setPlainText(newName)
335  self.selectedState.textGraphics.textChanged.emit(newName)
336 
337  def externalTransitionNameChanged(self, newName):
338  self.selectedTransition.transitionData.name = newName
339  self.selectedTransition.textGraphics.setPlainText(newName)
340  self.selectedTransition.textGraphics.textChanged.emit(newName)
341 
342  def stateCodeChanged(self, newCode):
343  self.selectedState.stateData.code = newCode
344 
345  def transitionCodeChanged(self, type, typeValue, code):
346  transition = self.selectedTransition.transitionData
347  transition.setType(type)
348  if type == TransitionType.TEMPORAL:
349  transition.setTemporalTime(typeValue)
350  elif type == TransitionType.CONDITIONAL:
351  transition.setCondition(typeValue)
352 
353  transition.setCode(code)
354 
355  def stateNameChanged(self, state):
356  self.stateNameChangedSignal.emit(state)
357 
359  # temporarily disable operation type while editing the text
360  self.prevOperationType = self.operationType
361  self.operationType = None
362  self.stateTextEditingStarted = True
363 
365  # after text edit finishes restore operation type
366  self.operationType = self.prevOperationType
367  self.prevOperationType = None
368  self.stateTextEditingStarted = True
369 
370  def setActiveState(self, state):
371  if state != self.activeState:
372  self.clearScene()
373  self.activeState = state
374  self.displayState(self.activeState)
375 
376  if state.namespace != self.activeNamespace:
377  self.activeNamespace = state.getNamespace()
378  self.activeNamespaceChanged.emit()
379 
380  def displayState(self, state):
381  transitions = []
382  for child in state.getChildren():
383  self.addStateItem(child.getGraphicsItem(), False)
384  transitions = transitions + child.getOriginTransitions()
385 
386  for tran in transitions:
387  self.addTransitionItem(tran.getGraphicsItem(), False)
388 
389  # print('set active state:' + self.activeState.name)
390  self.activeStateChanged.emit()
391 
392  def clearScene(self):
393  # clear scene
394  self.clear()
395  if self.activeState != None:
396  # reset all of the graphics item of the current active state
397  # print('type:' + str(type(self.activeState)))
398  for child in self.activeState.getChildren():
399  child.resetGraphicsItem()
400  for tran in child.getOriginTransitions():
401  tran.resetGraphicsItem()
402 
403  def resetIndexes(self):
404  """Reset Indices of Automata
405  Helper Funtion for creating new AutomataScene"""
406  self.stateIndex = 0
407  self.transitionIndex = 0
408 
409  def setLastIndexes(self, rootState):
410  """Updates AutomataScene's Largest State and Transition ID"""
411  if rootState.id > self.stateIndex:
412  self.stateIndex = rootState.id
413 
414  for tran in rootState.getOriginTransitions():
415  if tran.id > self.transitionIndex:
416  self.transitionIndex = tran.id
417 
418  for child in rootState.getChildren():
419  self.setLastIndexes(child)
def mouseReleaseEvent(self, qGraphicsSceneMouseEvent)
def mouseDoubleClickEvent(self, qGraphicsSceneMouseEvent)
def contextMenuEvent(self, qGraphicsSceneContextMenuEvent)
def mousePressEvent(self, qGraphicsSceneMouseEvent)
def addTransitionItem(self, tranItem, isInsertion=True)
def addStateItem(self, stateItem, isInsertion=True)
def transitionCodeChanged(self, type, typeValue, code)


visualstates
Author(s):
autogenerated on Thu Apr 1 2021 02:42:20