diff --git a/fkie_node_manager/src/fkie_node_manager/editor/graph_view.py b/fkie_node_manager/src/fkie_node_manager/editor/graph_view.py
index 0999aa56..fc7ad9ac 100644
--- a/fkie_node_manager/src/fkie_node_manager/editor/graph_view.py
+++ b/fkie_node_manager/src/fkie_node_manager/editor/graph_view.py
@@ -76,7 +76,8 @@ class GraphViewWidget(QDockWidget):
DATA_ARGS = Qt.UserRole + 7
DATA_DEF_ARGS_NOT_SET = Qt.UserRole + 8
DATA_ARG_NAME = Qt.UserRole + 9
- ITEM_TYPE = Qt.UserRole + 10
+ DATA_FILE_EXISTS = Qt.UserRole + 10
+ ITEM_TYPE = Qt.UserRole + 11
ITEM_TYPE_INC_FILE = 1
ITEM_TYPE_INC_GROUP_ARG = 2
ITEM_TYPE_INC_ARG = 3
@@ -216,6 +217,21 @@ def get_include_args(self, arglist, inc_string, from_file):
result[arg].append(incargs[arg])
return result
+ def get_included_files(self, from_file, inc_string=''):
+ '''
+ Returns all included files. If inc_string is not empty only files with raw string containing in inc_string are returned.
+ :rtype: Tuple(path, raw_data, line, exists)
+ '''
+ result = []
+ file_items = self.graphTreeView.model().match(self.graphTreeView.model().index(0, 0), self.DATA_INC_FILE, from_file, 10, Qt.MatchRecursive)
+ for index in file_items:
+ item = self.graphTreeView.model().itemFromIndex(index)
+ for row_idx in range(item.rowCount()):
+ inc_item = item.child(row_idx)
+ if not inc_string or inc_item.data(self.DATA_RAW) in inc_string:
+ result.append((inc_item.data(self.DATA_INC_FILE), inc_item.data(self.DATA_RAW), inc_item.data(self.DATA_LINE), inc_item.data(self.DATA_FILE_EXISTS)))
+ return result
+
def _refill_tree(self, tree, create_tree=True):
deep = 0
file_dsrc = self._root_path
@@ -304,6 +320,7 @@ def _append_items(self, item, deep, items=[]):
inc_item.setData(inc_file.raw_inc_path, self.DATA_RAW)
inc_item.setData(inc_file.args, self.DATA_ARGS)
inc_item.setData(inc_file.unset_default_args, self.DATA_DEF_ARGS_NOT_SET)
+ inc_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
# add included arguments
if inc_file.unset_default_args or inc_file.args:
arg_item = QStandardItem('arguments')
@@ -316,6 +333,7 @@ def _append_items(self, item, deep, items=[]):
da_item.setData(self.ITEM_TYPE_INC_ARG, self.ITEM_TYPE)
da_item.setData(inc_file.path_or_str, self.DATA_FILE)
da_item.setData(inc_file.inc_path, self.DATA_INC_FILE)
+ da_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
da_item.setData(da_name, self.DATA_ARG_NAME)
da_item.setToolTip("This argument is definded as default, but no set while include.")
arg_item.appendRow(da_item)
@@ -325,6 +343,7 @@ def _append_items(self, item, deep, items=[]):
da_item.setData(self.ITEM_TYPE_INC_ARG, self.ITEM_TYPE)
da_item.setData(inc_file.path_or_str, self.DATA_FILE)
da_item.setData(inc_file.inc_path, self.DATA_INC_FILE)
+ da_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
da_item.setData(da_name, self.DATA_ARG_NAME)
arg_item.appendRow(da_item)
inc_item.appendRow(arg_item)
diff --git a/fkie_node_manager/src/fkie_node_manager/editor/text_edit.py b/fkie_node_manager/src/fkie_node_manager/editor/text_edit.py
index 556abd21..df04aa3f 100644
--- a/fkie_node_manager/src/fkie_node_manager/editor/text_edit.py
+++ b/fkie_node_manager/src/fkie_node_manager/editor/text_edit.py
@@ -31,7 +31,6 @@
# POSSIBILITY OF SUCH DAMAGE.
-
from python_qt_binding.QtCore import QRegExp, Qt, Signal, QEvent
from python_qt_binding.QtGui import QColor, QFont, QKeySequence, QTextCursor, QTextDocument
import os
@@ -318,7 +317,9 @@ def file_changed(self, mtime):
def _strip_bad_parts(self, textblock, current_position):
result = textblock
- startidx = textblock.rfind('$(find ', 0, current_position)
+ startidx = textblock.rfind('$(dirname)', 0, current_position)
+ if startidx == -1:
+ startidx = textblock.rfind('$(find ', 0, current_position)
if startidx == -1:
startidx = textblock.rfind(' /', 0, current_position)
if startidx == -1:
@@ -351,67 +352,76 @@ def mouseReleaseEvent(self, event):
if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier:
cursor = self.cursorForPosition(event.pos())
try:
- textblock = self._strip_bad_parts(cursor.block().text(), cursor.positionInBlock())
- for inc_file in find_included_files(textblock, False, False, search_in_ext=[]):
- aval = inc_file.raw_inc_path
- aitems = aval.split("'")
- for search_for in aitems:
- if not search_for:
- continue
- try:
- rospy.logdebug("try to interpret: %s" % search_for)
- args_in_name = get_arg_names(search_for)
+ textblock = self._strip_bad_parts(
+ cursor.block().text(), cursor.positionInBlock())
+ r = self.parent.graph_view.get_included_files(
+ self.filename, textblock)
+ for inc_file, _raw_text, _line, file_exists in r:
+ try:
+ rospy.logdebug(
+ f"found included file: {inc_file}, exists: {file_exists}")
+ if file_exists:
+ event.setAccepted(True)
+ self.load_request_signal.emit(inc_file)
+ else:
+ rospy.logdebug(
+ f" included files does not exists, look for arguments in {inc_file}")
+ args_in_name = get_arg_names(inc_file)
resolved_args = {}
# if found arg in the name, try to detect values
if args_in_name:
- rospy.logdebug(" args %s in filename found, try to resolve..." % args_in_name)
- resolved_args = self.parent.graph_view.get_include_args(args_in_name, search_for, self.filename)
- if resolved_args:
- params = {}
- self._internal_args
- # create parameter dialog
- for key, val in resolved_args.items():
- values = list(val)
- # add args defined in current file
- if key in self._internal_args and self._internal_args[key] not in values:
- values.append(self._internal_args[key])
- params[key] = {':type': 'string', ':value': values}
- dia = ParameterDialog(params, store_geometry="open_launch_on_click")
- dia.setFilterVisible(False)
- dia.setWindowTitle('Select Parameter')
- if dia.exec_():
- params = dia.getKeywords()
- search_for = replace_arg(search_for, params)
- else:
- # canceled -> cancel interpretation
- QTextEdit.mouseReleaseEvent(self, event)
- return
- # now resolve find-statements
- rospy.logdebug(" send interpret request to daemon: %s" % search_for)
- inc_files = nm.nmd().launch.get_interpreted_path(self.filename, text=[search_for])
- for path, exists in inc_files:
- try:
- rospy.logdebug(" received interpret request from daemon: %s, exists: %d" % (path, exists))
- if exists:
- event.setAccepted(True)
- self.load_request_signal.emit(path)
+ rospy.logdebug(
+ f" args {args_in_name} in filename found, try to resolve...")
+ resolved_args = self.parent.graph_view.get_include_args(
+ args_in_name, _raw_text, self.filename)
+ if resolved_args:
+ params = {}
+ self._internal_args
+ # create parameter dialog
+ for key, val in resolved_args.items():
+ values = list(val)
+ # add args defined in current file
+ if key in self._internal_args and self._internal_args[key] not in values:
+ values.append(
+ self._internal_args[key])
+ params[key] = {
+ ':type': 'string', ':value': values}
+ dia = ParameterDialog(
+ params, store_geometry="open_launch_on_click")
+ dia.setFilterVisible(False)
+ dia.setWindowTitle('Select Parameter')
+ if dia.exec_():
+ params = dia.getKeywords()
+ inc_file = replace_arg(
+ inc_file, params)
+ self.load_request_signal.emit(
+ inc_file)
else:
- _filename, file_extension = os.path.splitext(path)
- if file_extension in nm.settings().launch_view_file_ext:
- # create a new file, if it does not exists
- result = MessageBox.question(self, "File not exists", '\n\n'.join(["Create a new file?", path]), buttons=MessageBox.Yes | MessageBox.No)
- if result == MessageBox.Yes:
- content = '\n\n' if path.endswith('.launch') else ''
- nm.nmd().file.save_file(path, content.encode(), 0)
- event.setAccepted(True)
- self.load_request_signal.emit(path)
- except Exception as e:
- MessageBox.critical(self, "Error", "File not found %s" % path, detailed_text=utf8(e))
- except exceptions.ResourceNotFound as not_found:
- MessageBox.critical(self, "Error", "Resource not found %s" % search_for, detailed_text=utf8(not_found.error))
+ # canceled -> cancel interpretation
+ QTextEdit.mouseReleaseEvent(
+ self, event)
+ return
+ else:
+ _filename, file_extension = os.path.splitext(
+ inc_file)
+ if file_extension in nm.settings().launch_view_file_ext:
+ # create a new file, if it does not exists
+ result = MessageBox.question(self, "File not exists", '\n\n'.join(
+ ["Create a new file?", inc_file]), buttons=MessageBox.Yes | MessageBox.No)
+ if result == MessageBox.Yes:
+ content = '\n\n' if inc_file.endswith(
+ '.launch') else ''
+ nm.nmd().file.save_file(inc_file, content.encode(), 0)
+ event.setAccepted(True)
+ self.load_request_signal.emit(
+ inc_file)
+ except (Exception, exceptions.ResourceNotFound) as e:
+ MessageBox.critical(
+ self, "Error", "File not found %s" % inc_file, detailed_text=utf8(e))
except Exception as err:
print(traceback.format_exc())
- MessageBox.critical(self, "Error", "Error while request included file %s" % self.filename, detailed_text=utf8(err))
+ MessageBox.critical(
+ self, "Error", "Error while request included file %s" % self.filename, detailed_text=utf8(err))
QTextEdit.mouseReleaseEvent(self, event)
def mouseMoveEvent(self, event):
diff --git a/fkie_node_manager_daemon/src/fkie_node_manager_daemon/common.py b/fkie_node_manager_daemon/src/fkie_node_manager_daemon/common.py
index 27fdf9c1..ce7a73ae 100644
--- a/fkie_node_manager_daemon/src/fkie_node_manager_daemon/common.py
+++ b/fkie_node_manager_daemon/src/fkie_node_manager_daemon/common.py
@@ -46,6 +46,7 @@
PACKAGE_FILE = 'package.xml'
EMPTY_PATTERN = re.compile('\b', re.I)
INCLUDE_PATTERN = [r"\s*(\$\(find.*?\)[^\"]*)",
+ r"\s*(\$\(dirname\)[^\"]*)",
r"file=\"(.*?)\"",
r"textfile=\"(.*?)\"",
r"binfile=\"(.*?)\"",
@@ -257,7 +258,7 @@ def interpret_path(path, pwd='.'):
Otherwise the parameter itself normalized by :py:func:`os.path.normpath` will be returned.
:rtype: str
'''
- result = path.strip()
+ result = path.strip().replace("$(dirname)", pwd)
# try replace package name by package path
pkg_pattern = re.compile(r"\$\(find (.*?)\)/|pkg:\/\/(.*?)/|package:\/\/(.*?)/")
for groups in pkg_pattern.finditer(path):
@@ -302,7 +303,7 @@ def replace_paths(text, pwd='.'):
Like meth:interpret_path(), but replaces all matches in the text and retain other text.
'''
result = text
- path_pattern = re.compile(r"(\$\(find .*?\)/)|(pkg:\/\/.*?/)|(package:\/\/.*?/)")
+ path_pattern = re.compile(r"(\$\(dirname\)|\$\(find .*?\)/)|(pkg:\/\/.*?/)|(package:\/\/.*?/)")
for groups in path_pattern.finditer(text):
for index in range(groups.lastindex):
path = groups.groups()[index]
@@ -455,7 +456,8 @@ def find_included_files(string,
search_in_ext=SEARCH_IN_EXT,
resolve_args={},
unique_files=[],
- rec_depth=0):
+ rec_depth=0,
+ filename=None):
'''
If the `string` parameter is a valid file the content of this file will be parsed.
In other case the `string` is parsed to find included files.
@@ -492,6 +494,10 @@ def find_included_files(string,
count_nl = content[match.start():match.end()].count('\n')
content = content[:match.start()] + '\n' * count_nl + content[match.end():]
match = comment_pattern.search(content, match.start())
+ if filename is not None:
+ pwd = os.path.dirname(filename)
+ if '://' in pwd:
+ pwd = re.sub(r"^.*://[^/]*", "", pwd)
inc_files_forward_args = []
# replace the arguments and detect arguments for include-statements
resolve_args_intern = {}