Package node_manager_fkie :: Package editor :: Module graph_view
[frames] | no frames]

Source Code for Module node_manager_fkie.editor.graph_view

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from python_qt_binding import loadUi 
 34  from python_qt_binding.QtCore import QObject, Signal, Qt 
 35  from python_qt_binding.QtGui import QStandardItemModel, QStandardItem 
 36  import os 
 37  import threading 
 38  import rospy 
 39   
 40  from node_manager_fkie.common import package_name 
 41  from node_manager_fkie.html_delegate import HTMLDelegate 
 42  from node_manager_fkie.launch_config import LaunchConfig 
 43   
 44  try: 
 45      from python_qt_binding.QtGui import QCheckBox, QFrame, QLabel, QTreeWidget, QTreeWidgetItem, QPushButton, QGroupBox, QDockWidget 
 46      from python_qt_binding.QtGui import QHBoxLayout, QVBoxLayout, QSpacerItem, QSplitter, QSizePolicy, QAbstractItemView, QItemSelectionModel 
 47  except: 
 48      from python_qt_binding.QtWidgets import QCheckBox, QFrame, QLabel, QTreeWidget, QTreeWidgetItem, QPushButton, QGroupBox, QDockWidget 
 49      from python_qt_binding.QtWidgets import QHBoxLayout, QVBoxLayout, QSpacerItem, QSplitter, QSizePolicy, QAbstractItemView 
 50      from python_qt_binding.QtCore import QItemSelectionModel 
 51   
 52   
 53  GRAPH_CACHE = {} 
 54  CHACHE_MUTEX = threading.RLock() 
 55   
 56   
