Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search and Grouping Feature #39

Open
wants to merge 10 commits into
base: noetic-devel
Choose a base branch
from
13 changes: 13 additions & 0 deletions frame_editor/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
Changelog for package frame_editor
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Forthcoming
------------------
* Bug fix: Deleted Frames not beeing able to be reused
* Feature: Clear Frame Buffer and Added loading animation for tf frame list when pressing either refresh button or deleting frames
* Feature: Automatically Update TF Frame List when adding/duplicating frames (without clearing the frame buffer)
* Chore: Moved from prints to rospy logging
* Feature: Added search bar for both tf and frame list (style: `grey` out or `hide` frames with argument `--filter_style`)
* Feature: Add Grouping Option for frames by setting the `group` attribute in the yaml file
* Feature: TF Frame list is automatically grouped by self-defined (`Frames`) and external frames (`Other`)
* Feature: Autosave Functionality
* Performance: Use Static Transform Publisher instead of Dynamic.
* Contributors: Jan Krieglstein, Daniel Bargmann

1.1.1 (2022-05-19)
------------------
* Bug fix: Empty frame list on noetic now gets displayed
Expand Down
2 changes: 2 additions & 0 deletions frame_editor/etc/frames.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ frames:
parent: world
position: {x: 0.5, y: 0.5, z: 0.5}
style: none
group: frames_1
frame_axis:
orientation: {w: 0.0, x: 0.0, y: 0.0, z: 1.0}
parent: frame1
position: {x: -0.47346997261047363, y: 0.20000000298023224, z: 0.30000001192092896}
style: none
group: frames_1
frame_cube:
orientation: {w: 0.0, x: 0.0, y: 0.0, z: 1.0}
parent: frame1
Expand Down
2 changes: 1 addition & 1 deletion frame_editor/launch/frame_editor.launch
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<launch>
<node pkg="rqt_gui" type="rqt_gui" name="frame_editor"
args="--standalone frame_editor --args --load '$(find frame_editor)/etc/frames.yaml' --rate 200"
args="--standalone frame_editor --args --load '$(find frame_editor)/etc/frames.yaml' --rate 200 --filter_style grey"
output="screen"/>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find frame_editor)/etc/frame_editor.rviz"/>
Expand Down
37 changes: 35 additions & 2 deletions frame_editor/src/frame_editor/FrameEditorGUI.ui
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@
<string>Frames</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="searchLine">
<property name="placeholderText">
<string>Search ...</string>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
Expand All @@ -64,10 +71,21 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QListWidget" name="list_frames">
<widget class="QTreeWidget" name="list_frames">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
Expand Down Expand Up @@ -873,10 +891,25 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QListWidget" name="list_tf">
<widget class="QLineEdit" name="search_tf">
<property name="placeholderText">
<string>Search ...</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="list_tf">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
Expand Down
12 changes: 6 additions & 6 deletions frame_editor/src/frame_editor/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,17 +379,17 @@ def __init__(self, editor, element, style):
self.old_element = element

if style == "plane":
self.new_element = Object_Plane(element.name, element.position, element.orientation, element.parent)
self.new_element = Object_Plane(element.name, element.position, element.orientation, element.parent, group=element.group)
elif style == "cube":
self.new_element = Object_Cube(element.name, element.position, element.orientation, element.parent)
self.new_element = Object_Cube(element.name, element.position, element.orientation, element.parent, group=element.group)
elif style == "sphere":
self.new_element = Object_Sphere(element.name, element.position, element.orientation, element.parent)
self.new_element = Object_Sphere(element.name, element.position, element.orientation, element.parent, group=element.group)
elif style == "axis":
self.new_element = Object_Axis(element.name, element.position, element.orientation, element.parent)
self.new_element = Object_Axis(element.name, element.position, element.orientation, element.parent, group=element.group)
elif style == "mesh":
self.new_element = Object_Mesh(element.name, element.position, element.orientation, element.parent)
self.new_element = Object_Mesh(element.name, element.position, element.orientation, element.parent, group=element.group)
else:
self.new_element = Frame(element.name, element.position, element.orientation, element.parent)
self.new_element = Frame(element.name, element.position, element.orientation, element.parent, group=element.group)

