41 from python_qt_binding.QtCore 
import Qt, QTimer, qWarning, Signal
 
   42 from python_qt_binding.QtWidgets 
import QGraphicsScene, QMessageBox
 
   44 from rqt_bag 
import bag_helper
 
   46 from .timeline_frame 
import TimelineFrame
 
   47 from .message_listener_thread 
import MessageListenerThread
 
   48 from .message_loader_thread 
import MessageLoaderThread
 
   49 from .player 
import Player
 
   50 from .recorder 
import Recorder
 
   51 from .timeline_menu 
import TimelinePopupMenu
 
   57     BagTimeline contains bag files, all information required to display the bag data visualization 
   58     on the screen Also handles events 
   60     status_bar_changed_signal = Signal()
 
   61     selected_region_changed = Signal(rospy.Time, rospy.Time)
 
   66             plugin context hook to enable adding rqt_bag plugin widgets as ROS_GUI snapin panes, 
  110         self.setBackgroundBrush(Qt.white)
 
  120         :returns: the ROS_GUI context, 'PluginContext' 
  126         Cleans up the timeline, bag and any threads 
  143         for bag 
in self.
_bags:
 
  152         creates an indexing thread for each new topic in the bag 
  153         fixes the boarders and notifies the indexing thread to index the new items bags 
  154         :param bag: ros bag file, ''rosbag.bag'' 
  156         self.
_bags.append(bag)
 
  158         bag_topics = bag_helper.get_topics(bag)
 
  162         for topic 
in new_topics:
 
  177             for topic 
in bag_topics:
 
  186             return sum(b.size 
for b 
in self.
_bags)
 
  191         :return: first stamp in the bags, ''rospy.Time'' 
  195             for bag 
in self.
_bags:
 
  196                 bag_start_stamp = bag_helper.get_start_stamp(bag)
 
  197                 if bag_start_stamp 
is not None and \
 
  198                         (start_stamp 
is None or bag_start_stamp < start_stamp):
 
  199                     start_stamp = bag_start_stamp
 
  204         :return: last stamp in the bags, ''rospy.Time'' 
  208             for bag 
in self.
_bags:
 
  209                 bag_end_stamp = bag_helper.get_end_stamp(bag)
 
  210                 if bag_end_stamp 
is not None and (end_stamp 
is None or bag_end_stamp > end_stamp):
 
  211                     end_stamp = bag_end_stamp
 
  216         :return: sorted list of topic names, ''list(str)'' 
  220             for bag 
in self.
_bags:
 
  221                 for topic 
in bag_helper.get_topics(bag):
 
  223             return sorted(topics)
 
  227         :return: dict of list of topics for each datatype, ''dict(datatype:list(topic))'' 
  230             topics_by_datatype = {}
 
  231             for bag 
in self.
_bags:
 
  232                 for datatype, topics 
in bag_helper.get_topics_by_datatype(bag).items():
 
  233                     topics_by_datatype.setdefault(datatype, []).extend(topics)
 
  234             return topics_by_datatype
 
  238         :return: datatype associated with a topic, ''str'' 
  239         :raises: if there are multiple datatypes assigned to a single topic, ''Exception'' 
  243             for bag 
in self.
_bags:
 
  244                 bag_datatype = bag_helper.get_datatype(bag, topic)
 
  245                 if datatype 
and bag_datatype 
and (bag_datatype != datatype):
 
  246                     raise Exception(
'topic %s has multiple datatypes: %s and %s' %
 
  247                                     (topic, datatype, bag_datatype))
 
  249                     datatype = bag_datatype
 
  254         generator function for bag entries 
  255         :param topics: list of topics to query, ''list(str)'' 
  256         :param start_stamp: stamp to start at, ''rospy.Time'' 
  257         :param end_stamp: stamp to end at, ''rospy,Time'' 
  258         :returns: entries the bag file, ''msg'' 
  263                 bag_start_time = bag_helper.get_start_stamp(b)
 
  264                 if bag_start_time 
is not None and bag_start_time > end_stamp:
 
  267                 bag_end_time = bag_helper.get_end_stamp(b)
 
  268                 if bag_end_time 
