cyberglove_calibrator.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright 2011 Shadow Robot Company Ltd.
4 #
5 # This program is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation version 2 of the License.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 # more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 
18 # import roslib; roslib.load_manifest('sr_control_gui')
19 import os
20 import rospy
21 import rospkg
22 
23 import traceback
24 import sys
25 
26 from qt_gui.plugin import Plugin
27 from python_qt_binding import loadUi
28 import QtCore
29 from QtCore import Qt, QEvent, QObject
30 import QtGui
31 from QtGui import *
32 import QtWidgets
33 from QtWidgets import *
34 
35 # from cyberglove_generic_plugin import CybergloveGenericPlugin
36 
37 from cyberglove_calibrer import *
38 from cyberglove_mapper import *
39 # from cyberglove_library import Cyberglove
40 
41 rootPath = os.path.join(
42  rospkg.RosPack().get_path('sr_gui_cyberglove_calibrator'))
43 noimage_path = rootPath + '/images/image-missing.png'
44 
45 
47 
48  """
49  Stores the description / images path for a given step.
50  """
51 
52  def __init__(self):
53  self.text = ""
54  self.image_path = [noimage_path, noimage_path]
55  self.current_substep = 0
56 
57 
58 class StepDescriber(QtWidgets.QWidget):
59 
60  """
61  Displays the description / images for the current step.
62  """
63 
64  def __init__(self, parent):
65  QtWidgets.QWidget.__init__(self, parent=parent)
66 
67  self.description = StepDescription
68  self.frame = QtWidgets.QFrame()
69  self.layout = QtWidgets.QVBoxLayout()
70 
71  self.text_description = QtWidgets.QTextEdit()
72  self.text_description.setMaximumHeight(60)
73  self.layout.addWidget(self.text_description)
74 
75  self.image_description = QtWidgets.QLabel()
76  self.image_description.setMinimumSize(170, 170)
77  self.layout.addWidget(self.image_description)
78 
79  self.frame.setLayout(self.layout)
80  layout = QtWidgets.QHBoxLayout()
81  layout.addWidget(self.frame)
82  self.setLayout(layout)
83  self.show()
84 
85  def set_description(self, description):
86  self.text_description.setText(description.text)
87  index = description.current_substep
88  self.image_description.setPixmap(
89  QtGui.QPixmap(description.image_path[index]))
90  self.image_description.repaint()
91  self.repaint()
92 
93 
94 class StepSelector(QtWidgets.QWidget):
95 
96  """
97  The user can select the step to calibrate through this widget.
98  """
99 
100  def __init__(self, parent, calibrer):
101  QtWidgets.QWidget.__init__(self, parent=parent)
102 
103  self.current_step_name = None
104  self.current_row = 0
105  self.steps = {}
107 
108  self.frame = QtWidgets.QFrame()
109  self.calibrer = calibrer
110  self.layout = QtWidgets.QVBoxLayout()
111  self.layout.setSpacing(5)
112 
113  self.title = QtWidgets.QLabel()
114  self.title.setText("Calibration Steps - 2 substeps by steps")
115 
117 
118  self.list = QtWidgets.QListWidget()
119  first_item = self.refresh_list()
120 
121  self.list.itemClicked['QListWidgetItem*'].connect(self.step_choosed)
122  self.list.setViewMode(QtWidgets.QListView.ListMode)
123  self.list.setResizeMode(QtWidgets.QListView.Adjust)
124 
125  self.list.setCurrentRow(0)
126  first_item = self.list.item(0)
127  first_item.setSelected(True)
128  self.step_choosed(first_item, second_substep=True)
129 
130  self.layout.addWidget(self.title)
131  self.layout.addWidget(self.list)
132 
133  self.frame.setLayout(self.layout)
134  layout = QtWidgets.QHBoxLayout()
135  layout.addWidget(self.frame)
136  layout.addWidget(self.step_describer)
137  self.setLayout(layout)
138  self.show()
139 
140  def step_choosed(self, item, first_time=False, second_substep=False):
141  step_name = str(item.text())
142  self.current_step_name = step_name
143 
144  if not second_substep:
145  self.steps_description[step_name].current_substep = 0
146 
147  self.current_row = self.list.currentRow()
148  index = self.steps_description[self.current_step_name].current_substep
149  name = self.current_step_name
150 
151  description = self.steps[name].step_description[index]
152  self.steps_description[self.current_step_name].text = description
153  self.step_describer.set_description(
155 
156  def refresh_list(self, value=0):
157  self.list.clear()
158  first_item = None
159  steps = self.calibrer.calibration_steps
160  index = 1
161  base_image_path = rootPath + '/images/step'
162  for step in steps:
163  item = QtWidgets.QListWidgetItem(step.step_name)
164  if first_item is None:
165  first_item = item
166  self.list.addItem(item)
167  self.steps[step.step_name] = step
168 
169  description = StepDescription()
170  description.image_path = [
171  base_image_path + str(index) + "-a.jpeg", base_image_path + str(index) + "-b.jpeg"]
172  self.steps_description[step.step_name] = description
173  index = index + 1
174  return first_item
175 
177  if self.steps_description[self.current_step_name].current_substep == 0:
178  self.steps_description[self.current_step_name].current_substep = 1
179  self.calibrer.do_step_min(self.current_row)
180 
181  description = self.steps[
182  self.current_step_name].step_description[1]
183  self.steps_description[self.current_step_name].text = description
184  self.step_describer.set_description(
186 
187  elif self.steps_description[self.current_step_name].current_substep == 1:
188  self.calibrer.do_step_max(self.current_row)
189  if self.current_row < len(self.steps) - 1:
190  self.list.setCurrentRow(self.current_row + 1)
191  next_item = self.list.item(self.current_row + 1)
192  self.step_choosed(next_item, second_substep=True)
193 
194 
195 class GloveCalibratingWidget(QtWidgets.QWidget):
196 
197  """
198  Displays which joints have been calibrated.
199  """
200 
201  def __init__(self, parent, joint_names):
202  QtWidgets.QWidget.__init__(self, parent=parent)
203  self.frame = QtWidgets.QFrame()
204 
205  self.layout = QtWidgets.QGridLayout()
206  self.layout.setHorizontalSpacing(5)
207  self.layout.setVerticalSpacing(5)
208 
209  green = QtGui.QColor(126, 255, 0)
210  red = QtGui.QColor(255, 36, 0)
211  orange = QtGui.QColor(255, 138, 0)
212  self.saved_palette = self.palette()
213  self.green_palette = self.palette()
214  self.green_palette.setBrush(QtGui.QPalette.Window, green)
215  self.red_palette = self.palette()
216  self.red_palette.setBrush(QtGui.QPalette.Window, red)
217  self.orange_palette = self.palette()
218  self.orange_palette.setBrush(QtGui.QPalette.Window, orange)
219 
220  col = 0
221  # vectors to set the correct row in the layout for each col
222  rows = [0, 0, 0, 0, 0, 0]
223 
224  self.joints_frames = {}
225 
226  for joint in joint_names:
227  if "index" in joint.lower():
228  col = 0
229  elif "middle" in joint.lower():
230  col = 1
231  elif "ring" in joint.lower():
232  if "pinkie" in joint.lower():
233  col = 3
234  else:
235  col = 2
236  elif "pinkie" in joint.lower():
237  col = 3
238  elif "thumb" in joint.lower():
239  col = 4
240  else:
241  col = 5
242 
243  row = rows[col]
244  rows[col] = row + 1
245 
246  subframe = QtWidgets.QFrame()
247  layout = QtWidgets.QHBoxLayout()
248  name = QtWidgets.QLabel()
249  name.setText(joint)
250  layout.addWidget(name)
251  subframe.setLayout(layout)
252  subframe.setPalette(self.red_palette)
253  subframe.setAutoFillBackground(True)
254  subframe.repaint()
255  self.joints_frames[joint] = subframe
256  self.layout.addWidget(subframe, row, col)
257 
258  self.set_not_calibrated(joint_names)
259 
260  self.frame.setLayout(self.layout)
261  layout = QtWidgets.QVBoxLayout()
262  layout.addWidget(self.frame)
263  self.frame.show()
264  self.setLayout(layout)
265  self.show()
266 
267  def set_not_calibrated(self, joints):
268  for joint in joints:
269  self.joints_frames[joint].setPalette(self.red_palette)
270  self.frame.repaint()
271 
272  def set_half_calibrated(self, joints):
273  for joint in joints:
274  self.joints_frames[joint].setPalette(self.orange_palette)
275  self.frame.repaint()
276 
277  def set_calibrated(self, joints):
278  for joint in joints:
279  self.joints_frames[joint].setPalette(self.green_palette)
280  self.frame.repaint()
281 
282 # class CybergloveCalibratorPlugin(CybergloveGenericPlugin):
283 
284 
286 
287  """
288  The plugin used to calibrate the glove.
289  """
290  name = "Cyberglove Calibrator"
291 
292  def __init__(self, context):
293  super(SrGuiCybergloveCalibrator, self).__init__(context)
294  self.setObjectName('SrGuiCybergloveCalibrator')
295  self.icon_dir = os.path.join(
296  rospkg.RosPack().get_path('sr_visualization_icons'), '/icons')
297 
298  ui_file = os.path.join(rospkg.RosPack().get_path(
299  'sr_gui_cyberglove_calibrator'), 'uis', 'SrGuiCybergloveCalibrator.ui')
300  self._widget = QtWidgets.QWidget()
301  loadUi(ui_file, self._widget)
302  context.add_widget(self._widget)
303 
304  # self.frame = QtWidgets.QFrame()
305  # self.layout = QtWidgets.QVBoxLayout()
306  # self.frame.setLayout(self.layout)
307  # self.window.setWidget(self.frame)
308 
309  # read nb_sensors from rosparam or fallback to 22
310  if rospy.has_param('cyberglove/cyberglove_joint_number'):
311  self.nb_sensors = rospy.get_param('cyberglove/cyberglove_joint_number')
312  else:
313  self.nb_sensors = 22
314 
315  self.calibrer = CybergloveCalibrer(description_function=None, nb_sensors=self.nb_sensors)
316  self.joint_names = self.calibrer.cyberglove.joints.keys()
317  self.joint_names.sort()
318 
319  self.layout = self._widget.layout
320  subframe = QtWidgets.QFrame()
321  sublayout = QtWidgets.QHBoxLayout()
322 
324  self._widget, self.joint_names)
325  self.layout.addWidget(self.glove_calibrating_widget)
326 
328  sublayout.addWidget(self.step_selector)
329 
330  btn_frame = QtWidgets.QFrame()
331  btn_layout = QtWidgets.QVBoxLayout()
332  btn_layout.setSpacing(25)
333  btn_calibrate = QtWidgets.QPushButton()
334  btn_calibrate.setText("Calibrate")
335  btn_calibrate.setToolTip("Calibrate the current selected step")
336  btn_calibrate.setIcon(
337  QtGui.QIcon(rootPath + '/images/icons/calibrate.png'))
338  btn_layout.addWidget(btn_calibrate)
339  btn_calibrate.clicked.connect(self.calibrate_current_step)
340  self.btn_save = QtWidgets.QPushButton()
341  self.btn_save.setText("Save")
342  self.btn_save.setToolTip("Save the current calibration")
343  self.btn_save.setIcon(QtGui.QIcon(rootPath + '/images/icons/save.png'))
344  self.btn_save.setDisabled(True)
345  btn_layout.addWidget(self.btn_save)
346  self.btn_save.clicked.connect(self.save_calib)
347  btn_load = QtWidgets.QPushButton()
348  btn_load.setText("Load")
349  btn_load.setToolTip("Load a Glove calibration")
350  btn_load.setIcon(QtGui.QIcon(rootPath + '/images/icons/load.png'))
351  btn_layout.addWidget(btn_load)
352  btn_load.clicked.connect(self.load_calib)
353  btn_frame.setLayout(btn_layout)
354  sublayout.addWidget(btn_frame)
355  subframe.setLayout(sublayout)
356  self.layout.addWidget(subframe)
357 
358  # QtCore.QTimer.singleShot(0, self.window.adjustSize)
359 
361  self.step_selector.calibrate_current_step()
362 
363  for name in self.joint_names:
364  if self.calibrer.is_step_done(name) == 0.5:
365  self.glove_calibrating_widget.set_half_calibrated([name])
366  elif self.calibrer.is_step_done(name) == 1.0:
367  self.glove_calibrating_widget.set_calibrated([name])
368 
369  if self.calibrer.all_steps_done():
370  range_errors = self.calibrer.check_ranges()
371  if len(range_errors) != 0:
372  QtWidgets.QMessageBox.warning(self._widget, "%d ensor range error(s) reported." % len(range_errors),
373  "\n".join(range_errors),
374  QtWidgets.QMessageBox.Ok,
375  QtWidgets.QMessageBox.Ok)
376 
377  self.btn_save.setEnabled(True)
378 
379  def save_calib(self):
380  # since pyqt5, filters are also returned
381  (filename, dummy) = QtWidgets.QFileDialog.getSaveFileName(
382  self._widget, 'Save Calibration', '')
383  if filename == "":
384  return
385 
386  write_output = self.calibrer.write_calibration_file(filename)
387  error = None
388  if write_output == -1:
389  error = "Calibration has not been finished, output not written."
390  elif write_output == -2:
391  error = "Error writing file."
392 
393  if error is not None:
394  QtWidgets.QMessageBox.critical(self._widget, "Error writing config!",
395  error,
396  QtWidgets.QMessageBox.Ok,
397  QtWidgets.QMessageBox.Ok)
398  elif QtWidgets.QMessageBox.question(self._widget,
399  "Load new Calibration",
400  "Do you want to load the new calibration file?",
401  QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
402  QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.Yes:
403  self.load_calib(filename)
404 
405  def load_calib(self, filename=""):
406  # when called from the button. filename is filled with some more info
407  if filename is False:
408  filename = ""
409  if "" == filename:
410  # since pyqt5, filters are also returned
411  (filename, dummy) = QtWidgets.QFileDialog.getOpenFileName(
412  self._widget, 'Open Calibration', '')
413  if "" == filename:
414  return
415 
416  if self.calibrer.load_calib(str(filename)) == 0:
417  # statusbar do not exist in rqt anymore (since shared with several plugins)
418  # self.statusBar().showMessage("New Cyberglove Calibration Loaded.")
419  QtWidgets.QMessageBox.information(self._widget, "Calibration successfully loaded",
420  "Calibration successfully loaded.",
421  QtWidgets.QMessageBox.Ok,
422  QtWidgets.QMessageBox.Ok)
423  else:
424  QtWidgets.QMessageBox.information(self._widget, "Calibration loading failed",
425  "Calibration loading failed",
426  QtWidgets.QMessageBox.Ok,
427  QtWidgets.QMessageBox.Ok)
def step_choosed(self, item, first_time=False, second_substep=False)


sr_gui_cyberglove_calibrator
Author(s): Ugo Cupcic
autogenerated on Wed Oct 14 2020 03:22:50