diff --git a/CMakeLists.txt b/CMakeLists.txt index d7a7839..49c9831 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,25 @@ -cmake_minimum_required(VERSION 2.8.3) +cmake_minimum_required(VERSION 3.5) project(rqt_console) -# Load catkin and all dependencies required for this package -find_package(catkin REQUIRED) -catkin_package() -catkin_python_setup() + +# Load ament and all dependencies required for this package +find_package(ament_cmake REQUIRED) +find_package(ament_cmake_python REQUIRED) +# Note: This is needed for rcl_interfaces Log.msg +find_package(rcl_interfaces REQUIRED) + +ament_python_install_package(${PROJECT_NAME} + PACKAGE_DIR src/${PROJECT_NAME}) install(FILES plugin.xml - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + DESTINATION share/${PROJECT_NAME} ) install(DIRECTORY resource - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + DESTINATION share/${PROJECT_NAME} ) install(PROGRAMS scripts/rqt_console - DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + DESTINATION lib/${PROJECT_NAME} ) + +ament_package() \ No newline at end of file diff --git a/package.xml b/package.xml index ea1595c..8fffd5b 100644 --- a/package.xml +++ b/package.xml @@ -1,4 +1,4 @@ - + rqt_console 0.4.8 rqt_console provides a GUI plugin for displaying and filtering ROS messages. @@ -12,19 +12,24 @@ https://github.com/ros-visualization/rqt_console https://github.com/ros-visualization/rqt_console/issues - catkin + ament_cmake - python-rospkg - python_qt_binding - roslib - rospy - rqt_gui - rqt_gui_py - rqt_logger_level - rqt_py_common + ament_index_python + python_qt_binding + rclpy + rqt_gui + rqt_gui_py + rqt_py_common + + + + + + rcl_interfaces - + + ament_cmake diff --git a/scripts/rqt_console b/scripts/rqt_console index 47ce376..a85f219 100755 --- a/scripts/rqt_console +++ b/scripts/rqt_console @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/setup.py b/setup.py deleted file mode 100644 index c9f1953..0000000 --- a/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python - -from distutils.core import setup -from catkin_pkg.python_setup import generate_distutils_setup - -d = generate_distutils_setup( - packages=['rqt_console', 'rqt_console.filters'], - package_dir={'': 'src'}, - scripts=['scripts/rqt_console'] -) - -setup(**d) diff --git a/src/rqt_console/console.py b/src/rqt_console/console.py index b35926c..6b3cd36 100644 --- a/src/rqt_console/console.py +++ b/src/rqt_console/console.py @@ -30,19 +30,19 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from rosgraph_msgs.msg import Log -import rospkg -import rospy +from rcl_interfaces.msg import Log + +import rclpy from python_qt_binding.QtCore import QMutex, QMutexLocker, QTimer from qt_gui.plugin import Plugin -from .console_settings_dialog import ConsoleSettingsDialog -from .console_widget import ConsoleWidget -from .message import Message -from .message_data_model import MessageDataModel -from .message_proxy_model import MessageProxyModel +from rqt_console.console_settings_dialog import ConsoleSettingsDialog +from rqt_console.console_widget import ConsoleWidget +from rqt_console.message import Message +from rqt_console.message_data_model import MessageDataModel +from rqt_console.message_proxy_model import MessageProxyModel class Console(Plugin): @@ -60,13 +60,12 @@ def __init__(self, context): super(Console, self).__init__(context) self.setObjectName('Console') - self._rospack = rospkg.RosPack() - + self._context = context self._model = MessageDataModel() self._proxy_model = MessageProxyModel() self._proxy_model.setSourceModel(self._model) - self._widget = ConsoleWidget(self._proxy_model, self._rospack) + self._widget = ConsoleWidget(self._proxy_model) if context.serial_number() > 1: self._widget.setWindowTitle( self._widget.windowTitle() + (' (%d)' % context.serial_number())) @@ -100,8 +99,7 @@ def convert_rosgraph_log_message(log_msg): msg.message = log_msg.msg msg.severity = log_msg.level msg.node = log_msg.name - msg.stamp = (log_msg.header.stamp.secs, log_msg.header.stamp.nsecs) - msg.topics = sorted(log_msg.topics) + msg.stamp = (log_msg.header.stamp.sec, log_msg.header.stamp.nanosec) msg.location = log_msg.file + ':' + log_msg.function + ':' + str(log_msg.line) return msg @@ -116,7 +114,7 @@ def insert_messages(self): self._model.insert_rows(msgs) def shutdown_plugin(self): - self._subscriber.unregister() + self._context.node.destroy_subscription(self._subscriber) self._timer.stop() self._widget.cleanup_browsers_on_close() @@ -127,9 +125,13 @@ def restore_settings(self, plugin_settings, instance_settings): self._widget.restore_settings(plugin_settings, instance_settings) def trigger_configuration(self): - topics = [t for t in rospy.get_published_topics() if t[1] == 'rosgraph_msgs/Log'] + topics = [ + topic_name for topic_name, topic_types + in self._context.node.get_topic_names_and_types() + if 'rcl_interfaces/Log' in topic_types] + topics.sort(key=lambda tup: tup[0]) - dialog = ConsoleSettingsDialog(topics, self._rospack) + dialog = ConsoleSettingsDialog(topics) (topic, message_limit) = dialog.query(self._topic, self._model.get_message_limit()) if topic != self._topic: self._subscribe(topic) @@ -138,6 +140,6 @@ def trigger_configuration(self): def _subscribe(self, topic): if self._subscriber: - self._subscriber.unregister() - self._subscriber = rospy.Subscriber(topic, Log, self.queue_message) + self._context.node.destroy_subscription(self._subscriber) + self._subscriber = self._node.create_subscription(Log, topic, self.queue_message) self._currenttopic = topic diff --git a/src/rqt_console/console_settings_dialog.py b/src/rqt_console/console_settings_dialog.py index d93e671..c38b521 100644 --- a/src/rqt_console/console_settings_dialog.py +++ b/src/rqt_console/console_settings_dialog.py @@ -32,11 +32,14 @@ import os +from ament_index_python.resources import get_resource + from python_qt_binding import loadUi from python_qt_binding.QtWidgets import QDialog -from rqt_logger_level.logger_level_widget import LoggerLevelWidget -from rqt_logger_level.logger_level_service_caller import LoggerLevelServiceCaller +# TODO(mlautman): Restore this functionality once rqt_logger_level has been ported +# from rqt_logger_level.logger_level_widget import LoggerLevelWidget +# from rqt_logger_level.logger_level_service_caller import LoggerLevelServiceCaller class ConsoleSettingsDialog(QDialog): @@ -45,21 +48,25 @@ class ConsoleSettingsDialog(QDialog): Dialog to change the subscribed topic and alter the message buffer size. """ - def __init__(self, topics, rospack): + def __init__(self, topics): """ :param topics: list of topics to allow switching, ''list of string'' :param limit: displayed in the message buffer size spin box, ''int'' """ super(ConsoleSettingsDialog, self).__init__() + + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rospack.get_path('rqt_console'), 'resource', 'console_settings_dialog.ui') + package_path, 'share', pkg_name, 'resource', 'console_settings_dialog.ui') + loadUi(ui_file, self) for topic in topics: self.topic_combo.addItem(topic[0] + ' (' + topic[1] + ')', topic[0]) - self._service_caller = LoggerLevelServiceCaller() - self._logger_widget = LoggerLevelWidget(self._service_caller) - self.levelsLayout.addWidget(self._logger_widget) + # self._service_caller = LoggerLevelServiceCaller() + # self._logger_widget = LoggerLevelWidget(self._service_caller) + # self.levelsLayout.addWidget(self._logger_widget) self.adjustSize() def query(self, topic, buffer_size): diff --git a/src/rqt_console/console_widget.py b/src/rqt_console/console_widget.py index 68d6ac6..c0c0bd7 100644 --- a/src/rqt_console/console_widget.py +++ b/src/rqt_console/console_widget.py @@ -32,6 +32,8 @@ import os +from ament_index_python.resources import get_resource + from python_qt_binding import loadUi from python_qt_binding.QtGui import QCursor, QIcon from python_qt_binding.QtWidgets import (QApplication, QFileDialog, QHeaderView, @@ -69,7 +71,7 @@ class ConsoleWidget(QWidget): Primary widget for the rqt_console plugin. """ - def __init__(self, proxy_model, rospack, minimal=False): + def __init__(self, proxy_model, minimal=False): """ :param proxymodel: the proxy model to display in the widget,''QSortFilterProxyModel'' :param minimal: if true the load, save and column buttons will be hidden as well as the @@ -79,14 +81,16 @@ def __init__(self, proxy_model, rospack, minimal=False): self._proxy_model = proxy_model self._model = self._proxy_model.sourceModel() self._paused = False - self._rospack = rospack # These are lists of Tuples = (,) self._exclude_filters = [] self._highlight_filters = [] + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - self._rospack.get_path('rqt_console'), 'resource', 'console_widget.ui') + package_path, 'share', pkg_name, 'resource', 'console_widget.ui') + loadUi(ui_file, self) if minimal: @@ -327,9 +331,9 @@ def _add_highlight_filter(self, filter_index=False): newfilter = self.filter_factory[filter_index][1]() if len(self.filter_factory[filter_index]) >= 4: newwidget = self.filter_factory[filter_index][2]( - newfilter, self._rospack, self.filter_factory[filter_index][3]) + newfilter, self.filter_factory[filter_index][3]) else: - newwidget = self.filter_factory[filter_index][2](newfilter, self._rospack) + newwidget = self.filter_factory[filter_index][2](newfilter) # pack the new filter tuple onto the filter list self._highlight_filters.append(( @@ -384,9 +388,9 @@ def _add_exclude_filter(self, filter_index=False): newfilter = self.filter_factory[filter_index][1]() if len(self.filter_factory[filter_index]) >= 4: newwidget = self.filter_factory[filter_index][2]( - newfilter, self._rospack, self.filter_factory[filter_index][3]) + newfilter, self.filter_factory[filter_index][3]) else: - newwidget = self.filter_factory[filter_index][2](newfilter, self._rospack) + newwidget = self.filter_factory[filter_index][2](newfilter) # pack the new filter tuple onto the filter list self._exclude_filters.append(( @@ -585,7 +589,7 @@ def _show_browsers(self): rowlist.append(self._proxy_model.mapToSource(current).row()) browsetext = self._model.get_selected_text(rowlist) if browsetext is not None: - self._browsers.append(TextBrowseDialog(browsetext, self._rospack)) + self._browsers.append(TextBrowseDialog(browsetext)) self._browsers[-1].show() def _handle_clear_button_clicked(self, checked): diff --git a/src/rqt_console/filters/custom_filter_widget.py b/src/rqt_console/filters/custom_filter_widget.py index d0cec83..d6276ab 100644 --- a/src/rqt_console/filters/custom_filter_widget.py +++ b/src/rqt_console/filters/custom_filter_widget.py @@ -31,7 +31,8 @@ # POSSIBILITY OF SUCH DAMAGE. import os -import rospkg + +from ament_index_python.resources import get_resource from python_qt_binding import loadUi from python_qt_binding.QtCore import Qt @@ -43,10 +44,14 @@ class CustomFilterWidget(QWidget): - def __init__(self, parentfilter, rospack, item_providers): + def __init__(self, parentfilter, item_providers): super(CustomFilterWidget, self).__init__() + + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rospack.get_path('rqt_console'), 'resource/filters', 'custom_filter_widget.ui') + package_path, 'share', pkg_name, 'resource', 'filters', 'custom_filter_widget.ui') + loadUi(ui_file, self) self.setObjectName('CustomFilterWidget') self._parentfilter = parentfilter # When data is changed it is stored in the parent filter diff --git a/src/rqt_console/filters/filter_wrapper_widget.py b/src/rqt_console/filters/filter_wrapper_widget.py index f5eac1d..c7e8e05 100644 --- a/src/rqt_console/filters/filter_wrapper_widget.py +++ b/src/rqt_console/filters/filter_wrapper_widget.py @@ -31,7 +31,8 @@ # POSSIBILITY OF SUCH DAMAGE. import os -import rospkg + +from ament_index_python.resources import get_resource from python_qt_binding import loadUi from python_qt_binding.QtGui import QIcon @@ -51,9 +52,12 @@ def __init__(self, widget, filter_name): :param filter_name: the name to be placed on the label ''str'' """ super(FilterWrapperWidget, self).__init__() - rp = rospkg.RosPack() + + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rp.get_path('rqt_console'), 'resource/filters', 'filter_wrapper_widget.ui') + package_path, 'share', pkg_name, 'resource', 'filters', 'filter_wrapper_widget.ui') + loadUi(ui_file, self) self.setObjectName('FilterWrapperWidget') self.delete_button.setIcon(QIcon.fromTheme('list-remove')) diff --git a/src/rqt_console/filters/list_filter_widget.py b/src/rqt_console/filters/list_filter_widget.py index 5af23ca..e1edc91 100644 --- a/src/rqt_console/filters/list_filter_widget.py +++ b/src/rqt_console/filters/list_filter_widget.py @@ -31,7 +31,8 @@ # POSSIBILITY OF SUCH DAMAGE. import os -import rospkg + +from ament_index_python.resources import get_resource from python_qt_binding import loadUi from python_qt_binding.QtCore import Qt @@ -48,15 +49,19 @@ class ListFilterWidget(QWidget): limited dynamic selections """ - def __init__(self, parentfilter, rospack, item_provider): + def __init__(self, parentfilter, item_provider): """ :param parentfilter: The filter object, must implement set_list and contain _list ''QObject'' :param item_provider: a function designed to provide a list or dict """ super(ListFilterWidget, self).__init__() + + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rospack.get_path('rqt_console'), 'resource/filters', 'list_filter_widget.ui') + package_path, 'share', pkg_name, 'resource', 'filters', 'list_filter_widget.ui') + loadUi(ui_file, self) self.setObjectName('ListFilterWidget') # When data is changed we need to store it in the parent filter diff --git a/src/rqt_console/filters/text_filter_widget.py b/src/rqt_console/filters/text_filter_widget.py index dfb5f8b..084a1e5 100644 --- a/src/rqt_console/filters/text_filter_widget.py +++ b/src/rqt_console/filters/text_filter_widget.py @@ -31,7 +31,8 @@ # POSSIBILITY OF SUCH DAMAGE. import os -import rospkg + +from ament_index_python.resources import get_resource from python_qt_binding import loadUi from python_qt_binding.QtWidgets import QWidget @@ -39,14 +40,18 @@ class TextFilterWidget(QWidget): - def __init__(self, parentfilter, rospack): + def __init__(self, parentfilter): """ Widget for displaying interactive data related to text filtering. :param parentfilter: buddy filter were data is stored, ''TimeFilter'' """ super(TextFilterWidget, self).__init__() + + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rospack.get_path('rqt_console'), 'resource/filters', 'text_filter_widget.ui') + package_path, 'share', pkg_name, 'resource', 'filters', 'text_filter_widget.ui') + loadUi(ui_file, self) self.setObjectName('TextFilterWidget') self._parentfilter = parentfilter # When data is changed it is stored in the parent filter diff --git a/src/rqt_console/filters/time_filter_widget.py b/src/rqt_console/filters/time_filter_widget.py index 8f6c529..da7e21f 100644 --- a/src/rqt_console/filters/time_filter_widget.py +++ b/src/rqt_console/filters/time_filter_widget.py @@ -32,7 +32,8 @@ from datetime import datetime import os -import rospkg + +from ament_index_python.resources import get_resource from python_qt_binding import loadUi from python_qt_binding.QtCore import QDateTime @@ -41,7 +42,7 @@ class TimeFilterWidget(QWidget): - def __init__(self, parentfilter, rospack, time_range_provider): + def __init__(self, parentfilter, time_range_provider): """ Widget for displaying interactive data related to time filtering. :param parentfilter: buddy filter were data is stored, ''TimeFilter'' @@ -49,8 +50,11 @@ def __init__(self, parentfilter, rospack, time_range_provider): the min and max time to be displayed, ''list of tuple'' """ super(TimeFilterWidget, self).__init__() + pkg_name = 'rqt_console' + _, package_path = get_resource('packages', pkg_name) ui_file = os.path.join( - rospack.get_path('rqt_console'), 'resource/filters', 'time_filter_widget.ui') + package_path, 'share', pkg_name, 'resource', 'filters', 'time_filter_widget.ui') + loadUi(ui_file, self) self.setObjectName('TimeFilterWidget') self._parentfilter = parentfilter # When data is changed it is stored in the parent filter diff --git a/src/rqt_console/message.py b/src/rqt_console/message.py index 901ccfd..5e8dcfd 100644 --- a/src/rqt_console/message.py +++ b/src/rqt_console/message.py @@ -30,8 +30,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from rosgraph_msgs.msg import Log - from python_qt_binding.QtCore import QCoreApplication, QDateTime, QObject @@ -62,7 +60,6 @@ def __init__(self): self.severity = None self.node = None self.__stamp = (None, None) - self.topics = [] self.location = None self._stamp_compare = None @@ -115,8 +112,8 @@ def _get_stamp_as_qdatetime(self, stamp): def get_stamp_string(self): return self._stamp_string - def set_stamp_format(self, format): - self._stamp_format = format + def set_stamp_format(self, stamp_format): + self._stamp_format = stamp_format if None not in self.__stamp: self.stamp = self.__stamp @@ -124,7 +121,6 @@ def pretty_print(self): text = self.tr('Node: ') + self.node + '\n' text += self.tr('Time: ') + self.get_stamp_string() + '\n' text += self.tr('Severity: ') + Message.SEVERITY_LABELS[self.severity] + '\n' - text += self.tr('Published Topics: ') + ', '.join(self.topics) + '\n' text += '\n' + self.message + '\n' text += '\n' + 'Location:' text += '\n' + self.location + '\n\n' diff --git a/src/rqt_console/text_browse_dialog.py b/src/rqt_console/text_browse_dialog.py index d6fb3a6..dcd591f 100644 --- a/src/rqt_console/text_browse_dialog.py +++ b/src/rqt_console/text_browse_dialog.py @@ -32,21 +32,27 @@ import os +from ament_index_python import get_resource + from python_qt_binding import loadUi from python_qt_binding.QtWidgets import QDialog - +from python_qt_binding.QtGui import QFont class TextBrowseDialog(QDialog): - """ Simple text brower Dialog that sets its text from the passed in text. """ - - def __init__(self, text, rospack): + def __init__(self, text): """ :param text: value to set the text of the widget to, ''str'' """ super(TextBrowseDialog, self).__init__() - ui_file = os.path.join(rospack.get_path('rqt_console'), 'resource', 'text_browse_dialog.ui') + + _, package_path = get_resource('packages', 'rqt_console') + ui_file = os.path.join( + package_path, 'share', 'rqt_console', 'resource', 'text_browse_dialog.ui') + + self.setFont(QFont('Mono')) + loadUi(ui_file, self) self.text_browser.setText(text)