is not None and bag_end_time < start_stamp:
 
  271                 connections = list(b._get_connections(topics))
 
  274                 bag_entries.append(sorted(b._get_entries(connections, start_stamp, end_stamp)))
 
  276             for entry 
in heapq.merge(*bag_entries):
 
  281         generator function for bag entries 
  282         :param topics: list of topics to query, ''list(str)'' 
  283         :param start_stamp: stamp to start at, ''rospy.Time'' 
  284         :param end_stamp: stamp to end at, ''rospy,Time'' 
  285         :returns: tuple of (bag, entry) for the entries in the bag file, ''(rosbag.Bag, rosbag.bag._IndexEntry)'' 
  289             for bag 
in self.
_bags:
 
  290                 bag_start_time = bag_helper.get_start_stamp(bag)
 
  291                 if bag_start_time 
is not None and bag_start_time > end_stamp:
 
  294                 bag_end_time = bag_helper.get_end_stamp(bag)
 
  295                 if bag_end_time 
is not None and bag_end_time < start_stamp:
 
  298                 connections = list(bag._get_connections(topic))
 
  301                 bag_entries.append([(bag, entry) 
for entry 
in sorted(
 
  302                     bag._get_entries(connections, start_stamp, end_stamp))])
 
  306             for bag_entry 
in sorted(itertools.chain(*bag_entries), key=
lambda bag_entry: bag_entry[1].time):
 
  312         :param t: time, ''rospy.Time'' 
  313         :param topic: the topic to be accessed, ''str'' 
  314         :return: tuple of (bag, entry) corisponding to time t and topic, ''(rosbag.bag, msg)'' 
  317             entry_bag, entry = 
None, 
None 
  318             for bag 
in self.
_bags:
 
  319                 bag_entry = bag._get_entry(t, bag._get_connections(topic))
 
  320                 if bag_entry 
and (
not entry 
or bag_entry.time > entry.time):
 
  321                     entry_bag, entry = bag, bag_entry
 
  323             return entry_bag, entry
 
  328         :param t: time, ''rospy.Time'' 
  329         :return: tuple of (bag, entry) corresponding to time t, ''(rosbag.bag, msg)'' 
  332             entry_bag, entry = 
None, 
None 
  333             for bag 
in self.
_bags:
 
  334                 bag_entry = bag._get_entry(t - rospy.Duration(0, 1), bag._get_connections())
 
  335                 if bag_entry 
and (
not entry 
or bag_entry.time < entry.time):
 
  336                     entry_bag, entry = bag, bag_entry
 
  338             return entry_bag, entry
 
  343         :param t: time, ''rospy.Time'' 
  344         :return: tuple of (bag, entry) corisponding to time t, ''(rosbag.bag, msg)'' 
  347             entry_bag, entry = 
None, 
None 
  348             for bag 
in self.
_bags:
 
  349                 bag_entry = bag._get_entry_after(t, bag._get_connections())
 
  350                 if bag_entry 
and (
not entry 
or bag_entry.time < entry.time):
 
  351                     entry_bag, entry = bag, bag_entry
 
  353             return entry_bag, entry
 
  357         :return: time of the next message after the current playhead position,''rospy.Time'' 
  370         :return: time of the next message before the current playhead position,''rospy.Time'' 
  389         Verify that a background task is not currently running before starting a new one 
  390         :param background_task: name of the background task, ''str'' 
  394                 QMessageBox.Warning, 
