Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parameters to visual states tool. #1

Merged
merged 33 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
de26bfe
Add core file for parameters
sudo-panda Jun 1, 2019
4f4ed20
Add parameters to visualstates
sudo-panda Jun 1, 2019
3a4cd96
Add dialog to add, edit and remove parameters
sudo-panda Jun 1, 2019
8dc6f0c
Enable saving of parameters
sudo-panda Jun 5, 2019
d5b8bdd
Enable opening of files with parameters
sudo-panda Jun 5, 2019
bbb00e9
Add a case to name validation of parameters
sudo-panda Jun 5, 2019
732aa11
Add cpp code generation with parameters
sudo-panda Jun 5, 2019
2213cb6
Add python code generation with parameters
sudo-panda Jun 5, 2019
88d5f74
Add initialization of global variables by taking input
sudo-panda Jun 9, 2019
c0deb28
Handle cases where value and description fields are empty
sudo-panda Jun 9, 2019
149f3bc
Stop searching for parameters in all files and only search in source …
sudo-panda Jun 9, 2019
fb5c98e
Enable importing of parameters
sudo-panda Jun 9, 2019
c48866e
Resolves issue in which editing parameters required change names
sudo-panda Jun 9, 2019
23e41e5
Merge branch 'master' into devel
sudo-panda Jun 10, 2019
27dfd4b
Moved parameters dialog to Local namespace dialog tab
sudo-panda Jun 20, 2019
1f31a7e
Prevent addition of parameters to Global Namespace"
sudo-panda Jun 20, 2019
47c0f1f
Save parameters inside namespace element
sudo-panda Jun 20, 2019
95d05c7
Open files that have parameters in namespace elements
sudo-panda Jun 20, 2019
fe0a687
Add import of parameters in local namespace
sudo-panda Jun 20, 2019
a7c665d
Add parameters to code during generation
sudo-panda Jun 20, 2019
4041e8f
Solve generation of code not working
sudo-panda Jun 23, 2019
65f7fc1
Replace try catch with if else
sudo-panda Jul 5, 2019
1e331c9
Add dialog to display imported parameters
sudo-panda Jul 7, 2019
e934de9
Change desc to value
sudo-panda Jul 8, 2019
aba93b9
Add selective import of states
sudo-panda Jul 11, 2019
5cd07e7
Add toggle view buttons
sudo-panda Jul 11, 2019
cc5a48f
Change absolute import to relative import
sudo-panda Jul 14, 2019
b54f87c
Add better display of parameters
sudo-panda Jul 15, 2019
741c73e
fix qgroupbox title spacing at the top
okanasik Jul 18, 2019
861224a
Copy list before pop to resolve empty list error
sudo-panda Jul 18, 2019
3eef8dc
Use dict in place of list to store and access parameter Namespace Dialog
sudo-panda Jul 26, 2019
d585894
Modify code to reselect sub states with parent
sudo-panda Aug 11, 2019
1d44a54
Remove .idea files
sudo-panda Aug 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/visualstates/core/parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'''
Copyright (C) 1997-2019 JDERobot Developers Team

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.

Authors : Baidyanath Kundu ([email protected])

'''

class Parameter:
def __init__(self, name='', type='', value='', desc=''):
self.name = name
self.type = type
self.value = value
self.desc = desc

def setName(self, name):
self.name = name

def setType(self, type):
self.type = type

def setValue(self, value):
self.value = value

def setDesc(self, desc):
self.desc = desc

def createDocFromParam(self, doc):
paramElement = doc.createElement('param')
paramElement.setAttribute('type', self.type)
paramElement.setAttribute('name', self.name)

valueElement = doc.createElement('value')
valueElement.appendChild(doc.createTextNode(self.value))
paramElement.appendChild(valueElement)
descElement = doc.createElement('description')
descElement.appendChild(doc.createTextNode(self.desc))
paramElement.appendChild(descElement)

return paramElement

def parseElement(self, element):
for (name, value) in element.attributes.items():
if name == 'name':
self.name = str(value)
elif name == 'type':
self.type = str(value)
try:
self.value = str(element.getElementsByTagName('value')[0].childNodes[0].nodeValue)
except IndexError:
self.desc = ''
sudo-panda marked this conversation as resolved.
Show resolved Hide resolved
try:
self.desc = str(element.getElementsByTagName('description')[0].childNodes[0].nodeValue)
except IndexError:
self.desc = ''

def isTypeEqualValue(type, value):
if type == 'Boolean' and not (value == 'True' or value == 'False'):
return False
elif type == 'Integer' and not value.isdigit():
sudo-panda marked this conversation as resolved.
Show resolved Hide resolved
return False
elif type == 'Float' and not (value.replace('.', '', 1).isdigit()):
sudo-panda marked this conversation as resolved.
Show resolved Hide resolved
return False
else:
return True