if editor.active_frame is element:
self.was_active = True
Expand Down
61 changes: 40 additions & 21 deletions frame_editor/src/frame_editor/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(self):
self.namespace = "frame_editor"
self.full_file_path = None
self.hz = 200
self.filter_style = "hide"


def get_file_name(self):
Expand Down Expand Up @@ -98,7 +99,7 @@ def tf_dict():
if isinstance(d, dict):
return d
else:
rospy.logwarn('Got invalid yaml from tf2: '+y)
rospy.logwarn('Got invalid yaml from tf2: {}'.format(y))
return {}

@staticmethod
Expand All @@ -119,7 +120,7 @@ def iter_frames(self, include_temp=True):
## PRINT ##
##
def print_all(self):
print("> Printing all frames")
rospy.loginfo("> Printing all frames")

for frame in self.frames:
frame.print_all()
Expand All @@ -129,7 +130,7 @@ def print_all(self):
##
def load_file(self, file_name):
if file_name:
print("> Loading file")
rospy.loginfo("> Loading file")
data = rosparam.load_file(file_name, self.namespace)[0][0]
self.load_data(data)
else:
Expand All @@ -143,7 +144,7 @@ def load_file(self, file_name):

def load_params(self, namespace):
if not rosparam.list_params(namespace):
print("> No data to load")
rospy.logwarn("> No data to load")
else:
data = rosparam.get_param(namespace)
self.load_data(data)
Expand All @@ -161,6 +162,11 @@ def load_data(self, data):
style = frame["style"]
else:
style = "none"

if "group" in frame:
group = frame["group"]
else:
group = ""

if "data" in frame:
dat = frame["data"]
Expand Down Expand Up @@ -190,13 +196,13 @@ def load_data(self, data):
f = Object_Mesh(name, position, orientation, frame["parent"], dat["package"], dat["path"], dat["scale"])
f.set_color(color)
else:
f = Frame(name, position, orientation, frame["parent"])
f = Frame(name, position, orientation, frame["parent"], group=group)

self.command(Command_AddElement(self, f))

self.undo_stack.endMacro()

print("> Loading done")
rospy.loginfo("> Loading done")

def save_file(self, filename):

Expand All @@ -222,6 +228,7 @@ def save_file(self, filename):
f["orientation"] = o

f["style"] = frame.style
f["group"] = frame.group

if frame.style == "plane":
f["data"] = { "length": frame.length, "width":frame.width, "color": frame.color }
Expand All @@ -245,14 +252,14 @@ def save_file(self, filename):

## To parameter server
rospy.set_param(self.namespace, data)
print(rospy.get_param(self.namespace))
rospy.loginfo(rospy.get_param(self.namespace))

## Dump param to file
if filename == '':
filename = self.full_file_path
print("Saving to file {}".format(filename))
rospy.loginfo("Saving to file {}".format(filename))
rosparam.dump_params(filename, self.namespace)
print("Saving done")
rospy.loginfo("Saving done")

self.full_file_path = filename
return True
Expand All @@ -277,7 +284,7 @@ def update_file_format(self, frame):
QtWidgets.QMessageBox.Yes)

if reply == QtWidgets.QMessageBox.Yes:
print("Saving: package: {} + relative path: {}".format(rospackage, rel_path))
rospy.loginfo("Saving: package: {} + relative path: {}".format(rospackage, rel_path))
frame.package = rospackage
frame.path = rel_path
return
Expand All @@ -289,7 +296,7 @@ def update_file_format(self, frame):
pass