'Exclamation', 
'Background operation already running:\n\n%s' %
 
  406         if len(self.
_bags) > 0:
 
  413         Starts a thread to save the current selection to a new bag file 
  414         :param path: filesystem path to write to, ''str'' 
  415         :param topics: topics to write to the file, ''list(str)'' 
  416         :param start_stamp: start of area to save, ''rospy.Time'' 
  417         :param end_stamp: end of area to save, ''rospy.Time'' 
  428         total_messages = len(bag_entries)
 
  431         if total_messages == 0:
 
  432             QMessageBox(QMessageBox.Warning, 
'rqt_bag', 
'No messages found', QMessageBox.Ok).exec_()
 
  440             QMessageBox(QMessageBox.Warning, 
'rqt_bag',
 
  441                         'Error opening bag file [%s] for writing' % path, QMessageBox.Ok).exec_()
 
  448             args=(export_bag, topics, start_stamp, end_stamp, bag_entries))
 
  453         Threaded function that saves the current selection to a new bag file 
  454         :param export_bag: bagfile to write to, ''rosbag.bag'' 
  455         :param topics: topics to write to the file, ''list(str)'' 
  456         :param start_stamp: start of area to save, ''rospy.Time'' 
  457         :param end_stamp: end of area to save, ''rospy.Time'' 
  459         total_messages = len(bag_entries)
 
  460         update_step = max(1, total_messages / 100)
 
  464         for bag, entry 
in bag_entries:
 
  469                 export_bag.write(topic, msg, t)
 
  470             except Exception 
as ex:
 
  471                 qWarning(
'Error exporting message at position %s: %s' %
 
  472                          (str(entry.position), str(ex)))
 
  477             if message_num % update_step == 0 
or message_num == total_messages:
 
  478                 new_progress = int(100.0 * (float(message_num) / total_messages))
 
  479                 if new_progress != progress:
 
  480                     progress = new_progress
 
  492         except Exception 
as ex:
 
  493             QMessageBox(QMessageBox.Warning, 
'rqt_bag', 
'Error closing bag file [%s]: %s' % (
 
  494                 export_bag.filename, str(ex)), QMessageBox.Ok).exec_()
 
  499             return bag._read_message(position)
 
  503         if event.buttons() == Qt.LeftButton:
 
  505         elif event.buttons() == Qt.MidButton:
 
  507         elif event.buttons() == Qt.RightButton:
 
  508             topic = self.
_timeline_frame.map_y_to_topic(self.views()[0].mapToScene(event.pos()).y())
 
  560                     self.
_player.start_clock_publishing()
 
  561             except Exception 
as ex:
 
  562                 qWarning(
'Error starting player; aborting publish: %s' % str(ex))
 
  590     play_all = property(_get_play_all, _set_play_all)
 
  601         moves the playhead to the next position based on the desired position 
  616         Moves the playhead a fixed distance into the future based on the current play speed 
  623         now = rospy.Time.from_sec(time.time())
 
  627                 new_playhead = self.end_stamp
 
  634                 if new_playhead > end_stamp:
 
  637                             new_playhead = start_stamp
 
  639                             new_playhead = end_stamp
 
  641                         new_playhead = end_stamp
 
  646                 elif new_playhead < start_stamp:
 
  649                             new_playhead = end_stamp
 
  651                             new_playhead = start_stamp
 
  653                         new_playhead = start_stamp
 
  663         Move the playhead to the next message 
  674                 delta = rospy.Time.from_sec(time.time()) - self.
last_frame 
  675                 if delta > rospy.Duration.from_sec(0.1):
 
  676                     delta = rospy.Duration.from_sec(0.1)
 
  687         self.
last_frame = rospy.Time.from_sec(time.time())
 
  692     def record_bag(self, filename, all=True, topics=[], regex=False, limit=0):
 
  695                 filename, bag_lock=self.
_bag_lock, all=all, topics=topics, regex=regex, limit=limit)
 
  696         except Exception 
as ex:
 
  697             qWarning(
'Error opening bag for recording [%s]: %s' % (filename, str(ex)))
 
  740                     listener.timeline_changed()
 
  741                 except Exception 
as ex:
 
  742                     qWarning(
'Error calling timeline_changed on %s: %s' % (type(listener), str(ex)))
 
  756         self.
_listeners.setdefault(topic, []).append(listener)
 
  769         if topic_listeners 
is not None and listener 
in topic_listeners:
 
  770             topic_listeners.remove(listener)
 
  772             if len(topic_listeners) == 0:
 
  795         elif play_speed < 0.0:
 
  804     play_speed = property(_get_play_speed, _set_play_speed)
 
  814         self.
last_frame = rospy.Time.from_sec(time.time())
 
  836             new_play_speed = -1.0