def isParamName(name):
if name == '':
return False
elif name.replace('_', '').isalnum() and name[0].isalpha():
return True
else:
return False
43 changes: 29 additions & 14 deletions src/visualstates/generators/cpprosgenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CppRosGenerator(BaseGenerator):
def __init__(self, libraries, config, states, globalNamespace):
BaseGenerator.__init__(self, libraries, config, states, globalNamespace)

def generate(self, projectPath, projectName):
def generate(self, params, projectPath, projectName):
# create source dir if not exists
if not os.path.exists(projectPath + os.sep + 'src'):
os.makedirs(projectPath + os.sep + 'src')
Expand All @@ -42,10 +42,7 @@ def generate(self, projectPath, projectName):
self.generateStateClasses(stringList)
self.generateTransitionClasses(stringList)
stringList.append('#endif')
sourceCode = ''.join(stringList)
fp = open(projectPath + os.sep + 'src' + os.sep + projectName + '.h', 'w')
fp.write(sourceCode)
fp.close()
headerSourceCode = ''.join(stringList)

stringList = []
self.generateHeadersForCpp(stringList, projectName)
Expand All @@ -56,28 +53,46 @@ def generate(self, projectPath, projectName):
self.generateMain(stringList, projectName)
sourceCode = ''.join(stringList)

stringList = []
self.generateRunTimeGui(stringList)
guiSourceCode = ''.join(stringList)

stringList = []
self.generateCmake(stringList, projectName, self.config)
cmakeString = ''.join(stringList)

xmlDoc = self.generatePackageXml(self.config, projectName)
xmlStr = xmlDoc.toprettyxml(indent=' ')

# replacing parameters with their values
for param in params:
findText = '${' + param.name + '}'
headerSourceCode = headerSourceCode.replace(findText, param.value)
sourceCode = sourceCode.replace(findText, param.value)
guiSourceCode = guiSourceCode.replace(findText, param.value)
cmakeString = cmakeString.replace(findText, param.value)
xmlStr = xmlStr.replace(findText, param.value)

# writing to files
fp = open(projectPath + os.sep + 'src' + os.sep + projectName + '.h', 'w')
fp.write(headerSourceCode)
fp.close()


fp = open(projectPath + os.sep + 'src' + os.sep + projectName + '.cpp', 'w')
fp.write(sourceCode)
fp.close()

stringList = []
self.generateRunTimeGui(stringList)
sourceCode = ''.join(stringList)
fp = open(projectPath + os.sep + projectName + '_runtime.py', 'w')
fp.write(sourceCode)
fp.write(guiSourceCode)
fp.close()
# make runtime gui python file executable
os.chmod(projectPath + os.sep + projectName + '_runtime.py', stat.S_IEXEC | stat.S_IXOTH | stat.S_IWRITE | stat.S_IREAD)

stringList = []
self.generateCmake(stringList, projectName, self.config)
cmakeString = ''.join(stringList)
fp = open(projectPath + os.sep + 'CMakeLists.txt', 'w')
fp.write(cmakeString)
fp.close()

xmlDoc = self.generatePackageXml(self.config, projectName)
xmlStr = xmlDoc.toprettyxml(indent=' ')
with open(projectPath + os.sep + 'package.xml', 'w') as f:
f.write(xmlStr)

Expand Down
23 changes: 17 additions & 6 deletions src/visualstates/generators/pythonrosgenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PythonRosGenerator(BaseGenerator):
def __init__(self, libraries, config, states, globalNamespace):
BaseGenerator.__init__(self, libraries, config, states, globalNamespace)

def generate(self, projectPath, projectName):
def generate(self, params, projectPath, projectName):
stringList = []
self.generateImports(stringList)
self.generateSignalHandling(stringList)
Expand All @@ -39,20 +39,31 @@ def generate(self, projectPath, projectName):
self.generateTransitionClasses(stringList)
self.generateMain(stringList)
sourceCode = ''.join(stringList)

stringList = []
self.generateCmake(stringList, projectName)
cmakeString = ''.join(stringList)

xmlDoc = self.generatePackageXml(self.config, projectName)
xmlStr = xmlDoc.toprettyxml(indent=' ')

#replacing parameters with their values
for param in params:
findText = '${'+param.name+'}'
sourceCode = sourceCode.replace(findText, param.value)
cmakeString = cmakeString.replace(findText, param.value)
xmlStr = xmlStr.replace(findText, param.value)

#writing to files
fp = open(projectPath + os.sep + projectName + '.py', 'w')
fp.write(sourceCode)
fp.close()
os.system('chmod +x "' +projectPath+ '"' + os.sep + projectName + '.py')

