visualstates.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 '''
3  Copyright (C) 1997-2017 JDERobot Developers Team
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  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU Library General Public License for more details.
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, see <http://www.gnu.org/licenses/>.
14  Authors : Okan Asik (asik.okan@gmail.com)
15  '''
16 from PyQt5.QtCore import Qt
17 from PyQt5.QtGui import QPainter, QColor, QPixmap
18 from PyQt5.QtWidgets import QMainWindow, QAction, QDockWidget, QTreeView, QGraphicsView, \
19  QWidget, QFileDialog, QLabel, QVBoxLayout, QPushButton, QMessageBox
20 from .automata.automatascene import AutomataScene, OpType
21 from ..parsers.filemanager import FileManager
22 from ..parsers.importmanager import ImportManager
23 from .tree.treemodel import TreeModel
24 from ..core.state import State
25 from ..core.namespace import Namespace
26 from .transition.timerdialog import TimerDialog
27 from .dialogs.namespacedialog import NamespaceDialog
28 from .dialogs.librariesdialog import LibrariesDialog
29 from .dialogs.rosconfigdialog import RosConfigDialog
30 from ..configs.rosconfig import RosConfig
31 from ..generators.cpprosgenerator import CppRosGenerator
32 from ..generators.pythonrosgenerator import PythonRosGenerator
33 from ..configs.rospackage import getPackagePath
34 from .dialogs.aboutdialog import AboutDialog
35 
36 
37 class VisualStates(QMainWindow):
38  def __init__(self, parent=None):
39  super(QMainWindow, self).__init__()
40 
41  self.setWindowTitle("VisualStates")
42  self.configDialog = None
43 
44  # root state
45  self.globalNamespace = Namespace('', '')
46  self.localNamespace = Namespace('', '')
47  self.rootState = State(0, "root", True, self.localNamespace)
48  self.activeState = self.rootState
50 
51  self.statusBar()
52  self.createMenu()
53  self.createTreeView()
54  self.createStateCanvas()
55 
56  self.setGeometry(0, 0, 800, 600)
57  self.show()
58 
59  self.fileManager = FileManager()
60  self.importManager = ImportManager()
61  self.automataPath = None
62 
63  self.libraries = []
64  self.config = None
65 
66  def createMenu(self):
67  # create actions
68  newAction = QAction('&New', self)
69  newAction.setShortcut('Ctrl+N')
70  newAction.setStatusTip('Create New Visual States')
71  newAction.triggered.connect(self.newAction)
72 
73  openAction = QAction('&Open', self)
74  openAction.setShortcut('Ctrl+O')
75  openAction.setStatusTip('Open Visual States')
76  openAction.triggered.connect(self.openAction)
77 
78  importAction = QAction('&Import', self)
79  openAction.setShortcut('Ctrl+I')
80  importAction.setStatusTip('Import A State')
81  importAction.triggered.connect(self.importAction)
82 
83  saveAction = QAction('&Save', self)
84  saveAction.setShortcut('Ctrl+S')
85  saveAction.setStatusTip('Save Visual States')
86  saveAction.triggered.connect(self.saveAction)
87 
88  saveAsAction = QAction('&Save As', self)
89  saveAsAction.setShortcut('Ctrl+Shift+S')
90  saveAsAction.setStatusTip('Save Visual States as New One')
91  saveAsAction.triggered.connect(self.saveAsAction)
92 
93  quitAction = QAction('&Quit', self)
94  quitAction.setShortcut('Ctrl+Q')
95  quitAction.setStatusTip('Quit Visual States')
96  quitAction.triggered.connect(self.quitAction)
97 
98  # figures menu
99  stateAction = QAction('&State', self)
100  stateAction.setStatusTip('Create a state')
101  stateAction.triggered.connect(self.stateAction)
102 
103  transitionAction = QAction('&Transition', self)
104  transitionAction.setStatusTip('Create a transition')
105  transitionAction.triggered.connect(self.transitionAction)
106 
107  # data menu
108  timerAction = QAction('&Timer', self)
109  timerAction.setShortcut('Ctrl+M')
110  timerAction.setStatusTip('Set timing of states')
111  timerAction.triggered.connect(self.timerAction)
112 
113  globalNamespaceAction = QAction('&Global Namespace', self)
114  globalNamespaceAction.setShortcut('Ctrl+G')
115  globalNamespaceAction.setStatusTip('Open Global Namespace')
116  globalNamespaceAction.triggered.connect(self.globalNamespaceAction)
117 
118  stateNamespaceAction = QAction('&State Namespace', self)
119  stateNamespaceAction.setShortcut('Ctrl+T')
120  stateNamespaceAction.setStatusTip('Open State Namespace')
121  stateNamespaceAction.triggered.connect(self.localNamespaceAction)
122 
123  # actions menu
124  librariesAction = QAction('&Libraries', self)
125  librariesAction.setShortcut('Ctrl+L')
126  librariesAction.setStatusTip('Add additional libraries')
127  librariesAction.triggered.connect(self.librariesAction)
128 
129  configFileAction = QAction('&ROS Config', self)
130  configFileAction.setShortcut('Ctrl+R')
131  configFileAction.setStatusTip('Edit ROS configuration')
132  configFileAction.triggered.connect(self.configFileAction)
133 
134  generateCppAction = QAction('&Generate C++', self)
135  generateCppAction.setShortcut('Ctrl+U')
136  generateCppAction.setStatusTip('Generate C++ code')
137  generateCppAction.triggered.connect(self.generateCppAction)
138 
139  generatePythonAction = QAction('&Generate Python', self)
140  generatePythonAction.setShortcut('Ctrl+Y')
141  generatePythonAction.setStatusTip('Generate Python code')
142  generatePythonAction.triggered.connect(self.generatePythonAction)
143 
144  # help menu
145  aboutAction = QAction('&About', self)
146  aboutAction.setShortcut('F1')
147  aboutAction.setStatusTip('Information about VisualStates')
148  aboutAction.triggered.connect(self.aboutAction)
149 
150  # create main menu
151  menubar = self.menuBar()
152  archieveMenu = menubar.addMenu('&File')
153  archieveMenu.addAction(newAction)
154  archieveMenu.addAction(openAction)
155  archieveMenu.addAction(importAction)
156  archieveMenu.addAction(saveAction)
157  archieveMenu.addAction(saveAsAction)
158  archieveMenu.addAction(quitAction)
159 
160  figuresMenu = menubar.addMenu('&Figures')
161  figuresMenu.addAction(stateAction)
162  figuresMenu.addAction(transitionAction)
163 
164  dataMenu = menubar.addMenu('&Data')
165  dataMenu.addAction(timerAction)
166  dataMenu.addAction(globalNamespaceAction)
167  dataMenu.addAction(stateNamespaceAction)
168 
169  actionsMenu = menubar.addMenu('&Actions')
170  actionsMenu.addAction(librariesAction)
171  actionsMenu.addAction(configFileAction)
172  actionsMenu.addAction(generateCppAction)
173  # actionsMenu.addAction(compileCppAction)
174  actionsMenu.addAction(generatePythonAction)
175 
176  helpMenu = menubar.addMenu('&Help')
177  helpMenu.addAction(aboutAction)
178 
179  def newAction(self):
180  self.automataScene.clearScene()
181  self.treeModel.removeAll()
182 
183  self.fileManager.setPath("")
184 
185  # create new root state
186  self.globalNamespace = Namespace('', '')
187  self.localNamespace = Namespace('', '')
188  self.rootState = State(0, 'root', True, self.localNamespace)
189 
190  self.automataScene.setActiveState(self.rootState)
191 
192  self.automataScene.resetIndexes()
193 
194  self.libraries = []
195  self.config = None
196 
197  def openAction(self):
198  fileDialog = QFileDialog(self)
199  fileDialog.setWindowTitle("Open VisualStates File")
200  fileDialog.setViewMode(QFileDialog.Detail)
201  fileDialog.setNameFilters(['VisualStates File (*.xml)'])
202  fileDialog.setDefaultSuffix('.xml')
203  fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
204  if fileDialog.exec_():
205  self.openFile(fileDialog.selectedFiles()[0])
206 
207  def openFile(self, fileName):
208  (rootState, config, libraries, globalNamespace) = self.fileManager.open(fileName)
209  if rootState is not None:
210  (self.rootState, self.config, self.libraries, self.globalNamespace) = (rootState, config, libraries, globalNamespace)
211  self.automataPath = self.fileManager.fullPath
212  self.treeModel.removeAll()
213  self.treeModel.loadFromRoot(self.rootState)
214  # set the active state as the loaded state
215  self.automataScene.setActiveState(self.rootState)
216  self.automataScene.setLastIndexes(self.rootState)
217  else:
218  self.showWarning("Wrong file selected",
219  "The selected file is not a valid VisualStates file")
220 
221 
222  def saveAction(self):
223  if len(self.fileManager.getFileName()) == 0:
224  self.saveAsAction()
225  else:
226  self.fileManager.save(self.rootState, self.config, self.libraries, self.globalNamespace)
227 
228  def saveAsAction(self):
229  fileDialog = QFileDialog(self)
230  fileDialog.setWindowTitle("Save VisualStates Project")
231  fileDialog.setViewMode(QFileDialog.Detail)
232  fileDialog.setNameFilters(['VisualStates File (*.xml)'])
233  fileDialog.setAcceptMode(QFileDialog.AcceptSave)
234  if fileDialog.exec_():
235  self.fileManager.setFullPath(fileDialog.selectedFiles()[0])
236  self.fileManager.save(self.rootState, self.config, self.libraries, self.globalNamespace)
237 
238  def quitAction(self):
239  # print('Quit')
240  self.close()
241 
242  def stateAction(self):
243  self.automataScene.setOperationType(OpType.ADDSTATE)
244 
245  def transitionAction(self):
246  self.automataScene.setOperationType(OpType.ADDTRANSITION)
247 
248  def importAction(self):
249  fileDialog = QFileDialog(self)
250  fileDialog.setWindowTitle("Import VisualStates File")
251  fileDialog.setViewMode(QFileDialog.Detail)
252  fileDialog.setNameFilters(['VisualStates File (*.xml)'])
253  fileDialog.setDefaultSuffix('.xml')
254  fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
255  if fileDialog.exec_():
256  tempPath = self.fileManager.getFullPath()
257  file = self.fileManager.open(fileDialog.selectedFiles()[0])
258  if file[0] is not None:
259  self.fileManager.setPath(tempPath)
260  # if the current active state already has an initial state make sure that
261  # there will not be any initial state in the imported state
262  if self.activeState.getInitialChild() is not None:
263  for childState in file[0].getChildren():
264  childState.setInitial(False)
265 
266  # Update importing Namespaces
267  importedState, self.config, self.libraries, self.globalNamespace = self.importManager.updateAuxiliaryData(file, self)
268  self.treeModel.loadFromRoot(importedState, self.activeState)
269  self.automataScene.displayState(self.activeState)
270  self.automataScene.setLastIndexes(self.rootState)
271  else:
272  self.showWarning("Wrong file selected",
273  "The selected file is not a valid VisualStates file")
274 
275  def timerAction(self):
276  if self.activeState is not None:
277  timerDialog = TimerDialog('Time Step Duration', str(self.activeState.getTimeStep()))
278  timerDialog.timeChanged.connect(self.timeStepDurationChanged)
279  timerDialog.exec_()
280 
282  self.globalNamespaceDialog = NamespaceDialog('Global Namespace', self.globalNamespace)
283  self.globalNamespaceDialog.namespaceChanged.connect(self.globalNamespaceChanged)
284  self.globalNamespaceDialog.exec_()
285 
287  self.localNamespaceDialog = NamespaceDialog('Local Namespace', self.activeNamespace)
288  self.localNamespaceDialog.namespaceChanged.connect(self.localNamespaceChanged)
289  self.localNamespaceDialog.exec_()
290 
291  def librariesAction(self):
292  librariesDialog = LibrariesDialog('Libraries', self.libraries)
293  librariesDialog.librariesChanged.connect(self.librariesChanged)
294  librariesDialog.exec_()
295 
296  def configFileAction(self):
297  if self.config is None:
298  self.config = RosConfig()
299  self.configDialog = RosConfigDialog('Config', self.config)
300  self.configDialog.exec_()
301 
302  def showWarning(self, title, msg):
303  QMessageBox.warning(self, title, msg)
304 
305  def showInfo(self, title, msg):
306  QMessageBox.information(self, title, msg)
307 
308  def generateCppAction(self):
309  stateList = []
310  if self.fileManager.hasFile():
311  self.getStateList(self.rootState, stateList)
312  if self.config is None:
313  self.config = RosConfig()
314  generator = CppRosGenerator(self.libraries, self.config, stateList, self.globalNamespace)
315  generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
316  self.showInfo('C++ Code Generation', 'C++ code generation is successful.')
317  else:
318  self.showWarning('C++ Generation', 'Please save the project before code generation.')
319 
320  # def compileCppAction(self):
321  # # print('compile cpp action')
322  # pass
323 
325  stateList = []
326  if self.fileManager.hasFile():
327  self.getStateList(self.rootState, stateList)
328  if self.config is None:
329  self.config = RosConfig()
330  generator = PythonRosGenerator(self.libraries, self.config, stateList, self.globalNamespace)
331  generator.generate(self.fileManager.getPath(), self.fileManager.getFileName())
332  self.showInfo('Python Code Generation', 'Python code generation is successful.')
333  else:
334  self.showWarning('Python Generation', 'Please save the project before code generation.')
335 
336  def aboutAction(self):
337  aboutDialog = AboutDialog()
338  aboutDialog.exec_()
339 
340  def createTreeView(self):
341  dockWidget = QDockWidget()
342  dockWidget.setAllowedAreas(Qt.LeftDockWidgetArea)
343  dockWidget.setFeatures(QDockWidget.NoDockWidgetFeatures)
344  dockWidget.setTitleBarWidget(QWidget())
345  self.treeView = QTreeView()
346  self.treeView.clicked.connect(self.treeItemClicked)
347  self.treeModel = TreeModel()
348  self.treeView.setModel(self.treeModel)
349 
350  self.upButton = QPushButton()
351  self.upButton.setText('Up')
352  self.upButton.clicked.connect(self.upButtonClicked)
353 
354  leftContainer = QWidget()
355  leftLayout = QVBoxLayout()
356  leftLayout.addWidget(self.treeView)
357  leftLayout.addWidget(self.upButton)
358  leftContainer.setLayout(leftLayout)
359 
360  dockWidget.setWidget(leftContainer)
361  self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget)
362 
363  def createStateCanvas(self):
364  self.stateCanvas = QGraphicsView()
365  self.automataScene = AutomataScene()
366  self.automataScene.setSceneRect(0, 0, 2000, 2000)
367  self.automataScene.activeStateChanged.connect(self.activeStateChanged)
368  self.automataScene.activeNamespaceChanged.connect(self.activeNamespaceChanged)
369  self.automataScene.stateInserted.connect(self.stateInserted)
370  self.automataScene.stateRemoved.connect(self.stateRemoved)
371  self.automataScene.stateImported.connect(self.stateImported)
372  self.automataScene.transitionInserted.connect(self.transitionInserted)
373  self.automataScene.stateNameChangedSignal.connect(self.stateNameChanged)
374  self.automataScene.setActiveState(self.rootState)
375 
376  self.setCentralWidget(self.stateCanvas)
377  self.stateCanvas.setScene(self.automataScene)
378  self.stateCanvas.setRenderHint(QPainter.Antialiasing)
379  self.stateCanvas.setAcceptDrops(True)
380 
381  def stateInserted(self, state):
382  if self.activeState != self.rootState:
383  parent = self.treeModel.getByDataId(self.activeState.id)
384  self.treeModel.insertState(state, QColor(Qt.white), parent)
385  else:
386  self.treeModel.insertState(state, QColor(Qt.white))
387 
388  def stateRemoved(self, state):
389  if self.activeState != self.rootState:
390  parent = self.treeModel.getByDataId(self.activeState.id)
391  self.treeModel.removeState(state.stateData, parent)
392  else:
393  self.treeModel.removeState(state.stateData)
394 
395  def stateImported(self):
396  self.importAction()
397 
398  def transitionInserted(self, tran):
399  # print('transition inserted:' + tran.transitionData.name)
400  pass
401 
402  def stateNameChanged(self, state):
403  dataItem = self.treeModel.getByDataId(state.stateData.id)
404  if dataItem != None:
405  dataItem.name = state.stateData.name
406  self.treeModel.layoutChanged.emit()
407 
409  if self.automataScene.activeState != self.activeState:
410  # print('visual states active state changed:' + self.automataScene.activeState.name)
411  self.activeState = self.automataScene.activeState
412  if self.activeState == self.rootState:
413  self.treeView.selectionModel().clearSelection()
414  else:
415  self.treeView.setCurrentIndex(self.treeModel.indexOf(self.treeModel.getByDataId(self.activeState.id)))
416 
418  if self.automataScene.activeNamespace != self.activeNamespace:
419  self.activeNamespace = self.automataScene.activeNamespace
420 
421  def upButtonClicked(self):
422  if self.activeState != None:
423  if self.activeState.parent != None:
424  #print(self.activeState.parent.id)
425  self.automataScene.setActiveState(self.activeState.parent)
426 
427  def getStateById(self, state, id):
428  if state.id == id:
429  return state
430  else:
431  result = None
432  for child in state.getChildren():
433  result = self.getStateById(child, id)
434  if result is not None:
435  return result
436  return result
437 
438  def treeItemClicked(self, index):
439  # print('clicked item.id:' + str(index.internalPointer().id))
440  state = self.getStateById(self.rootState, index.internalPointer().id)
441  if state is not None:
442  # set the active state as the loaded state
443  self.automataScene.setActiveState(state)
444 
445  def timeStepDurationChanged(self, duration):
446  if self.activeState is not None:
447  self.activeState.setTimeStep(duration)
448 
449  def librariesChanged(self, libraries):
450  self.libraries = libraries
451 
453  if self.globalNamespaceDialog:
454  self.globalNamespace = self.globalNamespaceDialog.getNamespace()
455 
457  if self.localNamespaceDialog:
458  self.activeNamespace = self.localNamespaceDialog.getNamespace()
459 
460  def getStateList(self, state, stateList):
461  if len(state.getChildren()) > 0:
462  stateList.append(state)
463 
464  for s in state.getChildren():
465  self.getStateList(s, stateList)
def getStateList(self, state, stateList)


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