1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 from python_qt_binding import QtGui
34 from python_qt_binding import QtCore
35
36 import os
37 import threading
38
39 import node_manager_fkie as nm
40 from common import is_package
41 from detailed_msg_box import WarningMessageBox
42 from xml_editor import Editor
45 '''
46 Enabled the syntax highlightning for the sync interface.
47 '''
48
50 QtGui.QSyntaxHighlighter.__init__(self, parent)
51 self.rules = []
52 self.commentStart = QtCore.QRegExp("#")
53 self.commentEnd = QtCore.QRegExp("\n")
54 self.commentFormat = QtGui.QTextCharFormat()
55 self.commentFormat.setFontItalic(True)
56 self.commentFormat.setForeground(QtCore.Qt.darkGray)
57 f = QtGui.QTextCharFormat()
58 r = QtCore.QRegExp()
59 r.setMinimal(True)
60 f.setFontWeight(QtGui.QFont.Normal)
61 f.setForeground (QtCore.Qt.darkBlue)
62 tagList = ["\\bignore_hosts\\b", "\\bsync_hosts\\b",
63 "\\bignore_nodes\\b", "\\bsync_nodes\\b",
64 "\\bignore_topics\\b", "\\bsync_topics\\b",
65 "\\bignore_services\\b", "\\bsync_services\\b",
66 "\\bsync_topics_on_demand\\b", "\\bsync_remote_nodes\\b"]
67 for tag in tagList:
68 r.setPattern(tag)
69 self.rules.append((QtCore.QRegExp(r), QtGui.QTextCharFormat(f)))
70
71 f.setForeground(QtCore.Qt.darkGreen)
72 f.setFontWeight(QtGui.QFont.Bold)
73 attrList = ["\\b\\*|\\*\\B|\\/\\*"]
74 for attr in attrList:
75 r.setPattern(attr)
76 self.rules.append((QtCore.QRegExp(r), QtGui.QTextCharFormat(f)))
77
78
79
80
81
82
83
84
85
87 for pattern, format in self.rules:
88 index = pattern.indexIn(text)
89 while index >= 0:
90 length = pattern.matchedLength()
91 self.setFormat(index, length, format)
92 index = pattern.indexIn(text, index + length)
93
94 self.setCurrentBlockState(0)
95 startIndex = 0
96 if self.previousBlockState() != 1:
97 startIndex = self.commentStart.indexIn(text)
98 if startIndex >= 0:
99 commentLength = len(text) - startIndex
100 self.setFormat(startIndex, commentLength, self.commentFormat)
101
104 '''
105 A dialog to set the sync options.
106 '''
107
109 QtGui.QDialog.__init__(self, parent)
110
111 self.setWindowTitle('Sync')
112 self.verticalLayout = QtGui.QVBoxLayout(self)
113 self.verticalLayout.setObjectName("verticalLayout")
114 self.resize(350,190)
115
116 self.toolButton_SyncAll = QtGui.QToolButton(self)
117 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
118 sizePolicy.setHorizontalStretch(0)
119 sizePolicy.setVerticalStretch(2)
120 sizePolicy.setHeightForWidth(self.toolButton_SyncAll.sizePolicy().hasHeightForWidth())
121 self.toolButton_SyncAll.setSizePolicy(sizePolicy)
122 self.toolButton_SyncAll.setObjectName("toolButton_SyncAll")
123 self.verticalLayout.addWidget(self.toolButton_SyncAll)
124 self.toolButton_SyncAll.setText(QtGui.QApplication.translate("Form", "Sync All", None, QtGui.QApplication.UnicodeUTF8))
125 self.toolButton_SyncAll.clicked.connect(self._on_sync_all_clicked)
126
127 self.toolButton_SyncAllAnyMsg = QtGui.QToolButton(self)
128 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
129 sizePolicy.setHorizontalStretch(0)
130 sizePolicy.setVerticalStretch(1)
131 sizePolicy.setHeightForWidth(self.toolButton_SyncAllAnyMsg.sizePolicy().hasHeightForWidth())
132 self.toolButton_SyncAllAnyMsg.setSizePolicy(sizePolicy)
133 self.toolButton_SyncAllAnyMsg.setObjectName("toolButton_SyncAllAnyMsg")
134 self.verticalLayout.addWidget(self.toolButton_SyncAllAnyMsg)
135 self.toolButton_SyncAllAnyMsg.setText(QtGui.QApplication.translate("Form", "Sync all (+AnyMsg)", None, QtGui.QApplication.UnicodeUTF8))
136 self.toolButton_SyncAllAnyMsg.clicked.connect(self._on_sync_all_anymsg_clicked)
137
138 self.toolButton_SyncTopicOnDemand = QtGui.QToolButton(self)
139 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
140 sizePolicy.setHorizontalStretch(0)
141 sizePolicy.setVerticalStretch(1)
142 sizePolicy.setHeightForWidth(self.toolButton_SyncTopicOnDemand.sizePolicy().hasHeightForWidth())
143 self.toolButton_SyncTopicOnDemand.setSizePolicy(sizePolicy)
144 self.toolButton_SyncTopicOnDemand.setObjectName("toolButton_SyncTopicOnDemand")
145 self.verticalLayout.addWidget(self.toolButton_SyncTopicOnDemand)
146 self.toolButton_SyncTopicOnDemand.setText(QtGui.QApplication.translate("Form", "Sync only topics on demand", None, QtGui.QApplication.UnicodeUTF8))
147 self.toolButton_SyncTopicOnDemand.clicked.connect(self._on_sync_topics_on_demand_clicked)
148
149 self.toolButton_SelectInterface = QtGui.QToolButton(self)
150 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
151 sizePolicy.setHorizontalStretch(0)
152 sizePolicy.setVerticalStretch(1)
153 sizePolicy.setHeightForWidth(self.toolButton_SelectInterface.sizePolicy().hasHeightForWidth())
154 self.toolButton_SelectInterface.setSizePolicy(sizePolicy)
155 self.toolButton_SelectInterface.setObjectName("toolButton_SelectInterface")
156 self.verticalLayout.addWidget(self.toolButton_SelectInterface)
157 self.toolButton_SelectInterface.setText(QtGui.QApplication.translate("Form", "Select an interface", None, QtGui.QApplication.UnicodeUTF8))
158 self.toolButton_SelectInterface.clicked.connect(self._on_select_interface_clicked)
159
160 self.interface_field = QtGui.QComboBox(self)
161 self.interface_field.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
162 self.interface_field.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed))
163 self.interface_field.setEditable(True)
164 self.interface_field.setVisible(False)
165 self.interface_field.setObjectName("interface_field")
166 self.verticalLayout.addWidget(self.interface_field)
167 self.interface_field.currentIndexChanged[str].connect(self._on_interface_selected)
168
169 self.toolButton_EditInterface = QtGui.QToolButton(self)
170 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
171 sizePolicy.setHorizontalStretch(0)
172 sizePolicy.setVerticalStretch(1)
173 sizePolicy.setHeightForWidth(self.toolButton_EditInterface.sizePolicy().hasHeightForWidth())
174 self.toolButton_EditInterface.setSizePolicy(sizePolicy)
175 self.toolButton_EditInterface.setObjectName("toolButton_EditInterface")
176 self.verticalLayout.addWidget(self.toolButton_EditInterface)
177 self.toolButton_EditInterface.setText(QtGui.QApplication.translate("Form", "Edit selected interface", None, QtGui.QApplication.UnicodeUTF8))
178 self.toolButton_EditInterface.clicked.connect(self._on_edit_interface_clicked)
179 self.toolButton_EditInterface.setVisible(False)
180
181 self.toolButton_CreateInterface = QtGui.QToolButton(self)
182 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
183 sizePolicy.setHorizontalStretch(0)
184 sizePolicy.setVerticalStretch(1)
185 sizePolicy.setHeightForWidth(self.toolButton_CreateInterface.sizePolicy().hasHeightForWidth())
186 self.toolButton_CreateInterface.setSizePolicy(sizePolicy)
187 self.toolButton_CreateInterface.setObjectName("toolButton_CreateInterface")
188 self.verticalLayout.addWidget(self.toolButton_CreateInterface)
189 self.toolButton_CreateInterface.setText(QtGui.QApplication.translate("Form", "Create an interface", None, QtGui.QApplication.UnicodeUTF8))
190 self.toolButton_CreateInterface.clicked.connect(self._on_create_interface_clicked)
191 self.toolButton_CreateInterface.setVisible(False)
192
193 self.textedit = Editor('', self)
194 hl = SyncHighlighter(self.textedit.document())
195 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
196 self.textedit.setSizePolicy(sizePolicy)
197 self.textedit.setObjectName("syncedit")
198 self.verticalLayout.addWidget(self.textedit)
199 self.textedit.setVisible(False)
200
201
202 self._fill_interface_thread = None
203 self._interfaces_files = None
204 self._sync_args = []
205 self._interface_filename = None
206
207 self.buttonBox = QtGui.QDialogButtonBox(self)
208 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel)
209 self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
210 self.buttonBox.setObjectName("buttonBox")
211 self.verticalLayout.addWidget(self.buttonBox)
212 QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept)
213 QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
214
215 self._new_iface = True
216
217 @property
219 return self._sync_args
220
221 @property
223 return self._interface_filename
224
225
227 self.setResult(QtGui.QDialog.Accepted)
228 self._sync_args = []
229 self._sync_args.append(''.join(['_interface_url:=', "'.'"]))
230 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'False']))
231 self._sync_args.append(''.join(['_ignore_hosts:=', '[]']))
232 self._sync_args.append(''.join(['_sync_hosts:=', '[]']))
233 self._sync_args.append(''.join(['_ignore_nodes:=', '[]']))
234 self._sync_args.append(''.join(['_sync_nodes:=', '[]']))
235 self._sync_args.append(''.join(['_ignore_topics:=', '[]']))
236 self._sync_args.append(''.join(['_sync_topics:=', '[]']))
237 self._sync_args.append(''.join(['_ignore_services:=', '[]']))
238 self._sync_args.append(''.join(['_sync_services:=', '[]']))
239 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False']))
240 self._interface_filename = None
241 self.accept()
242
244 self._sync_args = []
245 self._sync_args.append(''.join(['_interface_url:=', "'.'"]))
246 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True']))
247 self._sync_args.append(''.join(['_ignore_hosts:=', '[]']))
248 self._sync_args.append(''.join(['_sync_hosts:=', '[]']))
249 self._sync_args.append(''.join(['_ignore_nodes:=', '[]']))
250 self._sync_args.append(''.join(['_sync_nodes:=', '[]']))
251 self._sync_args.append(''.join(['_ignore_topics:=', '[]']))
252 self._sync_args.append(''.join(['_sync_topics:=', '[/*]']))
253 self._sync_args.append(''.join(['_ignore_services:=', '[]']))
254 self._sync_args.append(''.join(['_sync_services:=', '[]']))
255 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False']))
256 self._interface_filename = None
257 self.accept()
258
260 self._sync_args = []
261 self._sync_args.append(''.join(['_interface_url:=', "'.'"]))
262 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True']))
263 self._sync_args.append(''.join(['_ignore_hosts:=', '[]']))
264 self._sync_args.append(''.join(['_sync_hosts:=', '[]']))
265 self._sync_args.append(''.join(['_ignore_nodes:=', '[]']))
266 self._sync_args.append(''.join(['_sync_nodes:=', '[]']))
267 self._sync_args.append(''.join(['_ignore_topics:=', '[]']))
268 self._sync_args.append(''.join(['_sync_topics:=', '[/only_on_demand]']))
269 self._sync_args.append(''.join(['_ignore_services:=', '[/*]']))
270 self._sync_args.append(''.join(['_sync_services:=', '[]']))
271 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False']))
272 self._interface_filename = None
273 self.accept()
274
276 self.toolButton_SyncAll.setVisible(False)
277 self.toolButton_SyncAllAnyMsg.setVisible(False)
278 self.toolButton_SyncTopicOnDemand.setVisible(False)
279 self.toolButton_SelectInterface.setVisible(False)
280 self.interface_field.setVisible(True)
281 self.toolButton_CreateInterface.setVisible(True)
282 self.toolButton_EditInterface.setVisible(True)
283 self.toolButton_EditInterface.setEnabled(False)
284 self.textedit.setVisible(False)
285
286 if self._interfaces_files is None:
287 self.interface_field.addItems(['interface searching...'])
288 self.interface_field.setCurrentIndex(0)
289 self._fill_interface_thread = threading.Thread(target=self._fill_interfaces)
290 self._fill_interface_thread.start()
291 else:
292 self.toolButton_EditInterface.setEnabled(self._interfaces_files.has_key(self.interface_field.currentText()))
293 self.buttonBox.clear()
294 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
295 self.interface_field.setFocus(QtCore.Qt.TabFocusReason)
296 self.resize(350,80)
297
299 if self._interfaces_files is None:
300 self.root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')]
301 self._interfaces_files = {}
302 for p in self.root_paths:
303 ret = self._getInterfaces(p)
304 self._interfaces_files = dict(ret.items() + self._interfaces_files.items())
305 self.interface_field.clear()
306 self.interface_field.clearEditText()
307 self.interface_field.addItems(self._interfaces_files.keys())
308
310 if self._interfaces_files and self._interfaces_files.has_key(interface):
311 self._sync_args = []
312 self._sync_args.append(''.join(['_interface_url:=', interface]))
313 self.toolButton_EditInterface.setEnabled(True)
314 else:
315 self.toolButton_EditInterface.setEnabled(False)
316
318 if self.textedit.isVisible():
319 try:
320 tmp_file = os.path.join(nm.screen().LOG_PATH, 'tmp_sync_interface.sync')
321 with open(tmp_file, 'w+') as f:
322 iface = f.write(self.textedit.toPlainText())
323 from master_discovery_fkie.common import read_interface
324 read_interface(tmp_file)
325 if not self._new_iface and self._interfaces_files.has_key(self.interface_field.currentText()):
326 fileName = self._interfaces_files[self.interface_field.currentText()]
327 else:
328 fileName, selectedFilter = QtGui.QFileDialog.getSaveFileName(self, 'Save sync interface', '/home', "Sync Files (*.sync)")
329 if fileName:
330 with open(fileName, 'w+') as f:
331 self._interface_filename = fileName
332 iface = f.write(self.textedit.toPlainText())
333 if self._new_iface:
334 self.interface_field.clear()
335 self._interfaces_files = None
336 self._on_select_interface_clicked()
337
338
339 except Exception as e:
340 WarningMessageBox(QtGui.QMessageBox.Warning, "Create sync interface",
341 "Error while create interface",
342 str(e)).exec_()
343 elif self.interface_field.isVisible():
344 interface = self.interface_field.currentText()
345 if self._interfaces_files and self._interfaces_files.has_key(interface):
346 self._interface_filename = self._interfaces_files[interface]
347 self._sync_args = []
348 self._sync_args.append(''.join(['_interface_url:=', interface]))
349 QtGui.QDialog.accept(self)
350 self.resetView()
351 else:
352 QtGui.QDialog.accept(self)
353 self.resetView()
354
355
357 if self.textedit.isVisible():
358 self._on_select_interface_clicked()
359 else:
360 QtGui.QDialog.reject(self)
361 self.resetView()
362
364 self._new_iface = True
365 self.interface_field.setVisible(False)
366 self.toolButton_CreateInterface.setVisible(False)
367 self.toolButton_EditInterface.setVisible(False)
368 self.textedit.setVisible(True)
369 self.textedit.setText("# The ignore_* lists will be processed first.\n"
370 "# For ignore/sync nodes, topics or services\n"
371 "# use follow declaration:\n"
372 "#{param name}: \n"
373 "# - {ros name}\n"
374 "# or for selected hosts:\n"
375 "# - {host name}:\n"
376 "# - {ros name}\n\n"
377 "# you can use follow wildcard: '*', but not as a first character\n"
378 "ignore_hosts:\n"
379 "sync_hosts:\n\n"
380 "ignore_nodes:\n"
381 "sync_nodes:\n\n"
382 "ignore_topics:\n"
383 "sync_topics:\n\n"
384 "ignore_services:\n"
385 " - /*get_loggers\n"
386 " - /*set_logger_level\n"
387 "sync_services:\n\n"
388 "# If sync_topics_on_demand is True the local subscribed and published topics\n"
389 "# are synchronized with remote even if they are not in the sync_* list.\n"
390 "sync_topics_on_demand: False\n\n"
391 "# The nodes which are running not at the same host as the ROS master are not\n"
392 "# synchronized by default. Use sync_remote_nodes to sync these nodes also.\n"
393 "sync_remote_nodes: False\n\n"
394 )
395 self.resize(350,300)
396
398 self._new_iface = False
399 self.interface_field.setVisible(False)
400 self.toolButton_CreateInterface.setVisible(False)
401 self.toolButton_EditInterface.setVisible(False)
402 self.textedit.setVisible(True)
403 if self._interfaces_files.has_key(self.interface_field.currentText()):
404 try:
405 with open(self._interfaces_files[self.interface_field.currentText()], 'rw') as f:
406 iface = f.read()
407 self.textedit.setText(iface)
408 except Exception as e:
409 WarningMessageBox(QtGui.QMessageBox.Warning, "Edit sync interface",
410 "Error while open interface",
411 str(e)).exec_()
412 self.resize(350,300)
413
415 self.toolButton_SyncAll.setVisible(True)
416 self.toolButton_SyncAllAnyMsg.setVisible(True)
417 self.toolButton_SyncTopicOnDemand.setVisible(True)
418 self.toolButton_SelectInterface.setVisible(True)
419 self.interface_field.setVisible(False)
420 self.toolButton_CreateInterface.setVisible(False)
421 self.toolButton_EditInterface.setVisible(False)
422 self.textedit.setVisible(False)
423 self.buttonBox.clear()
424 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel)
425 self.resize(350,160)
426
428 result = {}
429 if os.path.isdir(path):
430 fileList = os.listdir(path)
431
432 if not package:
433
434 if is_package(fileList):
435 package = os.path.basename(path)
436 for f in fileList:
437 ret = self._getInterfaces(os.path.join(path, f), package)
438 result = dict(ret.items() + result.items())
439 elif package and os.path.isfile(path) and path.endswith('.sync'):
440
441 return {''.join(['pkg://', package, '///', os.path.basename(path)]) : path}
442 return result
443