stringList = []
self.generateCmake(stringList, projectName)
cmakeString = ''.join(stringList)
fp = open(projectPath + os.sep + 'CMakeLists.txt', 'w')
fp.write(cmakeString)
fp.close()

xmlDoc = self.generatePackageXml(self.config, projectName)
xmlStr = xmlDoc.toprettyxml(indent=' ')
with open(projectPath + os.sep + 'package.xml', 'w') as f:
f.write(xmlStr)

Expand Down
128 changes: 128 additions & 0 deletions src/visualstates/gui/dialogs/paramprop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'''
Copyright (C) 1997-2019 JDERobot Developers Team

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.

Authors : Baidyanath Kundu ([email protected])

'''
import sys
from PyQt5.QtWidgets import QDialog, QLabel, QLineEdit, \
QPushButton, QApplication, QHBoxLayout, QVBoxLayout, \
QScrollArea, QComboBox, QGridLayout, QPlainTextEdit, \
QMessageBox
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import *
from visualstates.core.parameter import Parameter, isParamName, isTypeEqualValue

class ParamPropDialog(QDialog):

paramAdded = pyqtSignal(list)
paramUpdated = pyqtSignal(list, int)

def __init__(self, params=None, id=-1):
super(QDialog, self).__init__()
self.params = params
self.id = id
self.param = Parameter()
if self.id != -1:
self.param = self.params[self.id]
self.setFixedSize(400, 360)
if id == -1:
self.setWindowTitle('Add Parameter')
else:
self.setWindowTitle('Edit Parameter')

Layout = QVBoxLayout()
self.setLayout(Layout)
rowLayout = QHBoxLayout()
nameLbl = QLabel('Name :')
nameLbl.setFixedWidth(100)
rowLayout.addWidget(nameLbl)
self.nameEdit = QLineEdit(self.param.name)
rowLayout.addWidget(self.nameEdit)
Layout.addLayout(rowLayout)

rowLayout = QHBoxLayout()
typeLbl = QLabel('Type :')
typeLbl.setFixedWidth(100)
rowLayout.addWidget(typeLbl)
self.typeCb = QComboBox()
self.typeCb.addItems(['String', 'Integer', 'Float', 'Boolean'])
self.typeCb.setCurrentText(self.param.type)
rowLayout.addWidget(self.typeCb)
Layout.addLayout(rowLayout)

rowLayout = QHBoxLayout()
valueLbl = QLabel('Value :')
valueLbl.setFixedWidth(100)
rowLayout.addWidget(valueLbl)
self.valueEdit = QLineEdit(self.param.value)
rowLayout.addWidget(self.valueEdit)
Layout.addLayout(rowLayout)

rowLayout = QHBoxLayout()
descLbl = QLabel('Description :')
descLbl.setFixedWidth(100)
descLbl.setAlignment(Qt.AlignTop)
rowLayout.addWidget(descLbl)
self.descEdit = QPlainTextEdit(self.param.desc)
self.descEdit.setFixedHeight(200)
rowLayout.addWidget(self.descEdit)
Layout.addLayout(rowLayout)

btnLayout = QHBoxLayout()
btnLayout.setAlignment(Qt.AlignRight)
saveBtn = QPushButton("Save")
saveBtn.setFixedWidth(80)
saveBtn.clicked.connect(self.saveClicked)
btnLayout.addWidget(saveBtn)
closeBtn = QPushButton("Close")
closeBtn.setFixedWidth(80)
closeBtn.clicked.connect(self.closeClicked)
btnLayout.addWidget(closeBtn)
Layout.addLayout(btnLayout)
self.setLayout(Layout)

def saveClicked(self):
if not isTypeEqualValue(self.typeCb.currentText(), self.valueEdit.text().strip()):
QMessageBox.warning(self, 'Error', 'Value is not same as type')
elif not isParamName(self.nameEdit.text().strip()):
QMessageBox.warning(self, 'Error', 'Name does not meet requirements of a parameter')
else:
for param in self.params:
if param.name == self.nameEdit.text():
QMessageBox.warning(self, 'Error', 'Name already in use')
return
newParam = False
if self.id == -1:
newParam = True
self.param.setName(self.nameEdit.text().strip())
self.param.setType(self.typeCb.currentText())
self.param.setValue(self.valueEdit.text().strip())
self.param.setDesc(self.descEdit.toPlainText())
if newParam:
self.params.append(self.param)
self.paramAdded.emit(self.params)
else:
self.paramUpdated.emit(self.params, self.id)
self.close()

def closeClicked(self):
self.close()

if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = ParamPropDialog(params=[])
dialog.exec_()
Loading