movement_recorder.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2011 Shadow Robot Company Ltd.
00004 #
00005 # This program is free software: you can redistribute it and/or modify it
00006 # under the terms of the GNU General Public License as published by the Free
00007 # Software Foundation, either version 2 of the License, or (at your option)
00008 # any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful, but WITHOUT
00011 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00013 # more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 #
00018 
00019 import os
00020 import rospkg
00021 import xml.etree.ElementTree as ET
00022 import time
00023 import threading
00024 import rospy
00025 
00026 from qt_gui.plugin import Plugin
00027 from python_qt_binding import loadUi
00028 from PyQt4.Qt import QTimer, QLayout, QPalette, QDoubleValidator, QIntValidator
00029 from sr_robot_commander.sr_hand_commander import SrHandCommander
00030 from sr_utilities.hand_finder import HandFinder
00031 
00032 from QtCore import Qt, SIGNAL, pyqtSignal
00033 from QtGui import QWidget, QFrame, QColor
00034 from QtGui import QLabel, QComboBox, QLineEdit, QPushButton, QHBoxLayout, QVBoxLayout, QGridLayout, QIcon, QFileDialog
00035 
00036 
00037 class Step(QWidget):
00038 
00039     """
00040     A step in a sequence of steps which compose a full movement.
00041     Contains add / remove steps buttons, loop control, pause / interpolation time,
00042     grasp type.
00043     """
00044 
00045     def __init__(self, parent, step_index, plugin_parent):
00046         QWidget.__init__(self, parent=parent)
00047         self.step_index = step_index
00048         self.parent = plugin_parent
00049         self.grasp = 0
00050         self.pause_time = 0
00051         self.interpolation_time = 2
00052         self.loop_to_step = -1
00053         self.number_of_loops = 0
00054         self.remaining_loops = 0
00055 
00056         self.widgets = []
00057 
00058     def draw(self):
00059         self.frame = QFrame(self)
00060         self.green = QColor(153, 231, 96)
00061         self.saved_palette = self.palette()
00062         green_palette = self.palette()
00063         green_palette.setBrush(QPalette.Window, self.green)
00064         self.frame.setPalette(green_palette)
00065 
00066         label_grasp = QLabel(self.frame)
00067         label_grasp.setText("Grasp:  ")
00068         self.widgets.append(label_grasp)
00069 
00070         self.list_grasp = QComboBox(self.frame)
00071         self.refresh_list()
00072         self.frame.connect(self.list_grasp, SIGNAL(
00073             'activated(QString)'), self.grasp_choosed)
00074         self.widgets.append(self.list_grasp)
00075 
00076         label_pause = QLabel(self.frame)
00077         label_pause.setText(" Pause Time:")
00078         self.widgets.append(label_pause)
00079 
00080         self.pause_input = QLineEdit(self.frame)
00081         self.pause_input.setValidator(QDoubleValidator(self))
00082         self.pause_input.setText("0.0")
00083         self.pause_input.setFixedWidth(35)
00084         self.pause_input.setAlignment(Qt.AlignRight)
00085         self.frame.connect(self.pause_input, SIGNAL(
00086             'textChanged(QString)'), self.pause_changed)
00087         self.widgets.append(self.pause_input)
00088 
00089         label_interp = QLabel(self.frame)
00090         label_interp.setText("s.   Interpolation Time:")
00091         self.widgets.append(label_interp)
00092 
00093         self.interp_input = QLineEdit(self.frame)
00094         self.interp_input.setValidator(QDoubleValidator(self))
00095         self.interp_input.setText("2.0")
00096         self.interp_input.setAlignment(Qt.AlignRight)
00097         self.interp_input.setFixedWidth(35)
00098         self.frame.connect(self.interp_input, SIGNAL(
00099             'textChanged(QString)'), self.interp_changed)
00100         self.widgets.append(self.interp_input)
00101 
00102         label_looping = QLabel(self.frame)
00103         label_looping.setText("s.   Loop to step:")
00104         self.widgets.append(label_looping)
00105 
00106         self.loop_input = QComboBox(self.frame)
00107         for i in range(0, self.step_index + 1):
00108             if i == 0:
00109                 self.loop_input.addItem("None")
00110             else:
00111                 self.loop_input.addItem(str(i))
00112         self.frame.connect(self.loop_input, SIGNAL(
00113             'activated(QString)'), self.choose_looping)
00114         self.widgets.append(self.loop_input)
00115 
00116         self.number_loops = QLineEdit(self.frame)
00117         self.number_loops.setValidator(QIntValidator(self))
00118         self.number_loops.setDisabled(True)
00119         self.number_loops.setText("0")
00120         self.number_loops.setAlignment(Qt.AlignRight)
00121         self.number_loops.setFixedWidth(35)
00122         # self.number_loops.setReadOnly(True)
00123         # self.number_loops.setStyleSheet("QWidget { background-color:
00124         # lightgrey }")
00125         self.frame.connect(self.number_loops, SIGNAL(
00126             'textChanged(QString)'), self.number_loops_changed)
00127         self.widgets.append(self.number_loops)
00128 
00129         label_times = QLabel(self.frame)
00130         label_times.setText("times. ")
00131         self.widgets.append(label_times)
00132 
00133         self.new_step_button = QPushButton(self.frame)
00134         self.new_step_button.setText("+")
00135         self.new_step_button.setFixedWidth(20)
00136         self.frame.connect(
00137             self.new_step_button, SIGNAL('clicked()'), self.add_step)
00138         self.widgets.append(self.new_step_button)
00139 
00140         self.remove_step_button = QPushButton(self.frame)
00141         self.remove_step_button.setText("-")
00142         self.remove_step_button.setFixedWidth(20)
00143         self.frame.connect(
00144             self.remove_step_button, SIGNAL('clicked()'), self.remove_step)
00145         self.widgets.append(self.remove_step_button)
00146 
00147         self.layout = QHBoxLayout()
00148         self.layout.setAlignment(Qt.AlignCenter)
00149         self.layout.setSpacing(2)
00150         self.layout.addWidget(label_grasp)
00151         self.layout.addWidget(self.list_grasp)
00152         self.layout.addWidget(label_pause)
00153         self.layout.addWidget(self.pause_input)
00154         self.layout.addWidget(label_interp)
00155         self.layout.addWidget(self.interp_input)
00156         self.layout.addWidget(label_looping)
00157         self.layout.addWidget(self.loop_input)
00158         self.layout.addWidget(self.number_loops)
00159         self.layout.addWidget(label_times)
00160         self.layout.addWidget(self.new_step_button)
00161         self.layout.addWidget(self.remove_step_button)
00162 
00163         self.frame.setLayout(self.layout)
00164 
00165         layout = QVBoxLayout()
00166         layout.addWidget(self.frame)
00167         self.frame.show()
00168         self.setLayout(layout)
00169         # self.setWidget(self.frame)
00170         self.show()
00171 
00172     def grasp_choosed(self, grasp_name):
00173         self.grasp = str(grasp_name)
00174 
00175     def pause_changed(self, pause_time):
00176         self.pause_time = float(pause_time)
00177 
00178     def interp_changed(self, interp_time):
00179         self.interpolation_time = float(interp_time)
00180 
00181     def add_step(self):
00182         self.parent.add_step(self.step_index)
00183 
00184     def number_loops_changed(self, number_loops):
00185         self.number_of_loops = int(number_loops)
00186 
00187     def choose_looping(self, looping):
00188         if looping == "None":
00189             self.number_loops.setDisabled(True)
00190             self.number_loops.setText("0")
00191             self.loop_to_step = -1
00192         else:
00193             self.number_loops.setEnabled(True)
00194             self.number_loops.setText("1")
00195             self.number_of_loops = 1
00196             self.loop_to_step = int(looping) - 1
00197 
00198     def remove_step(self, delete_first=False):
00199         """
00200         Make sure we don't delete the first item from the GUI
00201         """
00202         if not delete_first:
00203             if len(self.parent.steps) <= 1:
00204                 return
00205         self.close()
00206         self.parent.steps.remove(self)
00207         del self
00208 
00209     def set_step_id(self, index):
00210         self.step_index = index
00211 
00212     def is_playing(self):
00213         self.frame.setAutoFillBackground(True)
00214         self.frame.repaint()
00215 
00216     def stopped_playing(self):
00217         self.frame.setAutoFillBackground(False)
00218         self.frame.repaint()
00219 
00220     def save_to_xml(self):
00221         xml_step = ET.Element("step")
00222         grasp = ET.SubElement(xml_step, "grasp")
00223         grasp.set("name", self.grasp)
00224         pause = ET.SubElement(xml_step, "pause_time")
00225         pause.text = str(self.pause_time)
00226         interpolation = ET.SubElement(xml_step, "interpolation_time")
00227         interpolation.text = str(self.interpolation_time)
00228         looping = ET.SubElement(xml_step, "loop_to_step")
00229         looping.text = str(self.loop_to_step)
00230         nb_loops = ET.SubElement(xml_step, "number_loops")
00231         nb_loops.text = str(self.number_of_loops)
00232         return xml_step
00233 
00234     def load_from_xml(self, xml_element):
00235         for subelement in xml_element:
00236             if subelement.tag == "grasp":
00237                 grasp_name = subelement.attrib.get("name")
00238                 self.grasp_choosed(grasp_name)
00239                 list_grasps = self.parent.hand_commander.get_named_targets()
00240                 list_grasps.sort()
00241                 for index, grasp_name_ref in zip(range(0, len(list_grasps)), list_grasps):
00242                     if grasp_name == grasp_name_ref:
00243                         self.list_grasp.setCurrentIndex(index)
00244                         break
00245             if subelement.tag == "pause_time":
00246                 self.pause_time = float(subelement.text)
00247                 self.pause_input.setText(subelement.text)
00248 
00249             if subelement.tag == "interpolation_time":
00250                 self.interpolation_time = float(subelement.text)
00251                 self.interp_input.setText(subelement.text)
00252 
00253             if subelement.tag == "loop_to_step":
00254                 self.loop_to_step = int(subelement.text)
00255                 self.loop_input.setCurrentIndex(self.loop_to_step + 1)
00256                 if self.loop_to_step == -1:
00257                     self.number_loops.setDisabled(True)
00258                 else:
00259                     self.number_loops.setEnabled(True)
00260 
00261             if subelement.tag == "number_loops":
00262                 self.number_of_loops = int(subelement.text)
00263                 self.number_loops.setText(subelement.text)
00264         self.grasp_slider = None
00265 
00266     def refresh_list(self, value=0):
00267         self.list_grasp.clear()
00268         list_grasps = self.parent.hand_commander.get_named_targets()
00269         list_grasps.sort()
00270 
00271         self.grasp = list_grasps[0]
00272 
00273         for grasp_name in list_grasps:
00274             self.list_grasp.addItem(grasp_name)
00275 
00276 
00277 class SrGuiMovementRecorder(Plugin):
00278 
00279     """
00280     A rosgui plugin for recording and replaying movements
00281     """
00282 
00283     def __init__(self, context):
00284         super(SrGuiMovementRecorder, self).__init__(context)
00285         self.setObjectName('SrGuiMovementRecorder')
00286 
00287         self._publisher = None
00288         self._widget = QWidget()
00289 
00290         ui_file = os.path.join(rospkg.RosPack().get_path(
00291             'sr_gui_movement_recorder'), 'uis', 'SrGuiMovementRecorder.ui')
00292         loadUi(ui_file, self._widget)
00293         self._widget.setObjectName('SrGuiMovementRecorderUi')
00294         context.add_widget(self._widget)
00295 
00296         self.frame = self._widget.frame
00297         self.timer = QTimer(self.frame)
00298 
00299         self.layout = QVBoxLayout()
00300         self.layout.setSpacing(2)
00301 
00302         self.layout.setAlignment(Qt.AlignCenter)
00303         self.layout.setSizeConstraint(QLayout.SetFixedSize)
00304 
00305         self.sublayout = QGridLayout()
00306         self.command_frame = QFrame()
00307 
00308         # setting up the hand selection
00309         hand_finder = HandFinder()
00310         self.hand_parameters = hand_finder.get_hand_parameters()
00311 
00312         self.sublayout.addWidget(QLabel("Select Hand"), 0, 0)
00313         self.hand_combo_box = QComboBox()
00314 
00315         for hand_serial in self.hand_parameters.mapping.keys():
00316             self.hand_combo_box.addItem(hand_serial)
00317 
00318         self.sublayout.addWidget(self.hand_combo_box, 0, 1)
00319 
00320         self.frame.connect(self.hand_combo_box, SIGNAL('activated(QString)'), self.hand_selected)
00321 
00322         self.play_btn = QPushButton()
00323         self.play_btn.setText("Play")
00324         self.play_btn.setFixedWidth(80)
00325         self.command_frame.connect(
00326             self.play_btn, SIGNAL('clicked()'), self.button_play_clicked)
00327         self.sublayout.addWidget(self.play_btn, 0, 2)
00328 
00329         self.mutex = threading.Lock()
00330         self.stopped = True
00331 
00332         self.thread = None
00333 
00334         self.stop_btn = QPushButton()
00335         self.stop_btn.setText("Stop")
00336         self.stop_btn.setFixedWidth(80)
00337         self.command_frame.connect(
00338             self.stop_btn, SIGNAL('clicked()'), self.stop)
00339         self.sublayout.addWidget(self.stop_btn, 0, 3)
00340 
00341         self.sublayout.addWidget(QLabel(''), 0, 4)
00342 
00343         self.save_btn = QPushButton()
00344         self.save_btn.setText("Save")
00345         self.save_btn.setFixedWidth(80)
00346         self.command_frame.connect(
00347             self.save_btn, SIGNAL('clicked()'), self.save)
00348         self.sublayout.addWidget(self.save_btn, 0, 5)
00349 
00350         self.load_btn = QPushButton()
00351         self.load_btn.setText("Load")
00352         self.load_btn.setFixedWidth(80)
00353         self.command_frame.connect(
00354             self.load_btn, SIGNAL('clicked()'), self.load)
00355         self.sublayout.addWidget(self.load_btn, 0, 6)
00356 
00357         self.command_frame.setLayout(self.sublayout)
00358         self.layout.addWidget(self.command_frame)
00359 
00360         self.frame.setLayout(self.layout)
00361 
00362         path_to_icons = os.path.join(
00363             rospkg.RosPack().get_path('sr_visualization_icons'), 'icons')
00364         self.load_btn.setIcon(QIcon(os.path.join(path_to_icons, 'load.png')))
00365         self.save_btn.setIcon(QIcon(os.path.join(path_to_icons, 'save.png')))
00366         self.stop_btn.setIcon(QIcon(os.path.join(path_to_icons, 'stop.png')))
00367         self.play_btn.setIcon(QIcon(os.path.join(path_to_icons, 'play.png')))
00368 
00369         self.hand_commander = None
00370         self.steps = []
00371 
00372         self.steps_frame = QFrame()
00373         self.steps_layout = QVBoxLayout()
00374         self.steps_frame.setLayout(self.steps_layout)
00375 
00376         self.layout.addWidget(self.steps_frame)
00377 
00378         # selecting the first available hand
00379         self.__selected_hand = None
00380         self.hand_selected(self.hand_parameters.mapping.keys()[0])
00381 
00382     def hand_selected(self, serial):
00383         self.hand_commander = SrHandCommander(hand_parameters=self.hand_parameters,
00384                                               hand_serial=serial)
00385 
00386         if self.__selected_hand == serial:
00387             return
00388 
00389         self.remove_all_steps()
00390         self.add_step()
00391         self.__selected_hand = serial
00392 
00393     def save(self):
00394         filename = QFileDialog.getSaveFileName(self.frame, 'Save Script', '')
00395         filename = filename[0]
00396 
00397         if filename == "":
00398             return
00399 
00400         root = ET.Element("movement")
00401         for step in self.steps:
00402             root.append(step.save_to_xml())
00403 
00404         self.indent(root)
00405         tree = ET.ElementTree(root)
00406 
00407         tree.write(filename)
00408 
00409     def indent(self, elem, level=0):
00410         """
00411         print a prettier / indented xml tree
00412         """
00413         i = "\n" + level * "  "
00414         if len(elem):
00415             if not elem.text or not elem.text.strip():
00416                 elem.text = i + "  "
00417             if not elem.tail or not elem.tail.strip():
00418                 elem.tail = i
00419             for elem in elem:
00420                 self.indent(elem, level + 1)
00421             if not elem.tail or not elem.tail.strip():
00422                 elem.tail = i
00423         else:
00424             if level and (not elem.tail or not elem.tail.strip()):
00425                 elem.tail = i
00426 
00427     def load(self):
00428         """
00429         remove all the present steps
00430         and load new from xml file
00431         """
00432         filename = QFileDialog.getOpenFileName(self.frame, 'Open Script', '')
00433         filename = filename[0]
00434 
00435         if filename != "":
00436             self.remove_all_steps()
00437 
00438             tree = ET.parse(filename)
00439             root = tree.getroot()
00440             xml_steps = tree.findall("step")
00441 
00442             for step in xml_steps:
00443                 self.add_step()
00444                 self.steps[-1].load_from_xml(step)
00445 
00446     def remove_all_steps(self):
00447         while len(self.steps) != 0:
00448             self.steps[0].remove_step(delete_first=True)
00449 
00450     def add_step(self, step_index=None):
00451         if step_index is None:
00452             step_index = len(self.steps)
00453         else:
00454             step_index += 1
00455 
00456         step_tmp = Step(self.frame, step_index, self)
00457         self.steps.insert(step_index, step_tmp)
00458 
00459         for index, step in enumerate(self.steps):
00460             step.set_step_id(index)
00461 
00462         self.steps_layout.insertWidget(step_index, step_tmp)
00463         step_tmp.draw()
00464 
00465     def button_play_clicked(self):
00466         if len(self.steps) < 1:
00467             return
00468 
00469         self.play_btn.setDisabled(True)
00470         self.load_btn.setDisabled(True)
00471 
00472         for step in self.steps:
00473             step.remove_step_button.setDisabled(True)
00474             step.new_step_button.setDisabled(True)
00475             step.remaining_loops = step.number_of_loops
00476 
00477         self.fill_trajectory()
00478 
00479         self.thread = threading.Thread(None, self.play)
00480         self.thread.start()
00481 
00482     def fill_trajectory(self, step_index=None):
00483         if step_index is None:
00484             self.trajectory = []
00485             step_index = 0
00486         elif step_index >= len(self.steps):
00487             return
00488 
00489         step = self.steps[step_index]
00490 
00491         self.trajectory.append(
00492             {
00493                 'name': step.grasp,
00494                 'interpolate_time': step.interpolation_time,
00495                 'pause_time': step.pause_time
00496             }
00497         )
00498 
00499         if step.loop_to_step != -1 and step.remaining_loops > 0:
00500             step.remaining_loops -= 1
00501             next_step = step.loop_to_step
00502         else:
00503             next_step = step_index + 1
00504         self.fill_trajectory(next_step)
00505 
00506     def play(self):
00507         self.stopped = False
00508         first_time = True
00509         index = 0
00510 
00511         run = True
00512         in_progress = False
00513         while run:
00514             self.mutex.acquire()
00515             if self.stopped:
00516                 self.mutex.release()
00517                 return
00518             self.mutex.release()
00519 
00520             if not in_progress:
00521                 self.hand_commander.run_named_trajectory_unsafe(self.trajectory)
00522                 in_progress = True
00523             else:
00524                 rospy.sleep(0.1)
00525                 run = self.hand_commander.action_is_running()
00526 
00527         self.stop()
00528 
00529     def stop(self):
00530         self.hand_commander.send_stop_trajectory_unsafe()
00531         for step in self.steps:
00532             step.new_step_button.setEnabled(True)
00533             step.remove_step_button.setEnabled(True)
00534         self.mutex.acquire()
00535         self.stopped = True
00536         self.mutex.release()
00537         self.play_btn.setEnabled(True)
00538         self.load_btn.setEnabled(True)
00539 
00540     def _unregisterPublisher(self):
00541         if self._publisher is not None:
00542             self._publisher.unregister()
00543             self._publisher = None
00544 
00545     def shutdown_plugin(self):
00546         self.remove_all_steps()
00547         self._unregisterPublisher()


sr_gui_movement_recorder
Author(s): Ugo Cupcic
autogenerated on Thu Jun 6 2019 21:13:58