editor.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import os
4 import sys
5 
6 import time
7 import threading
8 import yaml
9 
10 import rospy
11 import rosparam
12 import rospkg
13 
14 from frame_editor.objects import *
15 from frame_editor.commands import *
16 
19 
20 from python_qt_binding import QtCore
21 from python_qt_binding.QtWidgets import QUndoStack
22 
23 
24 from frame_editor.interface_interactive_marker import FrameEditor_InteractiveMarker
25 from frame_editor.interface_services import FrameEditor_Services
26 from frame_editor.interface_markers import FrameEditor_Markers
27 from frame_editor.interface_tf import FrameEditor_TF
28 
29 
30 class FrameEditor(QtCore.QObject):
31 
32  def __init__(self, context):
33  self.static = FrameEditor.parse_args_static(context.argv())
34  Frame.init_tf(self.static)
35  super(FrameEditor, self).__init__()
36 
37  self.frames = {}
38  self.active_frame = None
39 
40 
41  self.observers = []
42  self.undo_level = 0
43  self.undo_elements = []
44  self.undo_stack = QUndoStack()
45  self.undo_stack.indexChanged.connect(self.undo_stack_changed)
46  self.__command_lock = threading.Lock()
47 
48  self.namespace = "frame_editor"
49  self.full_file_path = None
50  self.hz = 200
51  self.filter_style = "hide"
52 
53 
54  def get_file_name(self):
55  if self.full_file_path is None:
56  return ""
57  else:
58  return os.path.basename(self.full_file_path)
59 
60  def get_full_file_path(self):
61  if self.full_file_path is None:
62  return ""
63  else:
64  return self.full_file_path
65 
66 
68  @QtCore.Slot(int)
69  def undo_stack_changed(self, idx):
70  '''Updates all observers, whenever a command has been undone/redone'''
71  self.update_obsevers(self.undo_level)
72 
73  def add_undo_level(self, level, elements=None):
74  '''Used by commands to add a level for updating'''
75  self.undo_level = self.undo_level | level
76  if elements:
77  self.undo_elements.extend(elements)
78 
79  def command(self, command):
80  '''Push a command to the stack (blocking)'''
81  with self.__command_lock:
82  self.undo_stack.push(command)
83 
84 
85  def update_obsevers(self, level):
86  '''Updates all registered observers and resets the undo_level'''
87  for observer in self.observers:
88  observer.update(self, level, self.undo_elements)
89  self.undo_level = 0
90  self.undo_elements = []
91 
92  def broadcast(self):
93  for observer in self.observers:
94  observer.broadcast(self)
95 
96  @staticmethod
97  def tf_dict():
98  y = Frame.tf_buffer.all_frames_as_yaml()
99  d = yaml.safe_load(y)
100  if isinstance(d, dict):
101  return d
102  else:
103  rospy.logwarn('Got invalid yaml from tf2: {}'.format(y))
104  return {}
105 
106  @staticmethod
107  def frame_is_temporary(frame_id):
108  return frame_id.startswith('_')
109 
110  @staticmethod
111  def all_frame_ids(include_temp=True):
112  return [f for f in FrameEditor.tf_dict() if
113  not FrameEditor.frame_is_temporary(f) or include_temp]
114 
115  def all_editor_frame_ids(self, include_temp=True):
116  return [f for f in self.frames.keys() if
117  not FrameEditor.frame_is_temporary(f) or include_temp]
118 
119  def iter_frames(self, include_temp=True):
120  for f in self.frames.values():
121  if not self.frame_is_temporary(f.name) or include_temp:
122  yield f
123 
124 
125 
127  def print_all(self):
128  rospy.loginfo("> Printing all frames")
129 
130  for frame in self.frames:
131  frame.print_all()
132 
133 
134 
136  def load_file(self, file_name):
137  if file_name:
138  rospy.loginfo("> Loading file")
139  data = rosparam.load_file(file_name, self.namespace)[0][0]
140  self.load_data(data)
141  else:
142 
143  self.command(Command_ClearAll(self))
144 
145  self.undo_stack.clear()
146 
147  self.full_file_path = file_name
148  return True
149 
150  def load_params(self, namespace):
151  if not rosparam.list_params(namespace):
152  rospy.logwarn("> No data to load")
153  else:
154  data = rosparam.get_param(namespace)
155  self.load_data(data)
156 
157  def load_data(self, data):
158 
159  self.undo_stack.beginMacro("Import file")
160 
161 
162  for name, frame in data["frames"].items():
163  t = frame["position"]
164  o = frame["orientation"]
165 
166  if "style" in frame:
167  style = frame["style"]
168  else:
169  style = "none"
170 
171  if "group" in frame:
172  group = frame["group"]
173  else:
174  group = ""
175 
176  if "data" in frame:
177  dat = frame["data"]
178  if "color" in dat:
179  color = dat["color"]
180  else:
181  color = (0.0, 0.5, 0.5, 0.75)
182  if "package" not in dat:
183  dat["package"] = ""
184 
185  position = (t["x"], t["y"], t["z"])
186  orientation = (o["x"], o["y"], o["z"], o["w"])
187 
188  if style == "plane":
189  f = Object_Plane(name, position, orientation, frame["parent"], dat["length"], dat["width"])
190  f.set_color(color)
191  elif style == "cube":
192  f = Object_Cube(name, position, orientation, frame["parent"], dat["length"], dat["width"], dat["height"])
193  f.set_color(color)
194  elif style == "sphere":
195  f = Object_Sphere(name, position, orientation, frame["parent"], dat["diameter"])
196  f.set_color(color)
197  elif style == "axis":
198  f = Object_Axis(name, position, orientation, frame["parent"], dat["length"], dat["width"])
199  f.set_color(color)
200  elif style == "mesh":
201  f = Object_Mesh(name, position, orientation, frame["parent"], dat["package"], dat["path"], dat["scale"])
202  f.set_color(color)
203  else:
204  f = Frame(name, position, orientation, frame["parent"], group=group)
205 
206  self.command(Command_AddElement(self, f))
207 
208  self.undo_stack.endMacro()
209 
210  rospy.loginfo("> Loading done")
211 
212  def save_file(self, filename):
213 
214 
215  data = {}
216  frames = {}
217 
218  for frame in self.iter_frames(include_temp=False):
219  t = {}
220  t["x"] = float(frame.position[0])
221  t["y"] = float(frame.position[1])
222  t["z"] = float(frame.position[2])
223 
224  o = {}
225  o["x"] = float(frame.orientation[0])
226  o["y"] = float(frame.orientation[1])
227  o["z"] = float(frame.orientation[2])
228  o["w"] = float(frame.orientation[3])
229 
230  f = {}
231  f["parent"] = frame.parent
232  f["position"] = t
233  f["orientation"] = o
234 
235  f["style"] = frame.style
236  f["group"] = frame.group
237 
238  if frame.style == "plane":
239  f["data"] = { "length": frame.length, "width":frame.width, "color": frame.color }
240 
241  elif frame.style == "cube":
242  f["data"] = { "length": frame.length, "width": frame.width, "height": frame.height , "color": frame.color}
243 
244  elif frame.style == "sphere":
245  f["data"] = { "diameter": frame.diameter, "color": frame.color }
246 
247  elif frame.style == "axis":
248  f["data"] = { "length": frame.length, "width": frame.width, "color": frame.color }
249 
250  elif frame.style == "mesh":
251  self.update_file_format(frame)
252  f["data"] = { "package" : frame.package, "path" : frame.path, "scale" : frame.scale, "color": frame.color }
253 
254  frames[frame.name] = f
255 
256  data["frames"] = frames
257 
258 
259  rospy.set_param(self.namespace, data)
260  rospy.loginfo(rospy.get_param(self.namespace))
261 
262 
263  if filename == '':
264  filename = self.full_file_path
265  rospy.loginfo("Saving to file {}".format(filename))
266  rosparam.dump_params(filename, self.namespace)
267  rospy.loginfo("Saving done")
268 
269  self.full_file_path = filename
270  return True
271 
272  def update_file_format(self, frame):
273  if frame.package == "" and frame.path != "":
274  try:
275  import rospkg
276  import os
277  from python_qt_binding import QtWidgets
278  rospackage = rospkg.get_package_name(frame.path)
279  if rospackage is not None:
280  rel_path = os.path.relpath(frame.path , rospkg.RosPack().get_path(rospackage))
281  reply = QtWidgets.QMessageBox.question(None, "Convert absolute path to rospack+relative path?",
282  "The absolute path to your selected mesh can be converted to rospack+relative path."+
283  "This gives you more reliabilaty to reuse your saved configuration"+
284  "if your meshes are stored in rospackages\n\n"+
285  "Do you want to convert your configuration?\n"+
286  "Convert:\n'{}'\nto:\n'{}' and\n '{}'\n".format(frame.path, rospackage, rel_path),
287  QtWidgets.QMessageBox.Yes |
288  QtWidgets.QMessageBox.No,
289  QtWidgets.QMessageBox.Yes)
290 
291  if reply == QtWidgets.QMessageBox.Yes:
292  rospy.loginfo("Saving: package: {} + relative path: {}".format(rospackage, rel_path))
293  frame.package = rospackage
294  frame.path = rel_path
295  return
296  except:
297  # Do nothing if conversion fails
298  pass
299  else:
300  # Do nothing if conversion not needed
301  pass
302 
303  def run(self):
304  rospy.loginfo("> Going for some spins")
305  rate = rospy.Rate(self.hz) # hz
306  while not rospy.is_shutdown():
307  self.broadcast()
308  rate.sleep()
309 
310  @staticmethod
311  def parse_args_static(argv):
312  from argparse import ArgumentParser
313  parser = ArgumentParser()
314  static = False
315  parser.add_argument("-s", "--static", action="store_true", help="Use static tf broadcaster")
316  args, unknowns = parser.parse_known_args(argv)
317  static = args.static
318  return static
319 
320  def parse_args(self, argv):
321 
324  from argparse import ArgumentParser
325  parser = ArgumentParser()
326  # Add argument(s) to the parser.
327  #parser.add_argument("-q", "--quiet", action="store_true",
328  # dest="quiet",
329  # help="Put plugin in silent mode")
330  parser.add_argument("-l", "--load", action="append",
331  dest="file",
332  help="Load a file at startup. [rospack filepath/file]")
333  parser.add_argument(
334  "--filter_style",
335  type=str,
336  choices=["grey", "hide"],
337  help="Choose the filter style: 'grey' or 'hide' (default: 'hide')",
338  default="hide",
339  )
340 
341  parser.add_argument("-r", "--rate", type=int, help="Rate for broadcasting. Does not involve tf frames. Only effective for non-static broadcaster.")
342  parser.add_argument("-s", "--static", action="store_true", help="Use static tf broadcaster")
343 
344  args, unknowns = parser.parse_known_args(argv)
345  rospy.loginfo('arguments: {}'.format(args))
346  if unknowns:
347  rospy.logwarn('unknown parameters found: {}'.format(unknowns))
348 
349  self.static = args.static
350 
351  if args.rate:
352  self.hz = args.rate
353  else:
354  self.hz = 100
355 
356  if args.filter_style:
357  self.filter_style = args.filter_style
358 
359 
360  if args.file:
361  arg_path = args.file[0].split()
362  if len(arg_path) == 1:
363  #load file
364  filename = arg_path[0]
365  rospy.loginfo("Loading {}".format(filename))
366  success = self.load_file(str(filename))
367  elif len(arg_path) == 2:
368  #load rospack
369  rospack = rospkg.RosPack()
370  filename = os.path.join(rospack.get_path(arg_path[0]), arg_path[1])
371  rospy.loginfo("Loading {}".format(filename))
372  success = self.load_file(str(filename))
373  else:
374  rospy.logwarn("Load argument not understood! --load {}".format(arg_path))
375  rospy.logwarn("Please use --load 'myRosPackage pathInMyPackage/myYaml.yaml'")
376  rospy.logwarn("or use --load 'fullPathToMyYaml.yaml'")
377  success = None
378 
379  if success:
380  return filename
381  elif success == False:
382  rospy.logerr("ERROR LOADING FILE")
383  return ''
384 
385  def init_views(self):
386 
391 
392 if __name__ == "__main__":
393 
394  rospy.init_node('frame_editor')
395  editor = FrameEditor(sys.argv[1:])
396 
397  editor.parse_args(sys.argv[1:])
398  editor.init_views()
399 
400  rospy.loginfo("Frame editor ready!")
401  editor.run()
402 
403 # eof
frame_editor.editor.FrameEditor.services
services
Definition: editor.py:389
frame_editor.objects.Object_Cube
Definition: objects.py:174
frame_editor.interface_tf
Definition: interface_tf.py:1
frame_editor.editor.FrameEditor.namespace
namespace
Definition: editor.py:48
frame_editor.editor.FrameEditor.update_file_format
def update_file_format(self, frame)
Definition: editor.py:272
frame_editor.objects.Object_Axis
Definition: objects.py:206
frame_editor.editor.FrameEditor.undo_stack
undo_stack
Definition: editor.py:44
frame_editor.interface_services
Definition: interface_services.py:1
frame_editor.interface_services.FrameEditor_Services
Definition: interface_services.py:19
frame_editor.editor.FrameEditor.active_frame
active_frame
Definition: editor.py:38
frame_editor.editor.FrameEditor.add_undo_level
def add_undo_level(self, level, elements=None)
Definition: editor.py:73
frame_editor.editor.FrameEditor.observers
observers
Undo/Redo.
Definition: editor.py:41
frame_editor.editor.FrameEditor.iter_frames
def iter_frames(self, include_temp=True)
Definition: editor.py:119
frame_editor.editor.FrameEditor.load_params
def load_params(self, namespace)
Definition: editor.py:150
frame_editor.editor.FrameEditor.frames
frames
Definition: editor.py:37
frame_editor.editor.FrameEditor.frame_is_temporary
def frame_is_temporary(frame_id)
Definition: editor.py:107
frame_editor.editor.FrameEditor.interface_markers
interface_markers
Definition: editor.py:390
frame_editor.constructors_std
Definition: constructors_std.py:1
frame_editor.editor.FrameEditor.__command_lock
__command_lock
Definition: editor.py:46
frame_editor.editor.FrameEditor.static
static
Args ##.
Definition: editor.py:33
frame_editor.interface_interactive_marker.FrameEditor_InteractiveMarker
Definition: interface_interactive_marker.py:18
frame_editor.interface_markers
Definition: interface_markers.py:1
frame_editor.editor.FrameEditor.load_file
def load_file(self, file_name)
FILE I/O ##.
Definition: editor.py:136
frame_editor.editor.FrameEditor.get_file_name
def get_file_name(self)
Definition: editor.py:54
frame_editor.editor.FrameEditor.tf_dict
def tf_dict()
Definition: editor.py:97
frame_editor.editor.FrameEditor.command
def command(self, command)
Definition: editor.py:79
frame_editor.objects
Definition: objects.py:1
frame_editor.objects.Object_Sphere
Definition: objects.py:191
frame_editor.commands
Definition: commands.py:1
frame_editor.editor.FrameEditor.all_editor_frame_ids
def all_editor_frame_ids(self, include_temp=True)
Definition: editor.py:115
frame_editor.editor.FrameEditor.get_full_file_path
def get_full_file_path(self)
Definition: editor.py:60
frame_editor.interface_tf.FrameEditor_TF
Definition: interface_tf.py:10
frame_editor.editor.FrameEditor.parse_args
def parse_args(self, argv)
Process standalone plugin command-line arguments.
Definition: editor.py:320
frame_editor.editor.FrameEditor.print_all
def print_all(self)
PRINT ##.
Definition: editor.py:127
frame_editor.editor.FrameEditor.parse_args_static
def parse_args_static(argv)
Definition: editor.py:311
frame_editor.editor.FrameEditor.undo_stack_changed
def undo_stack_changed(self, idx)
Undo/Redo ##.
Definition: editor.py:69
frame_editor.editor.FrameEditor.save_file
def save_file(self, filename)
Definition: editor.py:212
frame_editor.editor.FrameEditor.__init__
def __init__(self, context)
Definition: editor.py:32
frame_editor.editor.FrameEditor.interface_tf
interface_tf
Views.
Definition: editor.py:387
frame_editor.objects.Object_Plane
Definition: objects.py:152
frame_editor.editor.FrameEditor.full_file_path
full_file_path
Clear everything.
Definition: editor.py:49
frame_editor.objects.Frame
Definition: objects.py:24
frame_editor.commands.Command_AddElement
Definition: commands.py:33
frame_editor.editor.FrameEditor.filter_style
filter_style
Definition: editor.py:51
frame_editor.editor.FrameEditor.all_frame_ids
def all_frame_ids(include_temp=True)
Definition: editor.py:111
frame_editor.constructors_geometry
Definition: constructors_geometry.py:1
frame_editor.editor.FrameEditor.run
def run(self)
Definition: editor.py:303
frame_editor.interface_interactive_marker
Definition: interface_interactive_marker.py:1
frame_editor.editor.FrameEditor.load_data
def load_data(self, data)
Definition: editor.py:157
frame_editor.interface_markers.FrameEditor_Markers
Definition: interface_markers.py:11
frame_editor.editor.FrameEditor.init_views
def init_views(self)
Definition: editor.py:385
frame_editor.editor.FrameEditor.undo_elements
undo_elements
Definition: editor.py:43
frame_editor.editor.FrameEditor.undo_level
undo_level
Definition: editor.py:42
frame_editor.commands.Command_ClearAll
Definition: commands.py:84
frame_editor.objects.Object_Mesh
Definition: objects.py:222
frame_editor.editor.FrameEditor
Definition: editor.py:30
frame_editor.editor.FrameEditor.interactive
interactive
Definition: editor.py:388
frame_editor.editor.FrameEditor.broadcast
def broadcast(self)
Definition: editor.py:92
frame_editor.editor.FrameEditor.update_obsevers
def update_obsevers(self, level)
Definition: editor.py:85
frame_editor.editor.FrameEditor.hz
hz
Definition: editor.py:50


frame_editor
Author(s): ipa-lth , ipa-frn
autogenerated on Thu May 15 2025 02:17:25