def delete_log(cls, nodename, grpc_uri, auto_pw_request=False, user=None, pw=None): ''' Deletes the log file associated with the given node. :param str nodename: the name of the node (with name space) :param str grpc_uri: uri of the node manager daemon where to delete log :raise Exception: on errors while resolving host :see: :meth:`fkie_node_manager.is_local()` ''' try: nm.nmd().screen.delete_log(grpc_uri, [nodename]) except Exception as err: rospy.logwarn("delete log using SSH because of error: %s" % utf8(err)) host = get_hostname(grpc_uri) if nm.is_local(host): screenLog = screen.get_logfile(node=nodename) pidFile = screen.get_pidfile(node=nodename) roslog = screen.get_ros_logfile(nodename) if os.path.isfile(screenLog): os.remove(screenLog) if os.path.isfile(pidFile): os.remove(pidFile) if os.path.isfile(roslog): os.remove(roslog) else: try: # output ignored: output, error, ok _, stdout, _, ok = nm.ssh().ssh_exec(host, [nm.settings().start_remote_script, '--delete_logs', nodename], user, pw, auto_pw_request, close_stdin=True, close_stdout=False, close_stderr=True) if ok: stdout.readlines() stdout.close() except nm.AuthenticationRequest as e: raise nm.InteractionNeededError(e, cls.delete_log, {'nodename': nodename, 'grpc_uri': host, 'auto_pw_request': auto_pw_request, 'user': user, 'pw': pw})
def closeEvent(self, event): ''' Test the open files for changes and save this if needed. ''' changed = [] # get the names of all changed files for i in range(self.tabWidget.count()): w = self.tabWidget.widget(i) if w.document().isModified(): changed.append(self.__getTabName(w.filename)) if changed: # ask the user for save changes if self.isHidden(): buttons = MessageBox.Yes | MessageBox.No else: buttons = MessageBox.Yes | MessageBox.No | MessageBox.Cancel result = MessageBox.question(self, "Unsaved Changes", '\n\n'.join(["Save the file before closing?", '\n'.join(changed)]), buttons=buttons) if result == MessageBox.Yes: for i in range(self.tabWidget.count()): w = self.tabWidget.widget(i).save() self.graph_view.clear_cache() event.accept() elif result == MessageBox.No: event.accept() elif rospy.is_shutdown(): event.ignore() else: event.accept() if event.isAccepted(): self.storeSetting() nm.nmd().file.changed_file.disconnect(self.on_changed_file) nm.nmd().file.packages_available.connect(self._on_new_packages) self.finished_signal.emit(self.init_filenames)
def reload_current_path(self, clear_cache=False): ''' Reloads the current path. ''' self.expand_item(self._current_path, PathItem.FOLDER, clear_cache) if clear_cache: nm.nmd().clear_cache()
def clear_cache(self): if self._root_path: nm.nmd().clear_cache(self._root_path) self._created_tree = False self.graphTreeView.model().clear() crp = self._current_path self._current_path = None self.set_file(crp, self._root_path, force_rebuild=True)
def reload_current_path(self, clear_cache=False): ''' Reloads the current path. ''' self.expand_item(self._current_path, PathItem.FOLDER, clear_cache) if clear_cache: nm.nmd().clear_cache() nm.nmd().launch.reset_package_path_threaded(self._current_path)
def set_current_master(self, masteruri, mastername): self._current_master = masteruri.rstrip(os.path.sep) self._current_master_name = mastername if self._is_root(self._current_path): nm.nmd().file.list_path_threaded(self._current_path) if nmdurl.equal_uri(self._current_path, masteruri_from_master()): self._add_path(nmdurl.nmduri(self._current_master), PathItem.REMOTE_DAEMON, 0, 0, get_hostname(self._current_master_name))
def kill_screens(cls, node, grpc_url, auto_ok_request=True, user=None, pw=None): ''' Searches for the screen associated with the given node and kill this screens. :param str node: the name of the node those screen output to kill :param str grpc_url: the url of node manager daemon where the screen is running ''' if node is None or len(node) == 0: return False try: # get the available screens screens = nm.nmd().screen.get_screens(grpc_url, node) if screens: do_kill = True if auto_ok_request: from fkie_node_manager.detailed_msg_box import MessageBox result = MessageBox.question( None, "Kill SCREENs?", '\n'.join(list(screens.keys())), buttons=MessageBox.Ok | MessageBox.Cancel) if result == MessageBox.Ok: do_kill = True if do_kill: host = get_hostname(grpc_url) for sname, _nname in screens.items(): pid, _, _ = sname.partition('.') if pid: try: nm.nmd().monitor.kill_process( int(pid), grpc_url) # nm.starter()._kill_wo(host, int(pid), auto_ok_request, user, pw) except Exception: import traceback rospy.logwarn( "Error while kill screen (PID: %s) on host '%s': %s", utf8(pid), utf8(host), traceback.format_exc(1)) nm.nmd().screen.wipe_screens(grpc_url) # if nm.is_local(host): # SupervisedPopen([screen.SCREEN, '-wipe'], object_id='screen -wipe', description="screen: clean up the socket with -wipe") # else: # nm.ssh().ssh_exec(host, [screen.SCREEN, '-wipe'], close_stdin=True, close_stdout=True, close_stderr=True) except nm.AuthenticationRequest as e: raise nm.InteractionNeededError( e, cls.kill_screens, { 'node': node, 'grpc_url': grpc_url, 'auto_ok_request': auto_ok_request, 'user': user, 'pw': pw })
def expand_item(self, path, path_id, clear_cache=False): ''' Returns for the given item and path the file path if this is a file. Otherwise the folder will be expanded and None will be returned. :param str path: the real path of the item :param int path_id: the id of the path :param bool clear_cache: clear cache before reload :return: path of the launch file or None :rtype: str :raise Exception: if no path to given item was found ''' if path_id in [PathItem.NOTHING]: return None has_shift_mod = Qt.ShiftModifier & QApplication.keyboardModifiers() if path_id in [ PathItem.LAUNCH_FILE, PathItem.CFG_FILE, PathItem.PROFILE, PathItem.FILE, PathItem.RECENT_FILE, PathItem.LAUNCH_FILE ]: if not has_shift_mod: return path root = self.invisibleRootItem() while root.rowCount(): root.removeRow(0) self.pyqt_workaround.clear() if has_shift_mod: if path_id in [ PathItem.LAUNCH_FILE, PathItem.CFG_FILE, PathItem.PROFILE, PathItem.FILE, PathItem.RECENT_FILE, PathItem.LAUNCH_FILE ]: self._current_path = os.path.dirname(path) else: self._current_path = nmdurl.nmduri() else: if path_id in [PathItem.ROOT]: surl, spath = nmdurl.split(path, with_scheme=True) if self._is_root(path) or spath in ['', os.path.sep]: self._current_path = nmdurl.nmduri() elif self._is_ros_root(path): self._current_path = surl else: dir_path = os.path.dirname(spath) self._current_path = nmdurl.join(surl, dir_path) elif self._current_path != path: self._current_path = path self._add_path(self._current_path, PathItem.ROOT, 0, 0, 'loading...') nm.nmd().file.list_path_threaded(self._current_path, clear_cache) # TODO: add functionality to go deep automatically # else: # key_mod = QApplication.keyboardModifiers() # onestep = False # if key_mod & Qt.ControlModifier: # onestep = True # root_path, items = self._moveDown(path, onestep) # self._setNewList((root_path, items)) return None
def search_for_node(self, search_text, path, recursive=False, args={}, count=1): ''' Searches for node in this document and all included files. :param str search_text: node to find with 'name="' prefix :return: the list with all files contain the text :rtype: [str, ...] ''' if path and not (path.endswith('.launch') or path.find('.launch.') > 0): return if not self._isrunning: return data = self._get_text(path) launch_node = self._get_launch_element(data, path) if launch_node is None: # something goes wrong while parse XML content # backup solution to search without resolve arguments self.search(search_text, path, recursive, args, count) else: # read XML content and update the arguments rospy.logdebug("search for node '%s' in %s with args: %s, recursive: %s" % (search_text, path, args, recursive)) resolve_args = dict(args) if not resolve_args: resolve_args.update(nm.nmd().launch.launch_args(path)) my_resolved_args = self._resolve_args(launch_node, resolve_args, path) # replace arguments and search for node in data search_for_name = search_text.replace('name="', '').replace('"', '') occur_idx = 0 for aname, _rname, span in self._next_node_name(data, search_for_name, my_resolved_args, path): # found, now test in XML for if and unless statements if self._check_node_conditions(launch_node, search_for_name, occur_idx, my_resolved_args, path): self._found += 1 self.search_result_signal.emit(search_text, True, path, span[0], span[1], -1, aname) else: self.warning_signal.emit("%s in %s ignored because of conditions." % (search_text, path)) occur_idx += 1 if self._isrunning and recursive: queue = [] inc_files = nm.nmd().launch.get_included_files(path, False, include_args=args, search_in_ext=['.launch', '.xml']) # read first all included files in current file for inc_file in inc_files: if not self._isrunning: return if inc_file.exists: queue.append((search_text, inc_file.inc_path, recursive, inc_file.args)) elif inc_file.inc_path.endswith('.launch') or inc_file.inc_path.find('.launch.') > 0: rospy.logwarn("skip parsing of not existing included file: %s" % inc_file.inc_path) # search in all files for search_text, inc_path, recursive, include_args in queue: new_dict = dict(my_resolved_args) new_dict.update(include_args) # skip search in not launch files if inc_path.endswith('.launch') or inc_path.find('.launch.') > 0: self.search_for_node(search_text, inc_path, recursive, new_dict, count + 1) if self._path == path and self._found == 0: self.warning_signal.emit("not found '%s' in %s (%srecursive)" % (search_text, path, '' if recursive else 'not '))
def keyPressEvent(self, event): ''' Enable the mouse tracking by X{setMouseTracking()} if the control key is pressed. ''' if self.isReadOnly(): if event.key() == Qt.Key_F5: nm.nmd().file.get_file_content_threaded(self.filename) else: event.accept() return if event.key() == Qt.Key_Control or event.key() == Qt.Key_Shift: self.setMouseTracking(True) if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_7: self.commentText() elif event.modifiers( ) == Qt.ControlModifier | Qt.ShiftModifier and event.key( ) == Qt.Key_Slash: self.commentText() elif event.modifiers( ) == Qt.ControlModifier | Qt.ShiftModifier and event.key() == Qt.Key_F: if isinstance(self.hl, XmlHighlighter): self.toprettyxml() else: self.toprettyyaml() elif event.modifiers() == Qt.AltModifier and event.key( ) == Qt.Key_Space: ext = os.path.splitext(self.filename) if ext[1] in self.CONTEXT_FILE_EXT: menu = self._create_context_substitution_menu(False) if menu is None: menu = self._create_context_menu_for_tag() if menu: menu.exec_( self.mapToGlobal(self.cursorRect().bottomRight())) elif event.key() != Qt.Key_Escape: # handle the shifting of the block if event.modifiers() == Qt.NoModifier and event.key( ) == Qt.Key_Tab: self.shiftText() elif event.modifiers() == Qt.ShiftModifier and event.key( ) == Qt.Key_Backtab: self.shiftText(back=True) else: event.accept() if event.key() in [Qt.Key_Enter, Qt.Key_Return]: ident = self.getIdentOfCurretLine() QTextEdit.keyPressEvent(self, event) if event.key() in [Qt.Key_Enter, Qt.Key_Return]: self.indentCurrentLine(ident - self.getIdentOfCurretLine()) else: QTextEdit.keyPressEvent(self, event)
def _rosclean_wo(self, grpc_uri, auto_pw_request=False, user=None, pw=None): try: nm.nmd().screen.rosclean(grpc_uri) except Exception as err: host = get_hostname(grpc_uri) if nm.is_local(host): rospy.loginfo("rosclean purge on localhost!") cmd = nm.settings().terminal_cmd(['rosclean purge -y'], "rosclean") SupervisedPopen(shlex.split(cmd), object_id="rosclean", description="rosclean") else: rospy.logwarn("use SSH to run 'rosclean' because of error: %s" % utf8(err)) # kill on a remote machine cmd = ['rosclean purge -y'] _ = nm.ssh().ssh_x11_exec(host, cmd, 'rosclean purge on %s' % host, user)
def transfer_file_nmd(cls, grpc_url, path, auto_pw_request=False, user=None, pw=None): ''' Copies the given file to the remote host. Uses caching of remote paths. :param str grpc_url: destination grpc server :param str path: file to transfer ''' try: nm.nmd().file.copy(path, grpc_url) except Exception as err: host = get_hostname(grpc_url) _uri, path = nmdurl.split(path) rospy.logwarn("use SSH to transfer file '%s' to '%s', because of error: %s" % (path, host, utf8(err))) cls.transfer_files(host, path, auto_pw_request, user, pw)
def paste_from_clipboard(self): ''' Copy the file or folder to new position... ''' if QApplication.clipboard().mimeData().hasText( ) and self._current_path: text = QApplication.clipboard().mimeData().text() if self.current_path and text.startswith('grpc://'): basename = os.path.basename(text) dest_path = os.path.join(self._current_path, basename) try: nm.nmd().file.copy(text, dest_path) self.reload_current_path(clear_cache=True) except Exception: import traceback print(traceback.format_exc())
def find_default_args(self, path, inc_args): ''' Searches for args with default value not overwritten by including file. :param str path: file content or a launch file path :param dict(str,str) inc_args: a dictionary with arguments set while include the given path. :return: a dictinary with default arguments not overwriting while include. :rtype: dict(str: str) ''' not_set_args = {} if path and not (path.endswith('.launch') or path.find('.launch.') > 0): return not_set_args if rospy.is_shutdown(): return not_set_args try: # get file content _, _, data = nm.nmd().file.get_file_content(path) launch_node = None # create xml node xml_nodes = minidom.parseString(data.encode('utf-8')).getElementsByTagName('launch') if xml_nodes: launch_node = xml_nodes[-1] if launch_node is not None: # read XML content and get default arguments default_args = get_internal_args(data, only_default=True) for arg_in_file, arg_value in default_args.items(): if arg_in_file not in inc_args: not_set_args[arg_in_file] = arg_value except Exception as err: msg = "can't get default arguments for %s: %s" % (path, utf8(err)) self.error.emit(msg) rospy.logwarn(msg) return not_set_args
def run(self): ''' ''' try: self.info_signal.emit("build tree: start for %s" % self.root_path, False) result = [] filelist = nm.nmd().launch.get_included_files(self.root_path, recursive=True, search_in_ext=nm.settings().SEARCH_IN_EXT) for inc_file in filelist: rospy.logdebug("build tree: append file: %s" % inc_file) inc_file.unset_default_args = self.find_default_args(inc_file.inc_path, inc_file.args) result.append(inc_file) if not inc_file.exists: self.info_signal.emit("build tree: skip parse %s, not exist" % inc_file.inc_path, True) self.graph.emit(result) except exceptions.GrpcTimeout as tout: rospy.logwarn("Build launch tree failed! Daemon not responded within %.2f seconds while" " get configuration file: %s\nYou can try to increase" " the timeout for GRPC requests in node manager settings." % (nm.settings().timeout_grpc, tout.remote)) self.error.emit('failed: timeout') except Exception: import traceback # print("Error while parse launch file for includes:\n\t%s" % traceback.format_exc()) formatted_lines = traceback.format_exc(1).splitlines() try: rospy.logwarn("Error while parse launch file for includes:\n\t%s", formatted_lines[-5]) except Exception: pass self.error.emit('failed: %s' % formatted_lines[-1])
def package_name(path): ''' Returns for given directory a tuple of package name and package path or None values. The results are cached! :rtype: tuple(name, path) ''' return nm.nmd().file.package_name(path)
def on_error_on_path(self, gpath): if gpath == self._current_search or gpath == self.launchlist_model.current_path: self.ui_search_line.set_process_active(False) if self.launchlist_model.is_in_root: self._reload_timer = threading.Timer( 2., nm.nmd().file.list_path_threaded) self._reload_timer.start()
def __init__(self, filename, parent=None): self.parent = parent QTextEdit.__init__(self, parent) self.setObjectName('Editor - %s' % filename) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_custom_context_menu) # self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.setAcceptRichText(False) font = QFont() font.setFamily("Fixed".decode("utf-8")) font.setPointSize(12) self.setFont(font) self.setLineWrapMode(QTextEdit.NoWrap) self.setTabStopWidth(25) self.setAcceptRichText(False) self.setCursorWidth(2) self.setFontFamily("courier new") self.setProperty("backgroundVisible", True) bg_style = "QTextEdit { background-color: #fffffc;}" self.setStyleSheet("%s" % (bg_style)) self.setTextColor(QColor(0, 0, 0)) self.regexp_list = [QRegExp("\\binclude\\b"), QRegExp("\\btextfile\\b"), QRegExp("\\bfile\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"), QRegExp("\\bvalue=.*package:\/\/\\b"), QRegExp("\\bvalue=.*\$\(find\\b"), QRegExp("\\bargs=.*\$\(find\\b"), QRegExp("\\bdefault=.*\$\(find\\b")] self.filename = filename self.file_mtime = 0 # f = QFile(filename) # if f.open(QIODevice.ReadOnly | QIODevice.Text): # self.file_info = QFileInfo(filename) # self.setText(unicode(f.readAll(), "utf-8")) self.path = '.' # enables drop events self.setAcceptDrops(True) # variables for threaded search self._search_thread = None self._stop = False self._internal_args = {} ext = os.path.splitext(filename) if self.filename: self.setText("") _, self.file_mtime, file_content = nm.nmd().file.get_file_content(filename) if ext[1] in ['.launch', '.xml']: self._internal_args = get_internal_args(file_content) self.setText(file_content) self._is_launchfile = False if ext[1] in ['.launch', '.xml', '.xacro', '.srdf', '.urdf']: if ext[1] in ['.launch']: self._is_launchfile = True self.hl = XmlHighlighter(self.document(), is_launch=False) self.cursorPositionChanged.connect(self._document_position_changed) else: self.hl = YamlHighlighter(self.document())
def search(self, search_text, path, recursive=False, args={}, count=1): ''' Searches for given text in this document and all included files. :param str search_text: text to find :return: the list with all files contain the text :rtype: [str, ...] ''' if not self._isrunning: return data = self._get_text(path) pos = data.find(search_text) slen = len(search_text) while pos != -1 and self._isrunning: if self._isrunning: doemit = True line = self._strip_text(data, pos) if self._only_launch: doemit = path.endswith( '.launch') or path.find('.launch.') > 0 if doemit: self._found += 1 self.search_result_signal.emit( search_text, True, path, pos, pos + len(search_text), data.count('\n', 0, pos) + 1, line) if self._count_results > 0 and self._count_results < self._found: # break search if found the requested count of occurrences return pos += slen pos = data.find(search_text, pos) if self._isrunning: if recursive: queue = [] inc_files = nm.nmd().launch.get_included_files( path, False, include_args=args) # read first all included files in current file for inc_file in inc_files: if not self._isrunning: return if inc_file.exists: queue.append((search_text, inc_file.inc_path, recursive, inc_file.args)) # search in all files for search_text, inc_path, recursive, include_args in queue: new_dict = dict(args) new_dict.update(include_args) # test search string for 'name=' and skip search in not launch files if self._only_launch or inc_path.endswith( '.launch') or path.find('.launch.') > 0: self.search(search_text, inc_path, recursive, new_dict, count + 1) if self._path == path and self._found == 0: self.warning_signal.emit( "not found '%s' in %s (%srecursive)" % (search_text, path, '' if recursive else 'not '))
def _refill_tree(self, tree, create_tree=True): deep = 0 file_dsrc = self._root_path try: file_dsrc = os.path.basename(self._root_path) except Exception: pass self.setWindowTitle("Include Graph - %s" % file_dsrc) if not self._created_tree and create_tree: has_none_packages = False self.graphTreeView.model().clear() pkg, _ = package_name(os.path.dirname(self._root_path)) if pkg is None: has_none_packages = True itemstr = '%s [%s]' % (os.path.basename(self._root_path), pkg) inc_item = QStandardItem('%s' % itemstr) inc_item.setData(self.ITEM_TYPE_INC_FILE, self.ITEM_TYPE) inc_item.setData(self._root_path, self.DATA_FILE) inc_item.setData(-1, self.DATA_LINE) inc_item.setData(self._root_path, self.DATA_INC_FILE) inc_item.setData(deep, self.DATA_LEVEL) # add included arguments for root file launch_args = nm.nmd().launch.launch_args(self._root_path) if launch_args: arg_item = QStandardItem('arguments') arg_item.setData(self.ITEM_TYPE_INC_GROUP_ARG, self.ITEM_TYPE) for da_name, da_value in launch_args.items(): da_item = QStandardItem('<arg>%s: %s' % (da_name, da_value)) da_item.setData(self.ITEM_TYPE_INC_ARG, self.ITEM_TYPE) da_item.setData(self._root_path, self.DATA_FILE) da_item.setData(da_name, self.DATA_ARG_NAME) arg_item.appendRow(da_item) inc_item.appendRow(arg_item) self._append_items(inc_item, deep, tree) self.graphTreeView.model().appendRow(inc_item) # self.graphTreeView.expand(self.graphTreeView.model().indexFromItem(inc_item)) self._created_tree = True self.has_none_packages = has_none_packages items = self.graphTreeView.model().match(self.graphTreeView.model().index(0, 0), self.DATA_INC_FILE, self._current_path, 10, Qt.MatchRecursive) first = True self.graphTreeView.selectionModel().clear() for item in items: if first: self._current_deep = item.data(self.DATA_LEVEL) first = False self.graphTreeView.selectionModel().select(item, QItemSelectionModel.Select) self.graphTreeView.expandAll() # collapse argument nodes proxy = self.graphTreeView.model() for row in range(proxy.rowCount()): index = proxy.index(row, 0) item = proxy.itemFromIndex(index) self._collapse_args(item) self.finished_signal.emit()
def _get_text(self, path): result = '' try: result = self._path_text[path] except KeyError: try: _, _, data = nm.nmd().file.get_file_content(path) result = utf8(data) except Exception as err: rospy.logwarn("can't get content: %s" % (utf8(err))) return result
def run(self): ''' ''' if self._grpc_url: try: self._result = nm.nmd().file.get_package_binaries( self._package, nmdurl.nmduri(self._grpc_url)) if not self._canceled: self.binaries_signal.emit(self._package, self._result) except Exception: import traceback print(traceback.format_exc())
def file_changed(self, mtime): if self.file_mtime != mtime: self.file_mtime = mtime result = MessageBox.question(self, "File changed", "File was changed, reload?", buttons=MessageBox.Yes | MessageBox.No) if result == MessageBox.Yes: try: _, self.file_mtime, file_content = nm.nmd().file.get_file_content(self.filename, force=True) self.setText(file_content) self.document().setModified(False) self.textChanged.emit() except Exception as err: MessageBox.critical(self, "Error", "Cannot open launch file %s" % self.filename, utf8(err))
def __init__(self, parent=None, progress_queue=None, viewobj=None): ''' Creates a new list model. ''' QStandardItemModel.__init__(self, parent) self.viewobj = viewobj self.setColumnCount(len(LaunchListModel.header)) self.setHorizontalHeaderLabels( [label for label, _width in LaunchListModel.header]) self.pyqt_workaround = dict( ) # workaround for using with PyQt: store the python object to keep the defined attributes in the TopicItem subclass self.items = [] self._roots = {} self._current_path = nmdurl.nmduri() self._current_master = masteruri_from_master() self._current_master_name = '' self.ros_root_paths = {} # {url: [root pasth(str)]} self.ros_root_paths[self._current_path] = [ os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':') ] self._progress_queue = progress_queue nm.nmd().file.listed_path.connect(self._listed_path) nm.nmd().file.packages_available.connect(self._on_new_packages) nm.nmd().file.error.connect(self._nmd_error)
def paste_from_clipboard(self): ''' Copy the file or folder to new position... ''' if QApplication.clipboard().mimeData().hasText( ) and self._current_path: text = QApplication.clipboard().mimeData().text() if self.current_path and text.startswith('grpc://'): basename = os.path.basename(text) dest_path = os.path.join(self._current_path, basename) try: if text == dest_path: dest_path = self._autorename(dest_path) rospy.logdebug("Autorename destination from %s to %s" % (text, dest_path)) rospy.logdebug("Copy %s to %s" % (text, dest_path)) nm.nmd().file.copy(text, dest_path) self.reload_current_path(clear_cache=True) except Exception as err: MessageBox.warning(None, "Copy failed", "Copy failed: %s" % utf8(err)) import traceback print(traceback.format_exc())
def resolve_pkg(pkg, grpc_url): ''' splits pkg url (pkg://package/launch) into package and launch file part and replace package by path. :rtype: str ''' if pkg.startswith('pkg://'): url = pkg.replace('pkg://', '') splits = url.split(os.path.sep, 1) if len(splits) == 2: packages = nm.nmd().file.get_packages(grpc_url) for path, pkgname in packages.items(): if pkgname == splits[0]: return os.path.join(path, splits[1]) raise Exception('invalid package url to split: %s' % pkg)
def save(self, force=False): ''' Saves changes to the file. :return: saved, errors, msg :rtype: bool, bool, str ''' if self.isReadOnly(): return False, True, "Cannot save, the content was not loaded properly!" if force or self.document().isModified(): try: mtime = nm.nmd().file.save_file(self.filename, self.toPlainText().encode('utf-8'), 0 if force else self.file_mtime) self.file_mtime = mtime if mtime == 0: MessageBox.warning(self, "Warning", "File not saved and not error reported: %s" % os.path.basename(self.filename)) self.document().setModified(mtime == 0) ext = os.path.splitext(self.filename) # validate the xml structure of the launch files if ext[1] in self.CONTEXT_FILE_EXT: imported = False try: from lxml import etree imported = True parser = etree.XMLParser() etree.fromstring(self.toPlainText().encode('utf-8'), parser) except Exception as e: if imported: self.markLine(e.position[0]) return True, True, utf8(e) # validate the yaml structure of yaml files elif ext[1] in self.YAML_VALIDATION_FILES: try: import ruamel.yaml ruamel.yaml.load(self.toPlainText().encode('utf-8'), Loader=ruamel.yaml.Loader) except ruamel.yaml.MarkedYAMLError as e: return True, True, "YAML validation error: %s" % e return True, False, '' except IOError as ioe: if ioe.errno in [file_item.EFILE_CHANGED, file_item.EFILE_REMOVED]: result = MessageBox.question(self, "Changed file", "%s\n%s" % (utf8(ioe), "Save anyway?"), buttons=MessageBox.Yes | MessageBox.No) if result == MessageBox.Yes: return self.save(force=True) else: return False, True, utf8(ioe) except Exception as e: print(traceback.format_exc()) return False, True, utf8(e) return False, False, ''
def show_packages(self, pattern): try: root = self.invisibleRootItem() while root.rowCount(): root.removeRow(0) self.pyqt_workaround.clear() items = [] currurl = self.current_grpc for url, packages in nm.nmd().file.get_packages().items(): if url == currurl: for path, name in packages.items(): if pattern in name: items.append((path, PathItem.PACKAGE, 0, 0, name)) self._set_new_list(self._current_path, items, add_history=False) except Exception: import traceback print(traceback.format_exc(2))
def __init__(self, filename, parent=None): self.parent = parent QTextEdit.__init__(self, parent) self.setObjectName('Editor - %s' % filename) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_custom_context_menu) # self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.setAcceptRichText(False) font = QFont() font.setFamily('Fixed') font.setPointSize(12) self.setFont(font) self.setLineWrapMode(QTextEdit.NoWrap) self.setTabStopWidth(25) self.setAcceptRichText(False) self.setCursorWidth(2) self.setFontFamily("courier new") self.setProperty("backgroundVisible", True) bg_style = "QTextEdit { background-color: #fffffc;}" self.setStyleSheet("%s" % (bg_style)) self.setTextColor(QColor(0, 0, 0)) self.regexp_list = [ QRegExp("\\binclude\\b"), QRegExp("\\btextfile\\b"), QRegExp("\\bfile\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"), QRegExp("\\bvalue=.*package:\/\/\\b"), QRegExp("\\bvalue=.*\$\(find\\b"), QRegExp("\\bargs=.*\$\(find\\b"), QRegExp("\\bdefault=.*\$\(find\\b") ] self.filename = filename self.file_mtime = 0 # f = QFile(filename) # if f.open(QIODevice.ReadOnly | QIODevice.Text): # self.file_info = QFileInfo(filename) # self.setText(unicode(f.readAll(), "utf-8")) self.path = '.' # variables for threaded search self._search_thread = None self._stop = False self._internal_args = {} self._ext = os.path.splitext(filename)[1] self.setText("Loading file content ... press F5 to reload!") self.setReadOnly(True) self._to_select = [] nm.nmd().file.file_content.connect(self._apply_file_content) nm.nmd().file.error.connect(self._on_nmd_error) if self.filename: nm.nmd().file.get_file_content_threaded(filename)
def setData(self, value, role=Qt.EditRole): if role == Qt.EditRole: # rename the file or folder if (self.name != value or self._isnew) and self.id in [ self.RECENT_FILE, self.LAUNCH_FILE, self.RECENT_PROFILE, self.PROFILE, self.CFG_FILE, self.FOLDER ]: if self.name != value: # some sanity checks if self.model()._exists(value): result = MessageBox.question( self.model().viewobj, "File exists", "File '%s' exists. Override?" % value, buttons=MessageBox.Yes | MessageBox.No) if result == MessageBox.No: return QStandardItem.setData(self, value, role) if self.id not in [self.FOLDER]: _filename, file_extension = os.path.splitext(value) if file_extension not in nm.settings( ).launch_view_file_ext: result = MessageBox.question( self.model().viewobj, "Unknown extension", "New name has unknown extension '%s'. Rename anyway?" % file_extension, buttons=MessageBox.Yes | MessageBox.No) if result == MessageBox.No: return QStandardItem.setData(self, value, role) new_path = os.path.join(os.path.dirname(self.path), value) try: # save a new file or rename existing file? content = b'' new_id = self._identify_path_on_ext(new_path, self.id) if self._isnew: if new_id in [self.FOLDER]: nm.nmd().file.new(new_path, 1) elif new_id in [self.LAUNCH_FILE]: content = ( b'<launch>\n' b' <arg name="robot_ns" default="my_robot"/>\n' b' <group ns="$(arg robot_ns)">\n' b' <node pkg="my_pkg" type="my_node" name="my_name" >\n' b' <param name="capability_group" value="MY_GROUP"/>\n' b' </node>\n' b' </group>\n' b'</launch>\n') nm.nmd().file.save_file(new_path, content, 0) else: nm.nmd().file.new(new_path, 0) self._isnew = False else: nm.nmd().file.rename(self.path, new_path) # check for new file extension if new_id != self.id: self.id = new_id self._update_icon() if self.name != value and self.id in [ self.RECENT_FILE, self.RECENT_PROFILE ]: # update in history nm.settings().launch_history_add(new_path, replace=self.path) self.name = value self._path = new_path except Exception as err: import traceback rospy.logwarn("Error while save new file: %s" % traceback.format_exc()) MessageBox.warning(None, "Rename failed", utf8(err)) return QStandardItem.setData(self, value, role)