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