Ubuntu 22.04安装ROS 1教程汇总
# Software License Agreement (BSD License)
#
# [License text保持不变]
from python_qt_binding.QtCore import qDebug, QPointF, QRectF, Qt, qWarning, Signal
from python_qt_binding.QtGui import QBrush, QCursor, QColor, QFont, \
QFontMetrics, QPen, QPolygonF
from python_qt_binding.QtWidgets import QGraphicsItem
import rospy
import bisect
import threading
from .index_cache_thread import IndexCacheThread
from .plugins.raw_view import RawView
class _SelectionMode(object):
"""
SelectionMode states consolidated for readability
NONE = no region marked or started
LEFT_MARKED = one end of the region has been marked
MARKED = both ends of the region have been marked
SHIFTING = region is marked; currently dragging the region
MOVE_LEFT = region is marked; currently changing the left boundary of the selected region
MOVE_RIGHT = region is marked; currently changing the right boundary of the selected region
"""
NONE = 'none'
LEFT_MARKED = 'left marked'
MARKED = 'marked'
SHIFTING = 'shifting'
MOVE_LEFT = 'move left'
MOVE_RIGHT = 'move right'
class TimelineFrame(QGraphicsItem):
"""
TimelineFrame Draws the framing elements for the bag messages
(time delimiters, labels, topic names and backgrounds).
Also handles mouse callbacks since they interact closely with the drawn elements
"""
def __init__(self, bag_timeline):
super(TimelineFrame, self).__init__()
self._bag_timeline = bag_timeline
self._clicked_pos = None
self._dragged_pos = None
# Timeline boundaries
self._start_stamp = None # earliest of all stamps
self._end_stamp = None # latest of all stamps
self._stamp_left = None # earliest currently visible timestamp on the timeline
self._stamp_right = None # latest currently visible timestamp on the timeline
self._history_top = 30
self._history_left = 0
self._history_width = 0
self._history_bottom = 0
self._history_bounds = {}
self._margin_left = 4
self._margin_right = 20
self._margin_bottom = 20
self._history_top = 30
# Background Rendering
# color of background of timeline before first message and after last
self._bag_end_color = QColor(0, 0, 0, 25)
self._history_background_color_alternate = QColor(179, 179, 179, 25)
self._history_background_color = QColor(204, 204, 204, 102)
# Timeline Division Rendering
# Possible time intervals used between divisions
# 1ms, 5ms, 10ms, 50ms, 100ms, 500ms
# 1s, 5s, 15s, 30s
# 1m, 2m, 5m, 10m, 15m, 30m
# 1h, 2h, 3h, 6h, 12h
# 1d, 7d
self._sec_divisions = [0.001, 0.005, 0.01, 0.05, 0.1, 0.5,
1, 5, 15, 30,
1 * 60, 2 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60,
1 * 60 * 60, 2 * 60 * 60, 3 * 60 * 60, 6 * 60 * 60, 12 * 60 * 60,
1 * 60 * 60 * 24, 7 * 60 * 60 * 24]
self._minor_spacing = 15
self._major_spacing = 50
self._major_divisions_label_indent = 3 # padding in px between line and label
self._major_division_pen = QPen(QBrush(Qt.black), 0, Qt.DashLine)
self._minor_division_pen = QPen(QBrush(QColor(153, 153, 153, 128)), 0, Qt.DashLine)
self._minor_division_tick_pen = QPen(QBrush(QColor(128, 128, 128, 128)), 0)
# Topic Rendering
self.topics = []
self._topics_by_datatype = {}
self._topic_font_height = None
self._topic_name_sizes = None
# minimum pixels between end of topic name and start of history
self._topic_name_spacing = 3
self._topic_font_size = 10.0
self._topic_font = QFont("cairo")
self._topic_font.setPointSize(int(self._topic_font_size))
self._topic_font.setBold(False)
self._topic_vertical_padding = 4
# percentage of the horiz space that can be used for topic display
self._topic_name_max_percent = 25.0
# Time Rendering
self._time_tick_height = 5
self._time_font_height = None
self._time_font_size = 10.0
self._time_font = QFont("cairo")
self._time_font.setPointSize(int(self._time_font_size))
self._time_font.setBold(False)
# Defaults
self._default_brush = QBrush(Qt.black, Qt.SolidPattern)
self._default_pen = QPen(Qt.black)
self._default_datatype_color = QColor(0, 0, 102, 204)
self._datatype_colors = {
'sensor_msgs/CameraInfo': QColor(0, 0, 77, 204),
'sensor_msgs/Image': QColor(0, 77, 77, 204),
'sensor_msgs/LaserScan': QColor(153, 0, 0, 204),
'pr2_msgs/LaserScannerSignal': QColor(153, 0, 0, 204),
'pr2_mechanism_msgs/MechanismState': QColor(0, 153, 0, 204),
'tf/tfMessage': QColor(0, 153, 0, 204),
}
# minimum number of pixels allowed between two bag messages before they are combined
self._default_msg_combine_px = 1.0
self._active_message_line_width = 3
# Selected Region Rendering
self._selected_region_color = QColor(0, 179, 0, 21)
self._selected_region_outline_top_color = QColor(0, 77, 0, 51)
self._selected_region_outline_ends_color = QColor(0, 77, 0, 102)
self._selecting_mode = _SelectionMode.NONE
self._selected_left = None
self._selected_right = None
self._selection_handle_width = 3.0
# Playhead Rendering
self._playhead = None # timestamp of the playhead
self._paused = False
self._playhead_pointer_size = (6, 6)
self._playhead_line_width = 1
self._playhead_color = QColor(255, 0, 0, 191)
# Zoom
self._zoom_sensitivity = 0.005
self._min_zoom_speed = 0.5
self._max_zoom_speed = 2.0
self._min_zoom = 0.0001 # max zoom out (in px/s)
self._max_zoom = 50000.0 # max zoom in (in px/s)
# Plugin management
self._viewer_types = {}
self._timeline_renderers = {}
self._rendered_topics = set()
self.load_plugins()
# Bag indexer for rendering the default message views on the timeline
self.index_cache_cv = threading.Condition()
self.index_cache = {}
self.invalidated_caches = set()
self._index_cache_thread = IndexCacheThread(self)
# TODO the API interface should exist entirely at the bag_timeline level.
# Add a "get_draw_parameters()" at the bag_timeline level to access these
# Properties, work in progress API for plugins:
# property: playhead
def _get_playhead(self):
return self._playhead
def _set_playhead(self, playhead):
"""
Sets the playhead to the new position, notifies the threads and updates the scene
so it will redraw
:signal: emits status_bar_changed_signal if the playhead is successfully set
:param playhead: Time to set the playhead to, ''rospy.Time()''
"""
with self.scene()._playhead_lock:
if playhead == self._playhead:
return
self._playhead = playhead
if self._playhead != self._end_stamp:
self.scene().stick_to_end = False
playhead_secs = playhead.to_sec()
if playhead_secs > self._stamp_right:
dstamp = playhead_secs - self._stamp_right + \
(self._stamp_right - self._stamp_left) * 0.75
if dstamp > self._end_stamp.to_sec() - self._stamp_right:
dstamp = self._end_stamp.to_sec() - self._stamp_right
self.translate_timeline(dstamp)
elif playhead_secs < self._stamp_left:
dstamp = self._stamp_left - playhead_secs + \
(self._stamp_right - self._stamp_left) * 0.75
if dstamp > self._stamp_left - self._start_stamp.to_sec():
dstamp = self._stamp_left - self._start_stamp.to_sec()
self.translate_timeline(-dstamp)
# Update the playhead positions
for topic in self.topics:
bag, entry = self.scene().get_entry(self._playhead, topic)
if entry:
if topic in self.scene()._playhead_positions and \
self.scene()._playhead_positions[topic] == (bag, entry.position):
continue
new_playhead_position = (bag, entry.position)
else:
new_playhead_position = (None, None)
with self.scene()._playhead_positions_cvs[topic]:
self.scene()._playhead_positions[topic] = new_playhead_position
# notify all message loaders that a new message needs to be loaded
self.scene()._playhead_positions_cvs[topic].notify_all()
self.scene().update()
self.scene().status_bar_changed_signal.emit()
playhead = property(_get_playhead, _set_playhead)
# TODO add more api variables here to allow plugin access
@property
def _history_right(self):
return self._history_left + self._history_width
@property
def has_selected_region(self):
return self._selected_left is not None and self._selected_right is not None
@property
def play_region(self):
if self.has_selected_region:
return (
rospy.Time.from_sec(self._selected_left), rospy.Time.from_sec(self._selected_right))
else:
return (self._start_stamp, self._end_stamp)
def emit_play_region(self):
play_region = self.play_region
if(play_region[0] is not None and play_region[1] is not None):
self.scene().selected_region_changed.emit(*play_region)
@property
def start_stamp(self):
return self._start_stamp
@property
def end_stamp(self):
return self._end_stamp
# QGraphicsItem implementation
def boundingRect(self):
return QRectF(
0, 0,
self._history_left + self._history_width + self._margin_right,
self._history_bottom + self._margin_bottom)
def paint(self, painter, option, widget):
if self._start_stamp is None:
return
self._layout()
self._draw_topic_dividers(painter)
self._draw_selected_region(painter)
self._draw_time_divisions(painter)
self._draw_topic_histories(painter)
self._draw_bag_ends(painter)
self._draw_topic_names(painter)
self._draw_history_border(painter)
self._draw_playhead(painter)
# END QGraphicsItem implementation
# Drawing Functions
def _qfont_width(self, name):
return QFontMetrics(self._topic_font).width(name)
def _trimmed_topic_name(self, topic_name):
"""
This function trims the topic name down to a reasonable percentage of the viewable scene
area
"""
allowed_width = self._scene_width * (self._topic_name_max_percent / 100.0)
allowed_width = allowed_width - self._topic_name_spacing - self._margin_left
trimmed_return = topic_name
if allowed_width < self._qfont_width(topic_name):
# We need to trim the topic
trimmed = ''
split_name = topic_name.split('/')
split_name = list(filter(lambda a: a != '', split_name))
# Save important last element of topic name provided it is small
popped_last = False
if self._qfont_width(split_name[-1]) < .5 * allowed_width:
popped_last = True
last_item = split_name[-1]
split_name = split_name[:-1]
allowed_width = allowed_width - self._qfont_width(last_item)
# Shorten and add remaining items keeping lengths roughly equal
for item in split_name:
if self._qfont_width(item) > allowed_width / float(len(split_name)):
trimmed_item = item[:-3] + '..'
while self._qfont_width(trimmed_item) > allowed_width / float(len(split_name)):
if len(trimmed_item) >= 3:
trimmed_item = trimmed_item[:-3] + '..'
else:
break
trimmed = trimmed + '/' + trimmed_item
else:
trimmed = trimmed + '/' + item
if popped_last:
trimmed = trimmed + '/' + last_item
trimmed = trimmed[1:]
trimmed_return = trimmed
return trimmed_return
def _layout(self):
"""
Recalculates the layout of the timeline to take into account any changes that have
occurred
"""
# Calculate history left and history width
self._scene_width = self.scene().views()[0].size().width()
max_topic_name_width = -1
for topic in self.topics:
topic_width = self._qfont_width(self._trimmed_topic_name(topic))
if max_topic_name_width <= topic_width:
max_topic_name_width = topic_width
# Calculate font height for each topic
self._topic_font_height = -1
for topic in self.topics:
topic_height = QFontMetrics(self._topic_font).height()
if self._topic_font_height <= topic_height:
self._topic_font_height = topic_height
# Update the timeline boundaries
new_history_left = self._margin_left + max_topic_name_width + self._topic_name_spacing
new_history_width = self._scene_width - new_history_left - self._margin_right
self._history_left = new_history_left
self._history_width = new_history_width
# Calculate the bounds for each topic
self._history_bounds = {}
y = self._history_top
for topic in self.topics:
datatype = self.scene().get_datatype(topic)
topic_height = None
if topic in self._rendered_topics:
renderer = self._timeline_renderers.get(datatype)
if renderer:
topic_height = renderer.get_segment_height(topic)
if not topic_height:
topic_height = self._topic_font_height + self._topic_vertical_padding
self._history_bounds[topic] = (self._history_left, y, self._history_width, topic_height)
y += topic_height
# new_history_bottom = max([y + h for (x, y, w, h) in self._history_bounds.values()]) - 1
new_history_bottom = max([y + h for (_, y, _, h) in self._history_bounds.values()]) - 1
if new_history_bottom != self._history_bottom:
self._history_bottom = new_history_bottom
def _draw_topic_histories(self, painter):
"""
Draw all topic messages
:param painter: allows access to paint functions,''QPainter''
"""
f