12 from python_qt_binding
import loadUi, QtCore, QtWidgets
13 from python_qt_binding.QtWidgets
import QWidget, QTreeWidgetItem, QTreeWidget, QProgressBar
14 from python_qt_binding.QtCore
import Slot, Qt, QTimer
15 from python_qt_binding.QtGui
import QColor
49 """Update progress bar value to simulate loading."""
60 signal_update = QtCore.Signal(int)
61 signal_update_tf = QtCore.Signal(bool, bool)
62 signal_load_animation = QtCore.Signal()
65 super(FrameEditorGUI, self).
__init__(context)
67 self.setObjectName(
'FrameEditorGUI')
71 self.
editor.parse_args(context.argv())
86 editor.observers.append(self)
103 ui_file = os.path.join(rospkg.RosPack().get_path(
'frame_editor'),
'src/frame_editor',
'FrameEditorGUI.ui')
104 loadUi(ui_file, widget)
105 widget.setObjectName(
'FrameEditorGUIUi')
107 widget.setWindowTitle(
"frame editor")
113 widget.style_frame.layout().addWidget(self.
interface_style.get_widget())
123 widget.btn_refresh.clicked.connect(
lambda: self.
signal_update_tf.emit(
True,
True))
166 rospy.loginfo(
"> Shutting down")
169 def update(self, editor, level, elements):
193 Updates the tree with items that match the search query and groups them
194 under two collapsible categories.
197 self.
widget.list_tf.clear()
200 frame_group = QTreeWidgetItem(self.
widget.list_tf)
201 frame_group.setText(0,
"Frames")
202 frame_group.setExpanded(
True)
204 other_group = QTreeWidgetItem(self.
widget.list_tf)
205 other_group.setText(0,
"Others")
206 other_group.setExpanded(
True)
208 items = sorted(self.
editor.all_frame_ids(include_temp=
False))
211 tree_item = QTreeWidgetItem()
212 tree_item.setText(0, item)
215 if item
in self.
editor.frames.keys():
221 if search_query.lower()
in item.lower()
or self.
editor.filter_style ==
"grey":
222 if self.
editor.filter_style ==
"grey":
223 if search_query.lower()
in item.lower():
224 tree_item.setForeground(0, Qt.black)
226 tree_item.setForeground(0, QColor(169, 169, 169))
227 group.addChild(tree_item)
230 self.
widget.list_tf.sortItems(0, Qt.AscendingOrder)
234 self.
widget.list_tf.clear()
239 self.
widget.list_tf.expandAll()
244 Updates the displayed tree items based on the search query entered in searchLine.
246 search_query = self.
widget.search_tf.text()
255 Updates the tree with items that match the search query.
256 Group frames based on their `group` attribute, with collapsible groups.
257 Non-group frames are added without grouping or collapsibility.
258 If filter_style is 'hide', top-level items are hidden only if they do not match the search query.
261 self.
widget.list_frames.clear()
264 items = sorted(self.
editor.frames.keys())
270 for element
in items:
271 group = self.
editor.frames[element].group
273 if group
not in group_list:
274 frame_group = QTreeWidgetItem(self.
widget.list_frames)
275 frame_group.setText(0, group)
276 frame_group.setExpanded(
True)
277 frame_group.setFlags(frame_group.flags() & ~Qt.ItemIsSelectable)
278 group_list[group] = frame_group
282 tree_item = QTreeWidgetItem()
283 tree_item.setText(0, item)
284 tree_item.setFlags(tree_item.flags() | Qt.ItemIsSelectable)
287 group = self.
editor.frames[item].group
290 group_item = group_list[group]
293 group_item = self.
widget.list_frames
297 match_found = search_query.lower()
in item.lower()
300 if match_found
or self.
editor.filter_style ==
"grey":
301 if self.
editor.filter_style ==
"grey":
303 tree_item.setForeground(0, Qt.black)
305 tree_item.setForeground(0, QColor(169, 169, 169))
308 if self.
editor.filter_style ==
"hide" and group_item == self.
widget.list_frames
and not match_found:
312 if isinstance(group_item, QTreeWidgetItem):
313 group_item.addChild(tree_item)
315 self.
widget.list_frames.addTopLevelItem(tree_item)
318 self.
widget.list_frames.sortItems(0, Qt.AscendingOrder)
324 Updates the displayed tree items based on the search query entered in searchLine.
326 search_query = self.
widget.searchLine.text()
331 if not self.
editor.active_frame:
333 self.
widget.list_frames.setCurrentItem(
None)
334 self.
widget.box_edit.setEnabled(
False)
337 self.
widget.box_edit.setEnabled(
True)
339 name = self.
editor.active_frame.name
347 top_level_items = self.
widget.list_frames.findItems(name, Qt.MatchExactly)
350 if not top_level_items:
351 for i
in range(self.
widget.list_frames.topLevelItemCount()):
352 top_item = self.
widget.list_frames.topLevelItem(i)
357 found_item = top_level_items[0]
361 self.
widget.list_frames.setCurrentItem(found_item)
367 source = self.
widget.list_tf.currentItem()
370 source_name = source.text(0)
371 frame = self.
editor.active_frame
374 frame.tf_buffer.lookup_transform(source_name, frame.name, rospy.Time(0)))
376 rot = tf.transformations.euler_from_quaternion(orientation)
377 if self.
widget.btn_deg.isChecked():
378 rot = (180.0*rot[0]/math.pi, 180.0*rot[1]/math.pi, 180.0*rot[2]/math.pi)
380 pos_str = [f
"{p:.4f}" for p
in position]
381 rot_str = [f
"{r:.4f}" for r
in rot]
382 except (tf2_ros.ConnectivityException, tf2_ros.LookupException):
383 pos_str = [
'N/A',
'N/A',
'N/A']
386 self.
widget.valx.setText(pos_str[0])
387 self.
widget.valy.setText(pos_str[1])
388 self.
widget.valz.setText(pos_str[2])
390 self.
widget.vala.setText(rot_str[0])
391 self.
widget.valb.setText(rot_str[1])
392 self.
widget.valc.setText(rot_str[2])
397 Recursively search for the item in the children of a given parent item.
400 for i
in range(parent_item.childCount()):
401 child_item = parent_item.child(i)
402 if child_item.text(0) == name:
410 f = self.
editor.active_frame
416 w.txt_name.setText(f.name)
417 w.txt_parent.setText(f.parent)
418 w.txt_group.setText(f.group)
422 w.txt_x.setValue(f.position[0])
423 w.txt_y.setValue(f.position[1])
424 w.txt_z.setValue(f.position[2])
426 rot = tf.transformations.euler_from_quaternion(f.orientation)
427 if self.
widget.btn_deg.isChecked():
428 rot = (180.0*rot[0]/math.pi, 180.0*rot[1]/math.pi, 180.0*rot[2]/math.pi)
430 w.txt_a.setValue(rot[0])
431 w.txt_b.setValue(rot[1])
432 w.txt_c.setValue(rot[2])
434 txt_abs_pos = (w.txt_abs_x, w.txt_abs_y, w.txt_abs_z)
435 txt_abs_rot = (w.txt_abs_a, w.txt_abs_b, w.txt_abs_c)
440 f.tf_buffer.lookup_transform(
'world', f.name, rospy.Time(0)))
441 for txt, p
in zip(txt_abs_pos, position):
444 rot = tf.transformations.euler_from_quaternion(orientation)
445 if self.
widget.btn_deg.isChecked():
446 rot = map(math.degrees, rot)
447 for txt, r
in zip(txt_abs_rot, rot):
451 for txt
in txt_abs_rot + txt_abs_pos:
452 txt.setEnabled(
False)
455 self.
widget.combo_style.setCurrentIndex(self.
widget.combo_style.findText(f.style))
458 @Slot(QTreeWidgetItem, QTreeWidgetItem)
466 if name
not in self.
editor.frames:
473 if not self.
editor.active_frame
or (self.
editor.active_frame.name != name):
489 existing_tf_frames = set(self.
editor.all_frame_ids())
490 existing_editor_frames = set(self.
editor.all_editor_frame_ids())
492 name, ok = QtWidgets.QInputDialog.getText(self.
widget, window_title,
"Name:", QtWidgets.QLineEdit.Normal, default_name);
495 while ok
and name
in existing_editor_frames
or (name
in existing_tf_frames
and not Frame.was_published_by_frameeditor(name)):
496 name, ok = QtWidgets.QInputDialog.getText(self.
widget, window_title,
"Name (must be unique):", QtWidgets.QLineEdit.Normal, default_name)
509 available_parents = self.
editor.all_frame_ids(include_temp=
False)
510 if not available_parents:
511 available_parents = [
"world"]
513 parent, ok = QtWidgets.QInputDialog.getItem(self.
widget,
"Add New Frame",
"Parent Name:", sorted(available_parents))
515 if not ok
or parent ==
"":
523 item = self.
widget.list_frames.currentItem()
526 source_name = item.text(0)
527 parent_name = self.
editor.frames[source_name].parent
537 return max(5.0 / self.
editor.hz, 0.1)
558 item = self.
widget.list_frames.currentItem()
575 parent = self.
widget.list_tf.currentItem()
579 if parent.text(0) == self.
editor.active_frame.name:
589 self.
set_pose([
"x",
"y",
"z",
"a",
"b",
"c"])
619 source = self.
widget.list_tf.currentItem()
623 frame = self.
editor.active_frame
636 self.
editor.active_frame.tf_buffer.lookup_transform(
637 self.
editor.active_frame.parent,
"world", rospy.Time(0)))
647 self.
editor.active_frame.listener.lookupTransform(
648 self.
editor.active_frame.parent,
"world", rospy.Time(0)))
655 frame = self.
editor.active_frame
656 value = widget.value()
659 if self.
widget.btn_deg.isChecked()
and symbol
in [
'a',
'b',
'c']:
660 value = value * math.pi / 180.0
662 if frame.value(symbol) != value:
686 value = self.
widget.txt_group.text()
687 if self.
editor.active_frame.group != value:
696 style = self.
widget.combo_style.currentText().lower()
697 if self.
editor.active_frame.style != style: