button_general.py
Go to the documentation of this file.
1 from distutils.version import LooseVersion
2 import math
3 import os
4 import sys
5 
6 import python_qt_binding
7 import python_qt_binding.QtCore as QtCore
8 from python_qt_binding.QtCore import QEvent
9 from python_qt_binding.QtCore import QSize
10 from python_qt_binding.QtCore import Qt
11 from python_qt_binding.QtCore import QTimer
12 from python_qt_binding.QtCore import QTranslator
13 from python_qt_binding.QtCore import qWarning
14 from python_qt_binding.QtCore import Slot
15 import python_qt_binding.QtGui as QtGui
16 from python_qt_binding.QtGui import QBrush
17 from python_qt_binding.QtGui import QColor
18 from python_qt_binding.QtGui import QFont
19 from python_qt_binding.QtGui import QIcon
20 from python_qt_binding.QtGui import QPainter
21 from python_qt_binding.QtGui import QPen
22 import yaml
23 
24 from resource_retriever import get_filename
25 import rospy
26 from rqt_gui_py.plugin import Plugin
27 from std_msgs.msg import Bool
28 from std_msgs.msg import Time
29 from std_srvs.srv import Empty
30 from std_srvs.srv import SetBool
31 from std_srvs.srv import Trigger
32 
33 if LooseVersion(python_qt_binding.QT_BINDING_VERSION).version[0] >= 5:
34  from python_qt_binding.QtWidgets import QAction
35  from python_qt_binding.QtWidgets import QCompleter
36  from python_qt_binding.QtWidgets import QFileDialog
37  from python_qt_binding.QtWidgets import QGroupBox
38  from python_qt_binding.QtWidgets import QHBoxLayout
39  from python_qt_binding.QtWidgets import QMenu
40  from python_qt_binding.QtWidgets import QMessageBox
41  from python_qt_binding.QtWidgets import QRadioButton
42  from python_qt_binding.QtWidgets import QSizePolicy
43  from python_qt_binding.QtWidgets import QToolButton
44  from python_qt_binding.QtWidgets import QVBoxLayout
45  from python_qt_binding.QtWidgets import QWidget
46 
47 else:
48  from python_qt_binding.QtGui import QAction
49  from python_qt_binding.QtGui import QCompleter
50  from python_qt_binding.QtGui import QFileDialog
51  from python_qt_binding.QtGui import QGroupBox
52  from python_qt_binding.QtGui import QHBoxLayout
53  from python_qt_binding.QtGui import QMenu
54  from python_qt_binding.QtGui import QMessageBox
55  from python_qt_binding.QtGui import QRadioButton
56  from python_qt_binding.QtGui import QSizePolicy
57  from python_qt_binding.QtGui import QToolButton
58  from python_qt_binding.QtGui import QVBoxLayout
59  from python_qt_binding.QtGui import QWidget
60 
61 
63  """
64  Qt widget to visualize multiple buttons
65  """
66  def __init__(self, button_type="push"):
67  super(ServiceButtonGeneralWidget, self).__init__()
68  self.button_type = button_type
69  self._layout_param = None
70  self._translator = QTranslator()
71  self._dialog = QFileDialog()
72  self._dialog.setFileMode(QFileDialog.ExistingFile)
73 
74  if rospy.has_param("~layout_yaml_file"):
75  self.loadLayoutYaml(None)
76 
77  self.show()
78 
79  def showError(self, message):
80  QMessageBox.about(self, "ERROR", message)
81 
82  def loadLayoutYaml(self, layout_param):
83  # Initialize layout of the buttons from yaml file
84  # The yaml file can be specified by rosparam
85  layout_yaml_file = rospy.get_param("~layout_yaml_file", layout_param)
86  resolved_layout_yaml_file = get_filename(layout_yaml_file)
87  if (resolved_layout_yaml_file is not None
88  and resolved_layout_yaml_file.startswith("file://")):
89  resolved_layout_yaml_file = resolved_layout_yaml_file[len("file://"):]
90  # check file exists
91  if os.path.exists(resolved_layout_yaml_file):
92  self.setupButtons(resolved_layout_yaml_file)
93  self.show()
94  return True
95  else:
96  self.showError("Cannot find %s (%s)" % (
97  layout_yaml_file, resolved_layout_yaml_file))
98  return False
99 
100  def setupButtons(self, yaml_file):
101  """
102  Parse yaml file and setup Buttons.
103  """
104  with open(yaml_file) as f:
105  yaml_data = yaml.safe_load(f)
106  self.setupButtons_with_yaml_data(yaml_data)
107 
108  def setupButtons_with_yaml_data(self, yaml_data, namespace=None):
109  """
110  Setup Buttons with yaml_data which is loaded in setupButtons. Format of the yaml file should be:
111  - name: 'button name' (required)
112  image: 'path to image for icon' (optional)
113  image_size: 'width and height of icon' (optional)
114  service: 'service' (required)
115  column: 'column index' (optional, defaults to 0)
116  """
117  self.buttons = []
118  if True:
119  # lookup colum direction
120  direction = 'vertical'
121  for d in yaml_data:
122  if 'direction' in d:
123  if d['direction'] == 'horizontal':
124  direction = 'horizontal'
125  else: # d['direction'] == 'vertical':
126  direction = 'vertical'
127  yaml_data.remove(d)
128  break
129  # lookup column num
130  column_indices = [d['column'] for d in yaml_data]
131  if len(column_indices) > 1:
132  max_column_index = max(*column_indices)
133  else:
134  max_column_index = column_indices[0]
135  if direction == 'vertical':
136  self.layout = QHBoxLayout()
137  self.layout_boxes = [QVBoxLayout()
138  for i in range(max_column_index + 1)]
139  else: # direction == 'horizontal'
140  self.layout = QVBoxLayout()
141  self.layout_boxes = [QHBoxLayout()
142  for i in range(max_column_index + 1)]
143  self.button_groups = [QGroupBox()
144  for i in range(max_column_index + 1)]
145  for button_data in yaml_data:
146  # check if all the field is available
147  if "name" not in button_data:
148  self.showError("name field is missed in yaml")
149  raise Exception("name field is missed in yaml")
150  if "service" not in button_data:
151  self.showError("service field is missed in yaml")
152  raise Exception("service field is missed in yaml")
153  if self.button_type == "push":
154  button = QToolButton()
155  else: # self.button_type == "radio":
156  button = QRadioButton()
157  button.setSizePolicy(
158  QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
159  if "image" in button_data:
160  image_file = get_filename(
161  button_data["image"])[len("file://"):]
162  if os.path.exists(image_file):
163  icon = QtGui.QIcon(image_file)
164  button.setIcon(icon)
165  if "image_size" in button_data:
166  button.setIconSize(QSize(
167  button_data["image_size"][0],
168  button_data["image_size"][1]))
169  else:
170  button.setIconSize(QSize(100, 100))
171  if "name" in button_data:
172  name = button_data['name']
173  button.setText(name)
174  if 'service_type' in button_data:
175  if button_data['service_type'] == 'Trigger':
176  service_type = Trigger
177  elif button_data['service_type'] == 'Empty':
178  service_type = Empty
179  elif button_data['service_type'] == 'SetBool':
180  service_type = SetBool
181  else:
182  raise Exception("Unsupported service type: {}".format(
183  button_data['service_type']))
184  else:
185  service_type = Empty
186  if service_type == SetBool:
187  button.setCheckable(True)
188  if namespace:
189  sname = button_data['service']
190  if sname[0] == '/' or sname[0] == '~':
191  sname = sname
192  else:
193  sname = namespace + '/' + sname
194  button.clicked.connect(
195  self.buttonCallback(sname, service_type, button))
196  else:
197  button.clicked.connect(
198  self.buttonCallback(button_data['service'], service_type, button))
199  if self.button_type == "push":
200  button.setToolButtonStyle(
201  QtCore.Qt.ToolButtonTextUnderIcon)
202  if ((self.button_type == "radio" or service_type == SetBool)
203  and ("default_value" in button_data
204  and button_data['default_value'])):
205  button.setChecked(True)
206  self.layout_boxes[button_data['column']].addWidget(button)
207  self.buttons.append(button)
208  for i in range(len(self.button_groups)):
209  self.button_groups[i].setLayout(self.layout_boxes[i])
210  for group in self.button_groups:
211  self.layout.addWidget(group)
212  self.setLayout(self.layout)
213 
214  def buttonCallback(self, service_name, service_type, button):
215  """
216  return function as callback
217  """
218  return lambda checked: self.buttonCallbackImpl(checked, service_name, service_type, button)
219 
220  def buttonCallbackImpl(self, checked, service_name, service_type=Empty, button=None):
221  srv = rospy.ServiceProxy(service_name, service_type)
222  try:
223  if service_type == SetBool:
224  res = srv(checked)
225  else:
226  res = srv()
227  if hasattr(res, 'success'):
228  success = res.success
229  if not success:
230  self.showError(
231  "Succeeded to call {}, but service response is res.success=False"
232  .format(service_name))
233  if service_type == SetBool and not button is None:
234  button.setChecked(not checked)
235  except rospy.ServiceException as e:
236  self.showError("Failed to call %s" % service_name)
237  if service_type == SetBool and not button is None:
238  button.setChecked(not checked)
239 
240  def save_settings(self, plugin_settings, instance_settings):
241  if self._layout_param:
242  instance_settings.set_value("layout_param", self._layout_param)
243  rospy.loginfo("save setting is called. %s" % self._layout_param)
244 
245  def restore_settings(self, plugin_settings, instance_settings):
246  if instance_settings.value("layout_param"):
247  self._layout_param = instance_settings.value("layout_param")
248  rospy.loginfo("restore setting is called. %s" % self._layout_param)
249  updated = self.loadLayoutYaml(self._layout_param)
250  if updated:
251  rospy.loginfo("succeeded to restore. %s" % self._layout_param)
252  else:
253  rospy.logerr("failed to restore. %s" % self._layout_param)
254 
256  self._layout_param = self._dialog.getOpenFileName(
257  None, self._translator.tr("Open File"), "",
258  self._translator.tr("YAML files (*.yaml *.yml)"))[0]
259 
260  if self._layout_param:
261  updated = self.loadLayoutYaml(self._layout_param)
262  rospy.loginfo(
263  "trigger configuration is called. %s" % self._layout_param)
264  if updated:
265  rospy.loginfo(
266  "succeeded to configure. %s" % self._layout_param)
267  else:
268  rospy.logerr(
269  "failed to configure. %s" % self._layout_param)
270  self.trigger_configuration()
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.loadLayoutYaml
def loadLayoutYaml(self, layout_param)
Definition: button_general.py:82
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.restore_settings
def restore_settings(self, plugin_settings, instance_settings)
Definition: button_general.py:245
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.buttonCallback
def buttonCallback(self, service_name, service_type, button)
Definition: button_general.py:214
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.setupButtons
def setupButtons(self, yaml_file)
Definition: button_general.py:100
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.setupButtons_with_yaml_data
def setupButtons_with_yaml_data(self, yaml_data, namespace=None)
Definition: button_general.py:108
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.buttons
buttons
Definition: button_general.py:117
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.button_type
button_type
Definition: button_general.py:68
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.layout
layout
Definition: button_general.py:136
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.trigger_configuration
def trigger_configuration(self)
Definition: button_general.py:255
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget._translator
_translator
Definition: button_general.py:70
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget
Definition: button_general.py:62
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.button_groups
button_groups
Definition: button_general.py:143
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.__init__
def __init__(self, button_type="push")
Definition: button_general.py:66
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.buttonCallbackImpl
def buttonCallbackImpl(self, checked, service_name, service_type=Empty, button=None)
Definition: button_general.py:220
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget._layout_param
_layout_param
Definition: button_general.py:69
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget._dialog
_dialog
Definition: button_general.py:71
rqt_gui_py::plugin
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.showError
def showError(self, message)
Definition: button_general.py:79
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.save_settings
def save_settings(self, plugin_settings, instance_settings)
Definition: button_general.py:240
jsk_rqt_plugins.button_general.ServiceButtonGeneralWidget.layout_boxes
layout_boxes
Definition: button_general.py:137


jsk_rqt_plugins
Author(s):
autogenerated on Mon Apr 7 2025 02:49:46