def run(self):
print("> Going for some spins")
rospy.loginfo("> Going for some spins")
rate = rospy.Rate(self.hz) # hz
while not rospy.is_shutdown():
self.broadcast()
Expand All @@ -309,39 +316,51 @@ def parse_args(self, argv):
dest="file",
help="Load a file at startup. [rospack filepath/file]")
parser.add_argument("-r", "--rate", type=int)

parser.add_argument(
"--filter_style",
type=str,
choices=["grey", "hide"],
help="Choose the filter style: 'grey' or 'hide' (default: 'hide')",
default="hide",
)

args, unknowns = parser.parse_known_args(argv)
print('arguments: {}'.format(args))
rospy.loginfo('arguments: {}'.format(args))
if unknowns:
print('unknown parameters found: {}'.format(unknowns))
rospy.logwarn('unknown parameters found: {}'.format(unknowns))

if args.rate:
self.hz = args.rate
else:
self.hz = 100

if args.filter_style:
self.filter_style = args.filter_style

## Load file ##
if args.file:
arg_path = args.file[0].split()
if len(arg_path) == 1:
#load file
filename = arg_path[0]
print("Loading {}".format(filename))
rospy.loginfo("Loading {}".format(filename))
success = self.load_file(str(filename))
elif len(arg_path) == 2:
#load rospack
rospack = rospkg.RosPack()
filename = os.path.join(rospack.get_path(arg_path[0]), arg_path[1])
print("Loading {}".format(filename))
rospy.loginfo("Loading {}".format(filename))
success = self.load_file(str(filename))
else:
print("Load argument not understood! --load {}".format(arg_path))
print("Please use --load 'myRosPackage pathInMyPackage/myYaml.yaml'")
print("or use --load 'fullPathToMyYaml.yaml'")
rospy.logwarn("Load argument not understood! --load {}".format(arg_path))
rospy.logwarn("Please use --load 'myRosPackage pathInMyPackage/myYaml.yaml'")
rospy.logwarn("or use --load 'fullPathToMyYaml.yaml'")
success = None

if success:
return filename
elif success == False:
print("ERROR LOADING FILE")
rospy.logerr("ERROR LOADING FILE")
return ''

def init_views(self):
Expand All @@ -361,7 +380,7 @@ def init_views(self):
editor.parse_args(sys.argv[1:])
editor.init_views()

print("Frame editor ready!")
rospy.loginfo("Frame editor ready!")
editor.run()

# eof
5 changes: 1 addition & 4 deletions frame_editor/src/frame_editor/interface_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ def __init__(self, frame_editor):
self.layout.addWidget(self.color_label, 5, 0)
self.layout.addWidget(self.color_button, 5, 1)

print("init")
self.update_widget(None)

def get_widget(self):
Expand Down Expand Up @@ -170,19 +169,17 @@ def btn_open_mesh_clicked(self):
if rospackage is None:
QtWidgets.QMessageBox.warning(self.widget, "Saving absolute path to mesh",
"Cannot find rospackage with selected mesh in it!\nSaving absolute path to mesh instead!")
#print("WARNING cannot find rospackage with mesh in it, saving absolute path")
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "package", ""))
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "path", path))
else:
rel_path = os.path.relpath(path , rospkg.RosPack().get_path(rospackage))
print("Saving: package: {} + relative path: {}".format(rospackage, rel_path))
rospy.loginfo("Saving: package: {} + relative path: {}".format(rospackage, rel_path))
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "package", rospackage))
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "path", rel_path))
except:
QtWidgets.QMessageBox.warning(self.widget, "Saving absolute path to mesh",
"The found rospackage with selected mesh in it is not sourced in your ROS workspace!\n"+
"Cannot resolve the packagepath\nSaving absolute path to mesh instead!")
#print("The package found is not sourced withing the current workspace, saving absolute path instead!")
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "package", ""))
self.editor.command(Command_SetGeometry(self.editor, self.editor.active_frame, "path", path))

Expand Down
Loading