From dc8c4285c4a3ad523c9c6c3530401366afb2a1b6 Mon Sep 17 00:00:00 2001 From: Yohei Kakiuchi Date: Tue, 20 Apr 2021 12:38:23 +0900 Subject: [PATCH 1/2] add node name to message from buttun's sample scripts --- jsk_rqt_plugins/sample_scripts/sample_service_buttons.py | 7 ++++--- .../sample_scripts/sample_service_radio_buttons.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/jsk_rqt_plugins/sample_scripts/sample_service_buttons.py b/jsk_rqt_plugins/sample_scripts/sample_service_buttons.py index 34a23a9b7..051f5a04a 100755 --- a/jsk_rqt_plugins/sample_scripts/sample_service_buttons.py +++ b/jsk_rqt_plugins/sample_scripts/sample_service_buttons.py @@ -20,17 +20,18 @@ def __init__(self): rospy.Service('dummy/buttonE', Empty, self._empty_cb), rospy.Service('dummy/buttonF', Empty, self._empty_cb), ] + self._name = rospy.get_name() def _set_bool_cb(self, req): - rospy.loginfo('SetBool service called: req.data={}'.format(req.data)) + rospy.loginfo('{} | SetBool service called: req.data={}'.format(self._name,req.data)) return SetBoolResponse(success=True) def _trigger_cb(self, req): - rospy.loginfo('Trigger service called') + rospy.loginfo('{} | Trigger service called'.format(self._name)) return TriggerResponse(success=True) def _empty_cb(self, req): - rospy.loginfo('Empty service called') + rospy.loginfo('{} | Empty service called'.format(self._name)) return EmptyResponse() diff --git a/jsk_rqt_plugins/sample_scripts/sample_service_radio_buttons.py b/jsk_rqt_plugins/sample_scripts/sample_service_radio_buttons.py index c5ed5c9bd..5d4d87e8c 100755 --- a/jsk_rqt_plugins/sample_scripts/sample_service_radio_buttons.py +++ b/jsk_rqt_plugins/sample_scripts/sample_service_radio_buttons.py @@ -16,9 +16,10 @@ def __init__(self): rospy.Service('dummy/buttonE', Empty, self._empty_cb), rospy.Service('dummy/buttonF', Empty, self._empty_cb), ] + self._name = rospy.get_name() def _empty_cb(self, req): - rospy.loginfo('Empty service called') + rospy.loginfo('{} | Empty service called'.format(self._name)) return EmptyResponse() From cc4b9ab82a686f4219d37330ccbbefb367646d2c Mon Sep 17 00:00:00 2001 From: Yohei Kakiuchi Date: Tue, 20 Apr 2021 12:43:06 +0900 Subject: [PATCH 2/2] add tabbed_buttuns to rqt_plugin --- jsk_rqt_plugins/bin/rqt_tabbed_buttons | 13 ++ jsk_rqt_plugins/plugin.xml | 13 ++ .../sample/sample_tabbed_buttons.launch | 23 +++ .../src/jsk_rqt_plugins/button_general.py | 26 ++- .../src/jsk_rqt_plugins/tabbed_button.py | 25 +++ .../jsk_rqt_plugins/tabbed_button_general.py | 167 ++++++++++++++++++ 6 files changed, 262 insertions(+), 5 deletions(-) create mode 100755 jsk_rqt_plugins/bin/rqt_tabbed_buttons create mode 100644 jsk_rqt_plugins/sample/sample_tabbed_buttons.launch create mode 100644 jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button.py create mode 100644 jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button_general.py diff --git a/jsk_rqt_plugins/bin/rqt_tabbed_buttons b/jsk_rqt_plugins/bin/rqt_tabbed_buttons new file mode 100755 index 000000000..2c030766c --- /dev/null +++ b/jsk_rqt_plugins/bin/rqt_tabbed_buttons @@ -0,0 +1,13 @@ +#!/usr/bin/env python +import sys + +from rqt_gui.main import Main +try: + from jsk_rqt_plugins.tabbed_button import ServiceTabbedButtons +except: + import roslib; roslib.load_manifest('jsk_rqt_plugins') + from jsk_rqt_plugins.tabbed_button import ServiceTabbedButtons + +plugin = 'ServiceTabbedButtons' +main = Main(filename=plugin) +sys.exit(main.main(standalone=plugin)) diff --git a/jsk_rqt_plugins/plugin.xml b/jsk_rqt_plugins/plugin.xml index 07c3492e1..0164c8c8e 100644 --- a/jsk_rqt_plugins/plugin.xml +++ b/jsk_rqt_plugins/plugin.xml @@ -136,4 +136,17 @@ A plugin to wait for user input. + + + + + folder + Plugins related to visualization. + + + utilities-system-monitor + + diff --git a/jsk_rqt_plugins/sample/sample_tabbed_buttons.launch b/jsk_rqt_plugins/sample/sample_tabbed_buttons.launch new file mode 100644 index 000000000..1b3868f75 --- /dev/null +++ b/jsk_rqt_plugins/sample/sample_tabbed_buttons.launch @@ -0,0 +1,23 @@ + + + + tabbed_layout: + tab_list: ['push', 'radio'] + push: + name: 'push button' + namespace: push + type: push + yaml_file: 'package://jsk_rqt_plugins/resource/service_button_layout.yaml' + radio: + name: 'radio button' + namespace: radio + type: radio + yaml_file: 'package://jsk_rqt_plugins/resource/service_radio_button_layout.yaml' + + + + + diff --git a/jsk_rqt_plugins/src/jsk_rqt_plugins/button_general.py b/jsk_rqt_plugins/src/jsk_rqt_plugins/button_general.py index d4b3f8d5f..f65293a72 100644 --- a/jsk_rqt_plugins/src/jsk_rqt_plugins/button_general.py +++ b/jsk_rqt_plugins/src/jsk_rqt_plugins/button_general.py @@ -127,7 +127,15 @@ def loadLayoutYaml(self, layout_param): def setupButtons(self, yaml_file): """ - Parse yaml file and setup Buttons. Format of the yaml file should be: + Parse yaml file and setup Buttons. + """ + with open(yaml_file) as f: + yaml_data = yaml.load(f) + self.setupButtons_with_yaml_data(yaml_data) + + def setupButtons_with_yaml_data(self, yaml_data, namespace=None): + """ + Setup Buttons with yaml_data which is loaded in setupButtons. Format of the yaml file should be: - name: 'button name' (required) image: 'path to image for icon' (optional) image_size: 'width and height of icon' (optional) @@ -135,8 +143,7 @@ def setupButtons(self, yaml_file): column: 'column index' (optional, defaults to 0) """ self.buttons = [] - with open(yaml_file) as f: - yaml_data = yaml.load(f) + if True: # lookup colum direction direction = 'vertical' for d in yaml_data: @@ -206,8 +213,17 @@ def setupButtons(self, yaml_file): service_type = Empty if service_type == SetBool: button.setCheckable(True) - button.clicked.connect( - self.buttonCallback(button_data['service'], service_type, button)) + if namespace: + sname = button_data['service'] + if sname[0] == '/' or sname[0] == '~': + sname = sname + else: + sname = namespace + '/' + sname + button.clicked.connect( + self.buttonCallback(sname, service_type, button)) + else: + button.clicked.connect( + self.buttonCallback(button_data['service'], service_type, button)) if self.button_type == "push": button.setToolButtonStyle( QtCore.Qt.ToolButtonTextUnderIcon) diff --git a/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button.py b/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button.py new file mode 100644 index 000000000..ed1f0a168 --- /dev/null +++ b/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button.py @@ -0,0 +1,25 @@ +from rqt_gui_py.plugin import Plugin +from jsk_rqt_plugins.tabbed_button_general import ServiceTabbedButtonGeneralWidget + +class ServiceTabbedButtons(Plugin): + """ + rqt class to provide multiple buttons + """ + def __init__(self, context): + super(ServiceTabbedButtons, self).__init__(context) + self.setObjectName("ServiceTabbedButtons") + self._widget = ServiceTabbedButtonWidget() + context.add_widget(self._widget) + def save_settings(self, plugin_settings, instance_settings): + self._widget.save_settings(plugin_settings, instance_settings) + def restore_settings(self, plugin_settings, instance_settings): + self._widget.restore_settings(plugin_settings, instance_settings) + def trigger_configuration(self): + self._widget.trigger_configuration() + +class ServiceTabbedButtonWidget(ServiceTabbedButtonGeneralWidget): + """ + Qt widget to visualize multiple buttons + """ + def __init__(self): + super(ServiceTabbedButtonWidget, self).__init__() diff --git a/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button_general.py b/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button_general.py new file mode 100644 index 000000000..0bafbd374 --- /dev/null +++ b/jsk_rqt_plugins/src/jsk_rqt_plugins/tabbed_button_general.py @@ -0,0 +1,167 @@ +from distutils.version import LooseVersion +import math +import os +import sys + +import python_qt_binding +import python_qt_binding.QtCore as QtCore +from python_qt_binding.QtCore import QEvent +from python_qt_binding.QtCore import QSize +from python_qt_binding.QtCore import Qt +from python_qt_binding.QtCore import QTimer +from python_qt_binding.QtCore import qWarning +from python_qt_binding.QtCore import Slot +import python_qt_binding.QtGui as QtGui +from python_qt_binding.QtGui import QBrush +from python_qt_binding.QtGui import QColor +from python_qt_binding.QtGui import QFont +from python_qt_binding.QtGui import QIcon +from python_qt_binding.QtGui import QPainter +from python_qt_binding.QtGui import QPen +import yaml + +from resource_retriever import get_filename +import rospy +from rqt_gui_py.plugin import Plugin +from std_msgs.msg import Bool +from std_msgs.msg import Time +from std_srvs.srv import Empty +from std_srvs.srv import SetBool +from std_srvs.srv import Trigger + +if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5: + from python_qt_binding.QtWidgets import QAction + from python_qt_binding.QtWidgets import QComboBox + from python_qt_binding.QtWidgets import QCompleter + from python_qt_binding.QtWidgets import QDialog + from python_qt_binding.QtWidgets import QGroupBox + from python_qt_binding.QtWidgets import QHBoxLayout + from python_qt_binding.QtWidgets import QLineEdit + from python_qt_binding.QtWidgets import QMenu + from python_qt_binding.QtWidgets import QMessageBox + from python_qt_binding.QtWidgets import QPushButton + from python_qt_binding.QtWidgets import QRadioButton + from python_qt_binding.QtWidgets import QSizePolicy + from python_qt_binding.QtWidgets import QToolButton + from python_qt_binding.QtWidgets import QVBoxLayout + from python_qt_binding.QtWidgets import QWidget + from python_qt_binding.QtWidgets import QTabWidget +else: + from python_qt_binding.QtGui import QAction + from python_qt_binding.QtGui import QComboBox + from python_qt_binding.QtGui import QCompleter + from python_qt_binding.QtGui import QDialog + from python_qt_binding.QtGui import QGroupBox + from python_qt_binding.QtGui import QHBoxLayout + from python_qt_binding.QtGui import QLineEdit + from python_qt_binding.QtGui import QMenu + from python_qt_binding.QtGui import QMessageBox + from python_qt_binding.QtGui import QPushButton + from python_qt_binding.QtGui import QRadioButton + from python_qt_binding.QtGui import QSizePolicy + from python_qt_binding.QtGui import QToolButton + from python_qt_binding.QtGui import QVBoxLayout + from python_qt_binding.QtGui import QWidget + from python_qt_binding.QtGui import QTabWidget + +from jsk_rqt_plugins.button_general import ServiceButtonGeneralWidget +from jsk_rqt_plugins.button_general import LineEditDialog + +class ServiceTabbedButtonGeneralWidget(QWidget): + def __init__(self): + super(ServiceTabbedButtonGeneralWidget, self).__init__() + self._tab_settings = None + + if rospy.has_param("~tabbed_layout"): + tabbed_layout = rospy.get_param("~tabbed_layout") + self._tab_list = [] + if not 'tab_list' in tabbed_layout: + self.showError("Cannot find tab_list in %s"%(tabbed_layout)) + return + tab_list = tabbed_layout['tab_list'] + for tb in tab_list: + if tb in tabbed_layout: + param_settings = tabbed_layout[tb] + settings = {} + ## + if 'type' in param_settings: + settings['type'] = param_settings['type'] + ## + if not 'name' in param_settings: + settings['name'] = tb + else: + settings['name'] = param_settings['name'] + ## + if 'yaml_file' in param_settings: + settings['yaml_file'] = param_settings['yaml_file'] + else: + self.showError("Cannot find yaml_file in %s"%(tb)) + settings = None + ## + if 'namespace' in param_settings: + settings['namespace'] = param_settings['namespace'] + + if settings: + self._tab_list.append(settings) + else: + self.showError("Cannot find key %s in %s"%(tb, tabbed_layout)) + else: + self.showError("Cannot find rosparam ~tabbed_layout") + return + + if len(self._tab_list) == 0: + self.showError("there is no valid param in ~tabbed_layout") + return + + qtab = QTabWidget() + for tb in self._tab_list: + wg = ServiceButtonGeneralWidget_in_tab(tb) + qtab.addTab(wg, tb['name'] ) + + #self.setWindowTitle('Tab Layout') + hbox = QHBoxLayout() + hbox.addWidget(qtab) + self.setLayout(hbox) + self.show() + + def showError(self, message): + QMessageBox.about(self, "ERROR", message) + + def save_settings(self, plugin_settings, instance_settings): + ## ignore settings + pass + def restore_settings(self, plugin_settings, instance_settings): + ## ignore settings + pass + def trigger_configuration(self): + ## ignore settings + pass + +class ServiceButtonGeneralWidget_in_tab(ServiceButtonGeneralWidget): + """ + Qt widget to visualize multiple buttons + """ + def __init__(self, settings): + super(ServiceButtonGeneralWidget, self).__init__() + yaml_file = settings['yaml_file'] + namespace = None + if 'type' in settings: + self.button_type = settings['type'] + else: + self.button_type = 'push' + + if 'namespace' in settings: + namespace = settings['namespace'] + + self._layout_param = None + self._dialog = LineEditDialog() + + resolved_yaml = get_filename(yaml_file) + if "file://" == resolved_yaml[0:7]: + resolved_yaml = resolved_yaml[len("file://"):] + + with open(resolved_yaml) as f: + yaml_data = yaml.load(f) + self.setupButtons_with_yaml_data(yaml_data=yaml_data, namespace=namespace) + + self.show()