Package node_manager_fkie :: Module launch_files_widget
[frames] | no frames]

Source Code for Module node_manager_fkie.launch_files_widget

  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  import os 
 33  from datetime import datetime 
 34   
 35  from python_qt_binding import QtGui 
 36  from python_qt_binding import QtCore 
 37  from python_qt_binding import loadUi 
 38   
 39  import rospy 
 40  from rosgraph_msgs.msg import Log 
 41   
 42  import node_manager_fkie as nm 
 43  from .common import masteruri_from_ros, package_name 
 44  from .detailed_msg_box import WarningMessageBox 
 45  from .launch_list_model import LaunchListModel, LaunchItem 
 46  from .progress_queue import ProgressQueue, ProgressThread 
 47  from .parameter_dialog import ParameterDialog 
 48   
49 -class LaunchFilesWidget(QtGui.QDockWidget):
50 ''' 51 Launch file browser. 52 ''' 53 54 load_signal = QtCore.Signal(str) 55 ''' load the launch file ''' 56 load_as_default_signal = QtCore.Signal(str, str) 57 ''' load the launch file as default (path, host) ''' 58 edit_signal = QtCore.Signal(list) 59 ''' list of paths to open in an editor ''' 60 transfer_signal = QtCore.Signal(list) 61 ''' list of paths selected for transfer ''' 62
63 - def __init__(self, parent=None):
64 ''' 65 Creates the window, connects the signals and init the class. 66 ''' 67 QtGui.QDockWidget.__init__(self, parent) 68 # initialize parameter 69 self.__current_path = os.path.expanduser('~') 70 # load the UI file 71 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'LaunchFilesDockWidget.ui') 72 loadUi(ui_file, self) 73 # initialize the view for the launch files 74 self.launchlist_model = LaunchListModel() 75 self.launchlist_proxyModel = QtGui.QSortFilterProxyModel(self) 76 self.launchlist_proxyModel.setSourceModel(self.launchlist_model) 77 self.xmlFileView.setModel(self.launchlist_proxyModel) 78 self.xmlFileView.setAlternatingRowColors(True) 79 self.xmlFileView.activated.connect(self.on_launch_selection_activated) 80 self.xmlFileView.setDragDropMode(QtGui.QAbstractItemView.DragOnly) 81 self.xmlFileView.setDragEnabled(True) 82 sm = self.xmlFileView.selectionModel() 83 sm.selectionChanged.connect(self.on_xmlFileView_selection_changed) 84 # self.searchPackageLine.setVisible(False) 85 self.searchPackageLine.textChanged.connect(self.set_package_filter) 86 self.searchPackageLine.focusInEvent = self._searchline_focusInEvent 87 # connect to the button signals 88 self.refreshXmlButton.clicked.connect(self.on_refresh_xml_clicked) 89 self.editXmlButton.clicked.connect(self.on_edit_xml_clicked) 90 self.newXmlButton.clicked.connect(self.on_new_xml_clicked) 91 self.openXmlButton.clicked.connect(self.on_open_xml_clicked) 92 self.transferButton.clicked.connect(self.on_transfer_file_clicked) 93 self.loadXmlButton.clicked.connect(self.on_load_xml_clicked) 94 self.loadXmlAsDefaultButton.clicked.connect(self.on_load_as_default) 95 # creates a default config menu 96 start_menu = QtGui.QMenu(self) 97 self.loadDeafaultAtHostAct = QtGui.QAction("&Load default config on host", self, statusTip="Loads the default config at given host", triggered=self.on_load_as_default_at_host) 98 start_menu.addAction(self.loadDeafaultAtHostAct) 99 self.loadXmlAsDefaultButton.setMenu(start_menu) 100 #initialize the progress queue 101 self.progress_queue = ProgressQueue(self.progressFrame_cfg, self.progressBar_cfg, self.progressCancelButton_cfg)
102
103 - def stop(self):
104 ''' 105 Cancel the executing queued actions. This method must be 106 called at the exit! 107 ''' 108 self.progress_queue.stop()
109
110 - def on_launch_selection_activated(self, activated):
111 ''' 112 Tries to load the launch file, if one was activated. 113 ''' 114 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 115 for item in selected: 116 try: 117 file = self.launchlist_model.expandItem(item.name, item.path, item.id) 118 self.searchPackageLine.setText('') 119 if not file is None: 120 if item.isLaunchFile(): 121 self.launchlist_model.add2LoadHistory(item.path) 122 key_mod = QtGui.QApplication.keyboardModifiers() 123 if (key_mod & QtCore.Qt.ShiftModifier): 124 self.load_as_default_signal.emit(item.path, None) 125 else: 126 self.load_signal.emit(item.path) 127 elif item.isConfigFile(): 128 self.edit_signal.emit([file]) 129 except Exception as e: 130 rospy.logwarn("Error while load launch file %s: %s"%(item, e)) 131 WarningMessageBox(QtGui.QMessageBox.Warning, "Load error", 132 'Error while load launch file:\n%s'%item.name, 133 "%s"%e).exec_()
134 # self.launchlist_model.reloadCurrentPath() 135
136 - def on_xmlFileView_selection_changed(self, selected, deselected):
137 ''' 138 On selection of a launch file, the buttons are enabled otherwise disabled. 139 ''' 140 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 141 for item in selected: 142 islaunch = item.isLaunchFile() 143 isconfig = item.isConfigFile() 144 self.editXmlButton.setEnabled(islaunch or isconfig) 145 self.loadXmlButton.setEnabled(islaunch) 146 self.transferButton.setEnabled(islaunch or isconfig) 147 self.loadXmlAsDefaultButton.setEnabled(islaunch)
148
149 - def set_package_filter(self, text):
150 self.launchlist_proxyModel.setFilterRegExp(QtCore.QRegExp(text, 151 QtCore.Qt.CaseInsensitive, 152 QtCore.QRegExp.Wildcard))
153
154 - def on_refresh_xml_clicked(self):
155 ''' 156 Reload the current path. 157 ''' 158 self.launchlist_model.reloadCurrentPath() 159 self.launchlist_model.reloadPackages() 160 self.editXmlButton.setEnabled(False) 161 self.loadXmlButton.setEnabled(False) 162 self.transferButton.setEnabled(False) 163 self.loadXmlAsDefaultButton.setEnabled(False)
164
165 - def on_edit_xml_clicked(self):
166 ''' 167 Opens an XML editor to edit the launch file. 168 ''' 169 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 170 for item in selected: 171 path = self.launchlist_model.expandItem(item.name, item.path, item.id) 172 if not path is None: 173 self.edit_signal.emit([path])
174
175 - def on_new_xml_clicked(self):
176 ''' 177 Creates a new launch file. 178 ''' 179 # get new file from open dialog, use last path if one exists 180 open_path = self.__current_path 181 if not self.launchlist_model.currentPath is None: 182 open_path = self.launchlist_model.currentPath 183 (fileName, filter) = QtGui.QFileDialog.getSaveFileName(self, 184 "New launch file", 185 open_path, 186 "Config files (*.launch *.yaml);;All files (*)") 187 if fileName: 188 try: 189 (pkg, pkg_path) = package_name(os.path.dirname(fileName)) 190 if pkg is None: 191 WarningMessageBox(QtGui.QMessageBox.Warning, "New File Error", 192 'The new file is not in a ROS package').exec_() 193 return 194 with open(fileName, 'w+') as f: 195 f.write("<launch>\n" 196 " <arg name=\"robot_ns\" default=\"my_robot\"/>\n" 197 " <group ns=\"$(arg robot_ns)\">\n" 198 " <param name=\"tf_prefix\" value=\"$(arg robot_ns)\"/>\n" 199 "\n" 200 " <node pkg=\"my_pkg\" type=\"my_node\" name=\"my_name\" >\n" 201 " <param name=\"capability_group\" value=\"MY_GROUP\"/>\n" 202 " </node>\n" 203 " </group>\n" 204 "</launch>\n" 205 ) 206 self.__current_path = os.path.dirname(fileName) 207 self.launchlist_model.setPath(self.__current_path) 208 self.edit_signal.emit([fileName]) 209 except EnvironmentError as e: 210 WarningMessageBox(QtGui.QMessageBox.Warning, "New File Error", 211 'Error while create a new file', 212 '%s'%e).exec_()
213
214 - def on_open_xml_clicked(self):
215 (fileName, filter) = QtGui.QFileDialog.getOpenFileName(self, 216 "Load launch file", 217 self.__current_path, 218 "Config files (*.launch);;All files (*)") 219 if fileName: 220 self.__current_path = os.path.dirname(fileName) 221 self.launchlist_model.add2LoadHistory(fileName) 222 self.load_signal.emit(fileName)
223
224 - def on_transfer_file_clicked(self):
225 ''' 226 Emit the signal to copy the selected file to a remote host. 227 ''' 228 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 229 paths = list() 230 for item in selected: 231 path = self.launchlist_model.expandItem(item.name, item.path, item.id) 232 if not path is None: 233 paths.append(path) 234 if paths: 235 self.transfer_signal.emit(paths)
236
237 - def on_load_xml_clicked(self):
238 ''' 239 Tries to load the selected launch file. The button is only enabled and this 240 method is called, if the button was enabled by on_launch_selection_clicked() 241 ''' 242 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 243 for item in selected: 244 path = self.launchlist_model.expandItem(item.name, item.path, item.id) 245 if not path is None: 246 self.launchlist_model.add2LoadHistory(path) 247 self.load_signal.emit(path)
248
250 ''' 251 Tries to load the selected launch file as default configuration. The button 252 is only enabled and this method is called, if the button was enabled by 253 on_launch_selection_clicked() 254 ''' 255 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 256 for item in selected: 257 path = self.launchlist_model.expandItem(item.name, item.path, item.id) 258 if not path is None: 259 params = {'Host' : ('string', 'localhost') } 260 dia = ParameterDialog(params) 261 dia.setFilterVisible(False) 262 dia.setWindowTitle('Start node on...') 263 dia.resize(350,120) 264 dia.setFocusField('Host') 265 if dia.exec_(): 266 try: 267 params = dia.getKeywords() 268 host = params['Host'] 269 rospy.loginfo("LOAD the launch file on host %s as default: %s"%(host, path)) 270 self.launchlist_model.add2LoadHistory(path) 271 self.load_as_default_signal.emit(path, host) 272 except Exception, e: 273 WarningMessageBox(QtGui.QMessageBox.Warning, "Load default config error", 274 'Error while parse parameter', 275 '%s'%e).exec_()
276 277
278 - def on_load_as_default(self):
279 ''' 280 Tries to load the selected launch file as default configuration. The button 281 is only enabled and this method is called, if the button was enabled by 282 on_launch_selection_clicked() 283 ''' 284 key_mod = QtGui.QApplication.keyboardModifiers() 285 if (key_mod & QtCore.Qt.ShiftModifier): 286 self.loadXmlAsDefaultButton.showMenu() 287 else: 288 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 289 for item in selected: 290 path = self.launchlist_model.expandItem(item.name, item.path, item.id) 291 if not path is None: 292 rospy.loginfo("LOAD the launch file as default: %s", path) 293 self.launchlist_model.add2LoadHistory(path) 294 self.load_as_default_signal.emit(path, None)
295
296 - def _launchItemsFromIndexes(self, indexes, recursive=True):
297 result = [] 298 for index in indexes: 299 if index.column() == 0: 300 model_index = self.launchlist_proxyModel.mapToSource(index) 301 item = self.launchlist_model.itemFromIndex(model_index) 302 if not item is None and isinstance(item, LaunchItem): 303 result.append(item) 304 return result
305
306 - def keyReleaseEvent(self, event):
307 ''' 308 Defines some of shortcuts for navigation/management in launch 309 list view or topics view. 310 ''' 311 key_mod = QtGui.QApplication.keyboardModifiers() 312 if not self.xmlFileView.state() == QtGui.QAbstractItemView.EditingState: 313 # remove history file from list by pressing DEL 314 if event == QtGui.QKeySequence.Delete: 315 selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) 316 for item in selected: 317 if item.path in self.launchlist_model.load_history: 318 self.launchlist_model.removeFromLoadHistory(item.path) 319 self.launchlist_model.reloadCurrentPath() 320 elif not key_mod and event.key() == QtCore.Qt.Key_F4 and self.editXmlButton.isEnabled(): 321 # open selected launch file in xml editor by F4 322 self.on_edit_xml_clicked() 323 elif event == QtGui.QKeySequence.Find: 324 # set focus to filter box for packages 325 self.searchPackageLine.setFocus(QtCore.Qt.ActiveWindowFocusReason) 326 elif event == QtGui.QKeySequence.Paste: 327 # paste files from clipboard 328 self.launchlist_model.paste_from_clipboard() 329 elif event == QtGui.QKeySequence.Copy: 330 # copy the selected items as file paths into clipboard 331 selected = self.xmlFileView.selectionModel().selectedIndexes() 332 indexes = [] 333 for s in selected: 334 indexes.append(self.launchlist_proxyModel.mapToSource(s)) 335 self.launchlist_model.copy_to_clipboard(indexes) 336 if self.searchPackageLine.hasFocus() and event.key() == QtCore.Qt.Key_Escape: 337 # cancel package filtering on pressing ESC 338 self.launchlist_model.show_packages(False) 339 self.searchPackageLine.setText('') 340 self.xmlFileView.setFocus(QtCore.Qt.ActiveWindowFocusReason) 341 QtGui.QDockWidget.keyReleaseEvent(self, event)
342
343 - def _searchline_focusInEvent(self, event):
344 self.launchlist_model.show_packages(True) 345 QtGui.QLineEdit.focusInEvent(self.searchPackageLine, event)
346