57 -class GraphViewWidget(QDockWidget):
58 ''' 59 A frame to find text in the Editor. 60 ''' 61 load_signal = Signal(str, bool) 62 ''' @ivar: filename of file to load, True if insert after the current open tab''' 63 goto_signal = Signal(str, int) 64 ''' @ivar: filename, line to go''' 65 DATA_FILE = Qt.UserRole + 1 66 DATA_LINE = Qt.UserRole + 2 67 DATA_INC_FILE = Qt.UserRole + 3 68 DATA_LEVEL = Qt.UserRole + 4 69
70 - def __init__(self, tabwidget, parent=None):
71 QDockWidget.__init__(self, "LaunchGraph", parent) 72 graph_ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'GraphDockWidget.ui') 73 loadUi(graph_ui_file, self) 74 self.setObjectName('LaunchGraph') 75 self.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) 76 self._tabwidget = tabwidget 77 self._current_path = None 78 self._root_path = None 79 self._current_deep = 0 80 self.graphTreeView.setSelectionBehavior(QAbstractItemView.SelectRows) 81 model = QStandardItemModel() 82 self.graphTreeView.setModel(model) 83 self.graphTreeView.setUniformRowHeights(True) 84 self.graphTreeView.header().hide() 85 self.htmlDelegate = HTMLDelegate() 86 self.graphTreeView.setItemDelegateForColumn(0, self.htmlDelegate) 87 self.graphTreeView.activated.connect(self.on_activated) 88 self.graphTreeView.clicked.connect(self.on_clicked) 89 self._created_tree = False 90 self._refill_tree([], [], False)
91
92 - def clear_cache(self):
93 with CHACHE_MUTEX: 94 GRAPH_CACHE.clear() 95 self._created_tree = False 96 self.graphTreeView.model().clear() 97 crp = self._current_path 98 self._current_path = None 99 self.set_file(crp, self._root_path)
100
101 - def set_file(self, current_path, root_path):
102 self._root_path = root_path 103 if self._current_path != current_path: 104 self._current_path = current_path 105 # TODO: run analyzer/path parser in a new thread 106 self.setWindowTitle("Include Graph - loading...") 107 self._fill_graph_thread = GraphThread(current_path, root_path) 108 self._fill_graph_thread.graph.connect(self._refill_tree) 109 self._fill_graph_thread.start()
110
111 - def find_parent_file(self):
112 selected = self.graphTreeView.selectionModel().selectedIndexes() 113 for index in selected: 114 item = self.graphTreeView.model().itemFromIndex(index.parent()) 115 self.load_signal.emit(item.data(self.DATA_INC_FILE), self._current_deep < item.data(self.DATA_LEVEL))
116
117 - def on_activated(self, index):
118 item = self.graphTreeView.model().itemFromIndex(index) 119 self.load_signal.emit(item.data(self.DATA_INC_FILE), self._current_deep < item.data(self.DATA_LEVEL))
120
121 - def on_clicked(self, index):
122 item = self.graphTreeView.model().itemFromIndex(index) 123 self.goto_signal.emit(item.data(self.DATA_FILE), item.data(self.DATA_LINE))
124
125 - def enable(self):
126 self.setVisible(True) 127 self.raise_() 128 self.activateWindow() 129 self.graphTreeView.setFocus()
130
131 - def _refill_tree(self, included_from, includes, create_tree=True):
132 deep = 0 133 file_dsrc = self._root_path 134 try: 135 file_dsrc = os.path.basename(self._root_path) 136 except Exception: 137 pass 138 self.setWindowTitle("Include Graph - %s" % file_dsrc) 139 if not self._created_tree and create_tree: 140 with CHACHE_MUTEX: 141 if self._root_path in GRAPH_CACHE: 142 pkg, _ = package_name(os.path.dirname(self._root_path)) 143 itemstr = '%s [%s]' % (os.path.basename(self._root_path), pkg) 144 inc_item = QStandardItem('%s' % itemstr) 145 inc_item.setData(self._root_path, self.DATA_FILE) 146 inc_item.setData(-1, self.DATA_LINE) 147 inc_item.setData(self._root_path, self.DATA_INC_FILE) 148 inc_item.setData(deep, self.DATA_LEVEL) 149 self._append_items(inc_item, deep + 1) 150 self.graphTreeView.model().appendRow(inc_item) 151 # self.graphTreeView.expand(self.graphTreeView.model().indexFromItem(inc_item)) 152 self._created_tree = True 153 items = self.graphTreeView.model().match(self.graphTreeView.model().index(0, 0), self.DATA_INC_FILE, self._current_path, 10, Qt.MatchRecursive) 154 first = True 155 self.graphTreeView.selectionModel().clearSelection() 156 for item in items: 157 if first: 158 self._current_deep = item.data(self.DATA_LEVEL) 159 self.graphTreeView.selectionModel().select(item, QItemSelectionModel.SelectCurrent) 160 first = False 161 else: 162 self.graphTreeView.selectionModel().select(item, QItemSelectionModel.Select) 163 self.graphTreeView.expandAll()
164
165 - def _append_items(self, item, deep):
166 path = item.data(self.DATA_INC_FILE) 167 if not path: 168 path = item.data(self.DATA_FILE) 169 if path in GRAPH_CACHE: 170 for inc_lnr, inc_path in GRAPH_CACHE[path]: 171 pkg, _ = package_name(os.path.dirname(inc_path)) 172 itemstr = '%s [%s]' % (os.path.basename(inc_path), pkg) 173 inc_item = QStandardItem('%d: %s' % (inc_lnr + 1, itemstr)) 174 inc_item.setData(path, self.DATA_FILE) 175 inc_item.setData(inc_lnr + 1, self.DATA_LINE) 176 inc_item.setData(inc_path, self.DATA_INC_FILE) 177 inc_item.setData(deep, self.DATA_LEVEL) 178 self._append_items(inc_item, deep + 1) 179 item.appendRow(inc_item)
180 181
182 -class GraphThread(QObject, threading.Thread):
183 ''' 184 A thread to parse file for includes 185 ''' 186 graph = Signal(list, list) 187 ''' 188 @ivar: graph is a signal, which emit two list for files include the current path and a list with included files. 189 Each entry is a tuple of the line number and path. 190 ''' 191
192 - def __init__(self, current_path, root_path):
193 ''' 194 :param root_path: the open root file 195 :type root_path: str 196 :param current_path: current shown file 197 :type current_path: str 198 ''' 199 QObject.__init__(self) 200 threading.Thread.__init__(self) 201 self.setDaemon(True) 202 self.current_path = current_path 203 self.root_path = root_path
204
205 - def run(self):
206 ''' 207 ''' 208 try: 209 includes = self._get_includes(self.current_path) 210 included_from = [] 211 incs = self._get_includes(self.root_path) 212 included_from = self._find_inc_file(self.current_path, incs, self.root_path) 213 self.graph.emit(included_from, includes) 214 except Exception: 215 import traceback 216 formatted_lines = traceback.format_exc(1).splitlines() 217 print "Error while parse launch file for includes:\n\t%s" % traceback.format_exc() 218 try: 219 rospy.logwarn("Error while parse launch file for includes:\n\t%s", formatted_lines[-1]) 220 except Exception: 221 pass
222
223 - def _get_includes(self, path):
224 result = [] 225 with CHACHE_MUTEX: 226 if path: 227 if path in GRAPH_CACHE: 228 result = GRAPH_CACHE[path] 229 else: 230 result = LaunchConfig.included_files(path, recursive=False, unique=False) 231 GRAPH_CACHE[path] = result 232 return result
233
234 - def _find_inc_file(self, filename, files, root_path):
235 result = [] 236 for f in files: 237 if filename == f[1]: 238 result.append((f[0], root_path)) 239 else: 240 inc_files = self._get_includes(f[1]) 241 result += self._find_inc_file(filename, inc_files, f[1]) 242 return result
243