__init__.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2010, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 import math
34 import random
35 
36 import rospy
37 
38 from python_qt_binding.QtCore import pyqtSlot
39 from python_qt_binding.QtCore import Qt
40 from python_qt_binding.QtCore import Signal
41 from python_qt_binding.QtGui import QFont
42 from python_qt_binding.QtWidgets import QApplication
43 from python_qt_binding.QtWidgets import QHBoxLayout
44 from python_qt_binding.QtWidgets import QLabel
45 from python_qt_binding.QtWidgets import QLineEdit
46 from python_qt_binding.QtWidgets import QPushButton
47 from python_qt_binding.QtWidgets import QSlider
48 from python_qt_binding.QtWidgets import QVBoxLayout
49 from python_qt_binding.QtWidgets import QGridLayout
50 from python_qt_binding.QtWidgets import QScrollArea
51 from python_qt_binding.QtWidgets import QSpinBox
52 from python_qt_binding.QtWidgets import QWidget
53 
54 RANGE = 10000
55 
56 
57 class JointStatePublisherGui(QWidget):
58  sliderUpdateTrigger = Signal()
59 
60  def __init__(self, title, jsp, num_rows=0):
61  super(JointStatePublisherGui, self).__init__()
62  self.jsp = jsp
63  self.joint_map = {}
64  self.vlayout = QVBoxLayout(self)
65  self.scrollable = QWidget()
66  self.gridlayout = QGridLayout()
67  self.scroll = QScrollArea()
68  self.scroll.setWidgetResizable(True)
69 
70  self.jsp.set_source_update_cb(self.source_update_cb)
71 
72  font = QFont("Helvetica", 9, QFont.Bold)
73 
74  ### Generate sliders ###
75  sliders = []
76  for name in self.jsp.joint_list:
77  if name not in self.jsp.free_joints:
78  continue
79  joint = self.jsp.free_joints[name]
80 
81  if joint['min'] == joint['max']:
82  continue
83 
84  joint_layout = QVBoxLayout()
85  row_layout = QHBoxLayout()
86 
87  label = QLabel(name)
88  label.setFont(font)
89  row_layout.addWidget(label)
90  display = QLineEdit("0.00")
91  display.setAlignment(Qt.AlignRight)
92  display.setFont(font)
93  display.setReadOnly(True)
94  row_layout.addWidget(display)
95 
96  joint_layout.addLayout(row_layout)
97 
98  slider = QSlider(Qt.Horizontal)
99 
100  slider.setFont(font)
101  slider.setRange(0, RANGE)
102  slider.setValue(RANGE/2)
103 
104  joint_layout.addWidget(slider)
105 
106  self.joint_map[name] = {'slidervalue': 0, 'display': display,
107  'slider': slider, 'joint': joint}
108  # Connect to the signal provided by QSignal
109  slider.valueChanged.connect(lambda event,name=name: self.onValueChangedOne(name))
110 
111  sliders.append(joint_layout)
112 
113  # Determine number of rows to be used in grid
114  self.num_rows = num_rows
115  # if desired num of rows wasn't set, default behaviour is a vertical layout
116  if self.num_rows == 0:
117  self.num_rows = len(sliders) # equals VBoxLayout
118  # Generate positions in grid and place sliders there
119  self.positions = self.generate_grid_positions(len(sliders), self.num_rows)
120  for item, pos in zip(sliders, self.positions):
121  self.gridlayout.addLayout(item, *pos)
122 
123  # Set zero positions read from parameters
124  self.center()
125 
126  # Synchronize slider and displayed value
127  self.sliderUpdate(None)
128 
129  # Set up a signal for updating the sliders based on external joint info
130  self.sliderUpdateTrigger.connect(self.updateSliders)
131 
132  self.scrollable.setLayout(self.gridlayout)
133  self.scroll.setWidget(self.scrollable)
134  self.vlayout.addWidget(self.scroll)
135 
136  # Buttons for randomizing and centering sliders and
137  # Spinbox for on-the-fly selecting number of rows
138  self.randbutton = QPushButton('Randomize', self)
139  self.randbutton.clicked.connect(self.randomize_event)
140  self.vlayout.addWidget(self.randbutton)
141  self.ctrbutton = QPushButton('Center', self)
142  self.ctrbutton.clicked.connect(self.center_event)
143  self.vlayout.addWidget(self.ctrbutton)
144  self.maxrowsupdown = QSpinBox()
145  self.maxrowsupdown.setMinimum(1)
146  self.maxrowsupdown.setMaximum(len(sliders))
147  self.maxrowsupdown.setValue(self.num_rows)
148  self.maxrowsupdown.valueChanged.connect(self.reorggrid_event)
149  self.vlayout.addWidget(self.maxrowsupdown)
150  self.setLayout(self.vlayout)
151 
152  def source_update_cb(self):
153  self.sliderUpdateTrigger.emit()
154 
155  def onValueChangedOne(self, name):
156  # A slider value was changed, but we need to change the joint_info metadata.
157  joint_info = self.joint_map[name]
158  joint_info['slidervalue'] = joint_info['slider'].value()
159  joint = joint_info['joint']
160  joint['position'] = self.sliderToValue(joint_info['slidervalue'], joint)
161  joint_info['display'].setText("%.2f" % joint['position'])
162 
163  @pyqtSlot()
164  def updateSliders(self):
165  self.update_sliders()
166 
167  def update_sliders(self):
168  for name, joint_info in self.joint_map.items():
169  joint = joint_info['joint']
170  joint_info['slidervalue'] = self.valueToSlider(joint['position'],
171  joint)
172  joint_info['slider'].setValue(joint_info['slidervalue'])
173 
174  def center_event(self, event):
175  self.center()
176 
177  def center(self):
178  rospy.loginfo("Centering")
179  for name, joint_info in self.joint_map.items():
180  joint = joint_info['joint']
181  joint_info['slider'].setValue(self.valueToSlider(joint['zero'], joint))
182 
183  def reorggrid_event(self, event):
184  self.reorganize_grid(event)
185 
186  def reorganize_grid(self, number_of_rows):
187  self.num_rows = number_of_rows
188 
189  # Remove items from layout (won't destroy them!)
190  items = []
191  for pos in self.positions:
192  item = self.gridlayout.itemAtPosition(*pos)
193  items.append(item)
194  self.gridlayout.removeItem(item)
195 
196  # Generate new positions for sliders and place them in their new spots
197  self.positions = self.generate_grid_positions(len(items), self.num_rows)
198  for item, pos in zip(items, self.positions):
199  self.gridlayout.addLayout(item, *pos)
200 
201  def generate_grid_positions(self, num_items, num_rows):
202  if num_rows == 0:
203  return []
204  positions = [(y, x) for x in range(int((math.ceil(float(num_items) / num_rows)))) for y in range(num_rows)]
205  positions = positions[:num_items]
206  return positions
207 
208  def randomize_event(self, event):
209  self.randomize()
210 
211  def randomize(self):
212  rospy.loginfo("Randomizing")
213  for name, joint_info in self.joint_map.items():
214  joint = joint_info['joint']
215  joint_info['slider'].setValue(
216  self.valueToSlider(random.uniform(joint['min'], joint['max']), joint))
217 
218  def sliderUpdate(self, event):
219  for name, joint_info in self.joint_map.items():
220  joint_info['slidervalue'] = joint_info['slider'].value()
221  self.update_sliders()
222 
223  def valueToSlider(self, value, joint):
224  return (value - joint['min']) * float(RANGE) / (joint['max'] - joint['min'])
225 
226  def sliderToValue(self, slider, joint):
227  pctvalue = slider / float(RANGE)
228  return joint['min'] + (joint['max']-joint['min']) * pctvalue
def generate_grid_positions(self, num_items, num_rows)
Definition: __init__.py:201
def reorganize_grid(self, number_of_rows)
Definition: __init__.py:186
def __init__(self, title, jsp, num_rows=0)
Definition: __init__.py:60


joint_state_publisher_gui
Author(s): David V. Lu!! , Jackie Kay
autogenerated on Sun Dec 13 2020 03:38:39