editor.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 import os
00004 import sys
00005 
00006 import time
00007 import threading
00008 import yaml
00009 
00010 import rospy
00011 import rosparam
00012 import rospkg
00013 
00014 import tf
00015 
00016 from frame_editor.objects import *
00017 from frame_editor.commands import *
00018 
00019 from frame_editor.constructors_geometry import *
00020 from frame_editor.constructors_std import *
00021 
00022 from python_qt_binding import QtCore
00023 from python_qt_binding.QtGui import QUndoStack
00024 
00025 ## Views
00026 from frame_editor.interface_interactive_marker import FrameEditor_InteractiveMarker
00027 from frame_editor.interface_services import FrameEditor_Services
00028 from frame_editor.interface_markers import FrameEditor_Markers
00029 from frame_editor.interface_tf import FrameEditor_TF
00030 
00031 
00032 class FrameEditor(QtCore.QObject):
00033 
00034     def __init__(self):
00035         super(FrameEditor, self).__init__()
00036 
00037         self.frames = {}
00038         self.active_frame = None
00039 
00040         ## Undo/Redo
00041         self.observers = []
00042         self.undo_level = 0
00043         self.undo_elements = []
00044         self.undo_stack = QUndoStack()
00045         self.undo_stack.indexChanged.connect(self.undo_stack_changed)
00046         self.__command_lock = threading.Lock()
00047 
00048         self.namespace = "frame_editor"
00049 
00050     ## Undo/Redo ##
00051     ##
00052     @QtCore.Slot(int)
00053     def undo_stack_changed(self, idx):
00054         '''Updates all observers, whenever a command has been undone/redone'''
00055         self.update_obsevers(self.undo_level)
00056 
00057     def add_undo_level(self, level, elements=None):
00058         '''Used by commands to add a level for updating'''
00059         self.undo_level = self.undo_level | level
00060         if elements:
00061             self.undo_elements.extend(elements)
00062 
00063     def command(self, command):
00064         '''Push a command to the stack (blocking)'''
00065         with self.__command_lock:
00066             self.undo_stack.push(command)
00067 
00068 
00069     def update_obsevers(self, level):
00070         '''Updates all registered observers and resets the undo_level'''
00071         for observer in self.observers:
00072             observer.update(self, level, self.undo_elements)
00073         self.undo_level = 0
00074         self.undo_elements = []
00075 
00076         ## Just in case: additional broadcast
00077         self.broadcast()
00078 
00079     def broadcast(self):
00080         for observer in self.observers:
00081             observer.broadcast(self)
00082 
00083     @staticmethod
00084     def tf_dict():
00085         y = Frame.tf_buffer.all_frames_as_yaml()
00086         d = yaml.load(y)
00087         if isinstance(d, dict):
00088             return d
00089         else:
00090             rospy.logwarn('Got invalid yaml from tf2: '+y)
00091             return {}
00092 
00093     @staticmethod
00094     def frame_is_temporary(frame_id):
00095         return frame_id.startswith('_')
00096 
00097     @staticmethod
00098     def all_frame_ids(include_temp=True):
00099         return [f for f in FrameEditor.tf_dict() if
00100                 not FrameEditor.frame_is_temporary(f) or include_temp]
00101 
00102     def iter_frames(self, include_temp=True):
00103         for f in self.frames.itervalues():
00104             if not self.frame_is_temporary(f.name) or include_temp:
00105                 yield f
00106 
00107 
00108     ## PRINT ##
00109     ##
00110     def print_all(self):
00111         print "> Printing all frames"
00112 
00113         for frame in self.frames:
00114             frame.print_all()
00115 
00116 
00117     ## FILE I/O ##
00118     ##
00119     def load_file(self, file_name):
00120         if file_name:
00121             print "> Loading file"
00122             data = rosparam.load_file(file_name, self.namespace)[0][0]
00123             self.load_data(data)
00124         else:
00125             ## Clear everything
00126             self.command(Command_ClearAll(self))
00127 
00128         self.undo_stack.clear()
00129 
00130         return True
00131 
00132     def load_params(self, namespace):
00133         if not rosparam.list_params(namespace):
00134             print "> No data to load"
00135         else:
00136             data = rosparam.get_param(namespace)
00137             self.load_data(data)
00138 
00139     def load_data(self, data):
00140 
00141         self.undo_stack.beginMacro("Import file")
00142 
00143         ## Import data
00144         for name, frame in data["frames"].items():
00145             t = frame["position"]
00146             o = frame["orientation"]
00147 
00148             if "style" in frame:
00149                 style = frame["style"]
00150             else:
00151                 style = "none"
00152 
00153             if "data" in frame:
00154                 dat = frame["data"]
00155                 if "color" in dat:
00156                     color = dat["color"]
00157                 else:
00158                     color = (0.0, 0.5, 0.5, 0.75)
00159 
00160             position = (t["x"], t["y"], t["z"])
00161             orientation = (o["x"], o["y"], o["z"], o["w"])
00162 
00163             if style == "plane":
00164                 f = Object_Plane(name, position, orientation, frame["parent"], dat["length"], dat["width"])
00165                 f.set_color(color)
00166             elif style == "cube":
00167                 f = Object_Cube(name, position, orientation, frame["parent"], dat["length"], dat["width"], dat["height"])
00168                 f.set_color(color)
00169             elif style == "sphere":
00170                 f = Object_Sphere(name, position, orientation, frame["parent"], dat["diameter"])
00171                 f.set_color(color)
00172             elif style == "axis":
00173                 f = Object_Axis(name, position, orientation, frame["parent"], dat["length"], dat["width"])
00174                 f.set_color(color)
00175             elif style == "mesh":
00176                 f = Object_Mesh(name, position, orientation, frame["parent"], dat["path"], dat["scale"])
00177                 f.set_color(color)
00178             else:
00179                 f = Frame(name, position, orientation, frame["parent"])
00180 
00181             self.command(Command_AddElement(self, f))
00182 
00183         self.undo_stack.endMacro()
00184 
00185         print "> Loading done"
00186 
00187     def save_file(self, filename):
00188 
00189         ## Data
00190         data = {}
00191         frames = {}
00192 
00193         for frame in self.iter_frames(include_temp=False):
00194             t = {}
00195             t["x"] = float(frame.position[0])
00196             t["y"] = float(frame.position[1])
00197             t["z"] = float(frame.position[2])
00198 
00199             o = {}
00200             o["x"] = float(frame.orientation[0])
00201             o["y"] = float(frame.orientation[1])
00202             o["z"] = float(frame.orientation[2])
00203             o["w"] = float(frame.orientation[3])
00204 
00205             f = {}
00206             f["parent"] = frame.parent
00207             f["position"] = t
00208             f["orientation"] = o
00209 
00210             f["style"] = frame.style
00211 
00212             if frame.style == "plane":
00213                 f["data"] = { "length": frame.length, "width":frame.width, "color": frame.color }
00214 
00215             elif frame.style == "cube":
00216                 f["data"] = { "length": frame.length, "width": frame.width, "height": frame.height , "color": frame.color}
00217 
00218             elif frame.style == "sphere":
00219                 f["data"] = { "diameter": frame.diameter, "color": frame.color }
00220 
00221             elif frame.style == "axis":
00222                 f["data"] = { "length": frame.length, "width": frame.width, "color": frame.color }
00223 
00224             elif frame.style == "mesh":
00225                 f["data"] = { "path" : frame.path, "scale" : frame.scale, "color": frame.color }
00226 
00227             frames[frame.name] = f
00228 
00229         data["frames"] = frames
00230 
00231         ## To parameter server
00232         rospy.set_param(self.namespace, data)
00233         print rospy.get_param(self.namespace)
00234 
00235         ## Dump param to file
00236         print "Saving to file", filename
00237         rosparam.dump_params(filename, self.namespace)
00238         print "Saving done"
00239 
00240         return True
00241 
00242 
00243     def run(self):
00244         print "> Going for some spins"
00245         rate = rospy.Rate(200) # hz
00246         while not rospy.is_shutdown():
00247             self.broadcast()
00248             rate.sleep()
00249 
00250     def parse_args(self, argv):
00251         ## Args ##
00252         ##
00253         # Process standalone plugin command-line arguments
00254         from argparse import ArgumentParser
00255         parser = ArgumentParser()
00256         # Add argument(s) to the parser.
00257         parser.add_argument("-q", "--quiet", action="store_true",
00258                       dest="quiet",
00259                       help="Put plugin in silent mode")
00260         parser.add_argument("-l", "--load", action="append",
00261                       dest="file",
00262                       help="Load a file at startup. [rospack filepath/file]")
00263 
00264         args, unknowns = parser.parse_known_args(argv)
00265         if not args.quiet:
00266             print 'arguments: ', args
00267             print 'unknowns: ', unknowns
00268 
00269         ## Load file ##
00270         ##
00271         if args.file:
00272             arg_path = args.file[0].split()
00273             if len(arg_path) == 1:
00274                 #load file
00275                 filename = arg_path[0]
00276                 print "Loading", filename
00277                 success = self.load_file(str(filename))
00278             elif len(arg_path) == 2:
00279                 #load rospack
00280                 rospack = rospkg.RosPack()
00281                 filename = os.path.join(rospack.get_path(arg_path[0]), arg_path[1])
00282                 print "Loading", filename
00283                 success = self.load_file(str(filename))
00284             else:
00285                 print "Load argument not understood! --load", arg_path
00286                 print "Please use --load 'myRosPackage pathInMyPackage/myYaml.yaml'"
00287                 print "or use --load 'fullPathToMyYaml.yaml'"
00288                 success = None
00289 
00290             if success:
00291                 return filename
00292             elif success == False:
00293                 print "ERROR LOADING FILE"
00294             return ''
00295 
00296     def init_views(self):
00297         ## Views
00298         self.interface_tf = FrameEditor_TF(self)
00299         self.interactive = FrameEditor_InteractiveMarker(self)
00300         self.services = FrameEditor_Services(self)
00301         self.interface_markers = FrameEditor_Markers(self)
00302 
00303 if __name__ == "__main__":
00304 
00305     rospy.init_node('frame_editor')
00306 
00307     editor = FrameEditor()
00308     # editor.load_params(rospy.get_name())
00309 
00310     editor.parse_args(sys.argv[1:])
00311     editor.init_views()
00312 
00313     print "Frame editor ready!"
00314     editor.run()
00315 
00316 # eof


frame_editor
Author(s): ipa-frn
autogenerated on Sat Jun 8 2019 20:21:35