Ejemplo n.º 1
0
class MessageBox(QDialog):

    NoIcon = 0
    Information = 1
    Warning = 2
    Critical = 3
    Question = 4

    NoButton = 0
    Ok = 1  # An "OK" button defined with the AcceptRole .
    Open = 2  # A "Open" button defined with the AcceptRole .
    Save = 4  # A "Save" button defined with the AcceptRole .
    Cancel = 8  # A "Cancel" button defined with the RejectRole .
    Close = 16  # A "Close" button defined with the RejectRole .
    Discard = 32  # A "Discard" or "Don't Save" button, depending on the platform, defined with the DestructiveRole .
    Apply = 64  # An "Apply" button defined with the ApplyRole .
    Reset = 128  # A "Reset" button defined with the ResetRole .
    RestoreDefaults = 256  # A "Restore Defaults" button defined with the ResetRole .
    Help = 512  # A "Help" button defined with the HelpRole .
    SaveAll = 1024  # A "Save All" button defined with the AcceptRole .
    Yes = 2048  # A "Yes" button defined with the YesRole .
    YesToAll = 4096  # A "Yes to All" button defined with the YesRole .
    No = 8192  # A "No" button defined with the NoRole .
    NoToAll = 16384  # A "No to All" button defined with the NoRole .
    Abort = 32768  # An "Abort" button defined with the RejectRole .
    Retry = 65536  # A "Retry" button defined with the AcceptRole .
    Ignore = 131072  # An "Ignore" button defined with the AcceptRole .
    Avoid = 262144  # An "'Don't show again'" button defined with the HelpRole, returns a default AcceptButton .

    def __init__(self,
                 icon,
                 title,
                 text,
                 detailed_text="",
                 buttons=Cancel | Ok,
                 parent=None):
        QDialog.__init__(self, parent=parent)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowTitleHint)
        self.setWindowFlags(self.windowFlags()
                            & ~Qt.WindowContextHelpButtonHint
                            & ~Qt.WindowMinimizeButtonHint)
        self.setObjectName('MessageBox')
        self._use_checkbox = True
        self.text = text
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setObjectName("verticalLayout")
        self.verticalLayout.setContentsMargins(1, 1, 1, 1)

        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
        # create icon
        pixmap = None
        if icon == self.NoIcon:
            pass
        elif icon == self.Question:
            pixmap = QPixmap(
                QImage(":icons/question.png").scaled(56, 56,
                                                     Qt.IgnoreAspectRatio,
                                                     Qt.SmoothTransformation))
        elif icon == self.Information:
            pixmap = QPixmap(
                QImage(":icons/info.png").scaled(56, 56, Qt.IgnoreAspectRatio,
                                                 Qt.SmoothTransformation))
        elif icon == self.Warning:
            pixmap = QPixmap(
                QImage(":icons/warning.png").scaled(56, 56,
                                                    Qt.IgnoreAspectRatio,
                                                    Qt.SmoothTransformation))
        elif icon == self.Critical:
            pixmap = QPixmap(
                QImage(":icons/critical.png").scaled(56, 56,
                                                     Qt.IgnoreAspectRatio,
                                                     Qt.SmoothTransformation))
        spacerItem = QSpacerItem(10, 60, QSizePolicy.Minimum,
                                 QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.icon_label = QLabel()
        if pixmap is not None:
            self.icon_label.setPixmap(pixmap)
        self.icon_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.horizontalLayout.addWidget(self.icon_label)
        spacerItem = QSpacerItem(10, 60, QSizePolicy.Minimum,
                                 QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        # add message
        self.message_label = QLabel(text)
        self.message_label.setWordWrap(True)
        self.message_label.setScaledContents(True)
        self.message_label.setOpenExternalLinks(True)
        self.horizontalLayout.addWidget(self.message_label)
        self.verticalLayout.addLayout(self.horizontalLayout)

        # create buttons
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName("buttonBox")
        self.buttonBox.setOrientation(Qt.Horizontal)

        self._accept_button = None
        self._reject_button = None
        self._buttons = buttons
        self._create_buttons(buttons)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.verticalLayout.addWidget(self.buttonBox)

        if detailed_text:
            self.btn_show_details = QPushButton(self.tr('Details...'))
            self.btn_show_details.setCheckable(True)
            self.btn_show_details.setChecked(True)
            self.btn_show_details.toggled.connect(self.on_toggled_details)
            self.buttonBox.addButton(self.btn_show_details,
                                     QDialogButtonBox.ActionRole)
            # create area for detailed text
            self.textEdit = textEdit = QTextEdit(self)
            textEdit.setObjectName("textEdit")
            textEdit.setReadOnly(True)
            textEdit.setText(detailed_text)
            # textEdit.setVisible(False)
            self.verticalLayout.addWidget(self.textEdit)
        self.resize(480, self.verticalLayout.totalSizeHint().height())
        buttons_in_box = self.buttonBox.buttons()
        if buttons_in_box:
            self.buttonBox.buttons()[0].setFocus()

    def setAcceptButton(self, button):
        '''
        Sets the button with given ID to accept button if more then one button with AcceptRole was added to this dialog.
        Adds the buttton to the box if is not already in.
        :param int button: button id
        '''
        if not button & self._buttons:
            self._create_buttons(button)
        self._accept_button = button

    def setRejectButton(self, button):
        '''
        Sets the button with given ID to reject button if more then one button with RejectRole was added to this dialog.
        Adds the buttton to the box if is not already in.
        :param int button: button id
        '''
        if not button & self._buttons:
            self._create_buttons(button)
        self._reject_button = button

    def on_toggled_details(self, checked):
        if checked:
            self.verticalLayout.addWidget(self.textEdit)
        else:
            self.verticalLayout.removeWidget(self.textEdit)
        self.textEdit.setVisible(checked)
        if not self.isMaximized():
            self.setMinimumSize(self.verticalLayout.totalMinimumSize())
            self.resize(self._current_size.width(),
                        self.verticalLayout.totalSizeHint().height())

    @staticmethod
    def about(parent, title, text, detailed_text='', buttons=Close):
        box = MessageBox(MessageBox.Information,
                         title,
                         text,
                         detailed_text=detailed_text,
                         buttons=buttons,
                         parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def information(parent, title, text, detailed_text='', buttons=Close):
        box = MessageBox(MessageBox.Information,
                         title,
                         text,
                         detailed_text=detailed_text,
                         buttons=buttons,
                         parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def question(parent,
                 title,
                 text,
                 detailed_text='',
                 buttons=Yes | No | Cancel):
        box = MessageBox(MessageBox.Question,
                         title,
                         text,
                         detailed_text=detailed_text,
                         buttons=buttons,
                         parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def warning(parent, title, text, detailed_text='', buttons=Ok):
        box = MessageBox(MessageBox.Warning,
                         title,
                         text,
                         detailed_text=detailed_text,
                         buttons=buttons,
                         parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def critical(parent, title, text, detailed_text='', buttons=Ok):
        box = MessageBox(MessageBox.Critical,
                         title,
                         text,
                         detailed_text=detailed_text,
                         buttons=buttons,
                         parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    def resizeEvent(self, event):
        if not self.isMaximized():
            self._current_size = event.size()

# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%% close handling                        %%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    def exec_(self):
        if self.text in IGNORED_ERRORS:
            self.accept()
            return self.result()
        return QDialog.exec_(self)

    def accept(self):
        if self.result() == 0:
            if self._accept_button is not None:
                self.setResult(self._accept_button)
            else:
                self.setResult(1)
        self.accepted.emit()
        if self.isModal():
            self.hide()

    def reject(self):
        if self.result() == 0:
            if self._reject_button is not None:
                self.setResult(self._reject_button)
        self.rejected.emit()
        self.hide()

    def hideEvent(self, event):
        # event.ignore()
        self.close()

    def closeEvent(self, event):
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        QDialog.closeEvent(self, event)

# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%% create buttons                        %%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    def _create_buttons(self, buttons):
        if MessageBox.Ok & buttons:
            self._accept_button = MessageBox.Ok
            bt = QPushButton(self.tr("&ok"))
            bt.clicked.connect(self._on_ok_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Open & buttons:
            self._accept_button = MessageBox.Open
            bt = QPushButton(self.tr("&Open"))
            bt.clicked.connect(self._on_open_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Save & buttons:
            self._accept_button = MessageBox.Save
            bt = QPushButton(self.tr("&Save"))
            bt.clicked.connect(self._on_save_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Cancel & buttons:
            self._reject_button = MessageBox.Cancel
            bt = QPushButton(self.tr("&Cancel"))
            bt.clicked.connect(self._on_cancel_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Close & buttons:
            self._reject_button = MessageBox.Close
            bt = QPushButton(self.tr("&Close"))
            bt.clicked.connect(self._on_close_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Discard & buttons:
            bt = QPushButton(self.tr("&Discard"))
            bt.clicked.connect(self._on_discard_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.DestructiveRole)
        if MessageBox.Apply & buttons:
            bt = QPushButton(self.tr("&Apply"))
            bt.clicked.connect(self._on_apply_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ApplyRole)
        if MessageBox.Reset & buttons:
            bt = QPushButton(self.tr("&Reset"))
            bt.clicked.connect(self._on_reset_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ResetRole)
        if MessageBox.RestoreDefaults & buttons:
            bt = QPushButton(self.tr("&RestoreDefaults"))
            bt.clicked.connect(self._on_restore_defaults_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ResetRole)
        if MessageBox.Help & buttons:
            bt = QPushButton(self.tr("&Help"))
            bt.clicked.connect(self._on_help_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.HelpRole)
        if MessageBox.SaveAll & buttons:
            self._accept_button = MessageBox.SaveAll
            bt = QPushButton(self.tr("&SaveAll"))
            bt.clicked.connect(self._on_saveall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Yes & buttons:
            bt = QPushButton(self.tr("&Yes"))
            bt.clicked.connect(self._on_yes_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.YesRole)
        if MessageBox.YesToAll & buttons:
            bt = QPushButton(self.tr("&YesToAll"))
            bt.clicked.connect(self._on_yestoall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.YesRole)
        if MessageBox.No & buttons:
            bt = QPushButton(self.tr("&No"))
            bt.clicked.connect(self._on_no_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.NoRole)
        if MessageBox.NoToAll & buttons:
            bt = QPushButton(self.tr("&NoToAll"))
            bt.clicked.connect(self._on_notoall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.NoRole)
        if MessageBox.Abort & buttons:
            self._reject_button = MessageBox.Abort
            bt = QPushButton(self.tr("&Abort"))
            bt.clicked.connect(self._on_abort_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Retry & buttons:
            self._accept_button = MessageBox.Retry
            bt = QPushButton(self.tr("&Retry"))
            bt.clicked.connect(self._on_retry_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Ignore & buttons:
            self._accept_button = MessageBox.Ignore
            bt = QPushButton(self.tr("&Ignore"))
            bt.clicked.connect(self._on_ignore_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Avoid & buttons:
            if self._use_checkbox:
                checkbox = QCheckBox("&Don't show again", self)
                checkbox.stateChanged.connect(self._check_ignore)
                self.buttonBox.addButton(checkbox, QDialogButtonBox.HelpRole)
            else:
                bt = QPushButton(self.tr("&Don't show again"))
                bt.setMaximumHeight(24)
                bt.clicked.connect(self._add_to_ignore)
                self.buttonBox.addButton(bt, QDialogButtonBox.HelpRole)

    def _on_ok_clicked(self):
        self.done(MessageBox.Ok)

    def _on_open_clicked(self):
        self.done(MessageBox.Open)

    def _on_save_clicked(self):
        self.done(MessageBox.Save)

    def _on_cancel_clicked(self):
        self.done(MessageBox.Cancel)

    def _on_close_clicked(self):
        self.done(MessageBox.Close)

    def _on_discard_clicked(self):
        self.done(MessageBox.Discard)

    def _on_apply_clicked(self):
        self.done(MessageBox.Apply)

    def _on_reset_clicked(self):
        self.done(MessageBox.Reset)

    def _on_restore_defaults_clicked(self):
        self.done(MessageBox.RestoreDefaults)

    def _on_help_clicked(self):
        self.done(MessageBox.Help)

    def _on_saveall_clicked(self):
        self.done(MessageBox.SaveAll)

    def _on_yes_clicked(self):
        self.done(MessageBox.Yes)

    def _on_yestoall_clicked(self):
        self.done(MessageBox.YesToAll)

    def _on_no_clicked(self):
        self.done(MessageBox.No)

    def _on_notoall_clicked(self):
        self.done(MessageBox.NoToAll)

    def _on_abort_clicked(self):
        self.done(MessageBox.Abort)

    def _on_retry_clicked(self):
        self.done(MessageBox.Retry)

    def _on_ignore_clicked(self):
        self.done(MessageBox.Ignore)

    def _add_to_ignore(self):
        IGNORED_ERRORS.append(self.text)
        self.accept()

    def _check_ignore(self, state):
        if state:
            IGNORED_ERRORS.append(self.text)
        else:
            try:
                IGNORED_ERRORS.remove(self.text)
            except Exception:
                pass
Ejemplo n.º 2
0
class BatteryProfileFrame(QFrame):
    def __init__(self, parent=None):
        super(BatteryProfileFrame, self).__init__(parent)
        self._cmd_vel_topic_name = '/mobile_base/commands/velocity'
        self._battery_topic_name = "/mobile_base/sensors/core/battery"
        self._ui = Ui_battery_profile_frame()
        self._motion = None
        self.robot_state_subscriber = None
        self._motion_thread = None
        self._plot_widget = None

    def setupUi(self, cmd_vel_topic_name):
        self._ui.setupUi(self)
        self._cmd_vel_topic_name = cmd_vel_topic_name
        self._plot_layout = QVBoxLayout(self._ui.battery_profile_group_box)
        self._ui.start_button.setEnabled(True)
        self._ui.stop_button.setEnabled(False)
        self.restore()

    def shutdown(self):
        '''
          Used to terminate the plugin
        '''
        rospy.loginfo("Kobuki TestSuite: battery test shutdown")
        self._stop()

    ##########################################################################
    # Widget Management
    ##########################################################################

    def hibernate(self):
        '''
          This gets called when the frame goes out of focus (tab switch).
          Disable everything to avoid running N tabs in parallel when in
          reality we are only running one.
        '''
        self._stop()
        self._plot_layout.removeWidget(self._plot_widget)
        self._plot_widget = None

    def restore(self):
        '''
          Restore the frame after a hibernate.
        '''
        self._plot_widget = PlotWidget()
        self._plot_widget.setWindowTitle("Battery Profile")
        self._plot_widget.topic_edit.setText(self._battery_topic_name)
        self._plot_layout.addWidget(self._plot_widget)

        self._data_plot = DataPlot(self._plot_widget)
        self._data_plot.set_autoscale(y=False)
        self._data_plot.set_ylim([0, 180])
        self._plot_widget.switch_data_plot_widget(self._data_plot)

    ##########################################################################
    # Motion Callbacks
    ##########################################################################

    def _run_finished(self):
        self._ui.start_button.setEnabled(True)
        self._ui.stop_button.setEnabled(False)

    ##########################################################################
    # Qt Callbacks
    ##########################################################################
    @Slot()
    def on_start_button_clicked(self):
        self._ui.start_button.setEnabled(False)
        self._ui.stop_button.setEnabled(True)
        if not self._motion:
            self._motion = Rotate(self._cmd_vel_topic_name)
            self._motion.init(self._ui.angular_speed_spinbox.value())
        if not self.robot_state_subscriber:
            self.robot_state_subscriber = rospy.Subscriber(
                "/mobile_base/events/robot_state", RobotStateEvent,
                self.robot_state_callback)
        rospy.sleep(0.5)
        self._plot_widget._start_time = rospy.get_time()
        self._plot_widget.enable_timer(True)
        try:
            self._plot_widget.remove_topic(self._battery_topic_name)
        except KeyError:
            pass
        self._plot_widget.add_topic(self._battery_topic_name)
        self._motion_thread = WorkerThread(self._motion.execute,
                                           self._run_finished)
        self._motion_thread.start()

    @Slot()
    def on_stop_button_clicked(self):
        '''
          Hardcore stoppage - straight to zero.
        '''
        self._stop()

    def _stop(self):
        if self._plot_widget:
            self._plot_widget.enable_timer(False)  # pause plot rendering
        if self._motion_thread:
            self._motion.stop()
            self._motion_thread.wait()
            self._motion_thread = None
        if self._motion:
            self._motion.shutdown()
            self._motion = None
        if self.robot_state_subscriber:
            self.robot_state_subscriber.unregister()
            self.robot_state_subscriber = None
        self._ui.start_button.setEnabled(True)
        self._ui.stop_button.setEnabled(False)

    @pyqtSlot(float)
    def on_angular_speed_spinbox_valueChanged(self, value):
        if self._motion:
            self._motion.init(self._ui.angular_speed_spinbox.value())

    ##########################################################################
    # External Slot Callbacks
    ##########################################################################
    @Slot(str)
    def on_cmd_vel_topic_combo_box_currentIndexChanged(self, topic_name):
        '''
          To be connected to the configuration dock combo box (via the main testsuite frame)
        '''
        self._cmd_vel_topic_name = topic_name
        print("DudetteBattery %s" % topic_name)

    ##########################################################################
    # Ros Callbacks
    ##########################################################################

    def robot_state_callback(self, data):
        if data.state == RobotStateEvent.OFFLINE:
            self.stop()
Ejemplo n.º 3
0
class GyroDriftFrame(QFrame):
    def __init__(self, parent=None):
        super(GyroDriftFrame, self).__init__(parent)
        self._ui = Ui_gyro_drift_frame()
        self._laser_scan_angle_topic_name = '/laser_scan_angle'
        self._gyro_scan_angle_topic_name = '/gyro_scan_angle'
        self._error_scan_angle_topic_name = '/error_scan_angle'
        self._cmd_vel_topic_name = '/mobile_base/commands/velocity'
        self._gyro_topic_name = '/mobile_base/sensors/imu_data'
        self._motion = None
        self._scan_to_angle = None
        self._motion_thread = None
        self._plot_widget = None
        self._plot_widget_live = None

    def setupUi(self):
        self._ui.setupUi(self)
        self._plot_layout = QVBoxLayout(self._ui.scan_angle_group_box)
        self._ui.start_button.setEnabled(True)
        self._ui.stop_button.setEnabled(False)

    def shutdown(self):
        '''
          Used to terminate the plugin
        '''
        rospy.loginfo("Kobuki TestSuite: gyro drift shutdown")
        self._stop()

    ##########################################################################
    # Widget Management
    ##########################################################################

    def _pause_plots(self):
        '''
         Pause plots, more importantly, pause greedy plot rendering
        '''
        if self._plot_widget:
            self._plot_widget.enable_timer(False)
        if self._plot_widget_live:
            self._plot_widget_live.enable_timer(False)

    def hibernate(self):
        '''
          This gets called when the frame goes out of focus (tab switch).
          Disable everything to avoid running N tabs in parallel when in
          reality we are only running one.
        '''
        self._stop()
        self._plot_layout.removeWidget(self._plot_widget)
        self._plot_layout.removeWidget(self._plot_widget_live)
        self._plot_widget = None
        self._plot_widget_live = None

    def restore(self):
        '''
          Restore the frame after a hibernate.
        '''
        self._plot_widget = PlotWidget()
        self._plot_widget.setWindowTitle("Error")
        self._plot_layout.addWidget(self._plot_widget)
        self._plot_widget.switch_data_plot_widget(DataPlot(self._plot_widget))
        self._plot_widget.data_plot.dynamic_range = True
        self._plot_widget_live = PlotWidget()
        self._plot_widget_live.setWindowTitle("Live Graphs")
        self._plot_widget_live.switch_data_plot_widget(DataPlot(self._plot_widget_live))
        self._plot_layout.addWidget(self._plot_widget_live)

    ##########################################################################
    # Qt Callbacks
    ##########################################################################
    @Slot()
    def on_start_button_clicked(self):
        self._ui.start_button.setEnabled(False)
        self._ui.stop_button.setEnabled(True)
        if not self._scan_to_angle:
            self._scan_to_angle = ScanToAngle('/scan',self._laser_scan_angle_topic_name)
        if not self._motion:
            self._motion = DriftEstimation(self._laser_scan_angle_topic_name, self._gyro_scan_angle_topic_name, self._error_scan_angle_topic_name, self._cmd_vel_topic_name,self._gyro_topic_name)
            self._motion.init(self._ui.angular_speed_spinbox.value())
        rospy.sleep(0.5)
        self._plot_widget._start_time = rospy.get_time()
        self._plot_widget.enable_timer(True)
        try:
            self._plot_widget.remove_topic(self._error_scan_angle_topic_name+'/scan_angle')
            self._plot_widget_live.remove_topic(self._laser_scan_angle_topic_name+'/scan_angle')
            self._plot_widget_live.remove_topic(self._gyro_scan_angle_topic_name+'/scan_angle')
            self._plot_widget_live.remove_topic(self._cmd_vel_topic_name+'/angular/z')
        except KeyError:
            pass
        self._plot_widget_live.add_topic(self._cmd_vel_topic_name+'/angular/z')
        self._plot_widget_live.add_topic(self._laser_scan_angle_topic_name+'/scan_angle')
        self._plot_widget_live.add_topic(self._gyro_scan_angle_topic_name+'/scan_angle')
        self._plot_widget.add_topic(self._error_scan_angle_topic_name+'/scan_angle')
        self._motion_thread = WorkerThread(self._motion.execute, None)
        self._motion_thread.start()

    @Slot()
    def on_stop_button_clicked(self):
        self._stop()

    def _stop(self):
        self._pause_plots()
        if self._scan_to_angle:
            self._scan_to_angle.shutdown()
            self._scan_to_angle = None
        if self._motion:
            self._motion.stop()
        if self._motion_thread:
            self._motion_thread.wait()
            self._motion_thread = None
        if self._motion:
            self._motion.shutdown()
            self._motion = None
        self._ui.start_button.setEnabled(True)
        self._ui.stop_button.setEnabled(False)

    @pyqtSlot(float)
    def on_angular_speed_spinbox_valueChanged(self, value):
        if self._motion:
            self._motion.init(self._ui.angular_speed_spinbox.value())
class MessageBox(QDialog):

    NoIcon = 0
    Information = 1
    Warning = 2
    Critical = 3
    Question = 4

    NoButton = 0
    Ok = 1         # An "OK" button defined with the AcceptRole .
    Open = 2       # A "Open" button defined with the AcceptRole .
    Save = 4       # A "Save" button defined with the AcceptRole .
    Cancel = 8     # A "Cancel" button defined with the RejectRole .
    Close = 16     # A "Close" button defined with the RejectRole .
    Discard = 32   # A "Discard" or "Don't Save" button, depending on the platform, defined with the DestructiveRole .
    Apply = 64     # An "Apply" button defined with the ApplyRole .
    Reset = 128    # A "Reset" button defined with the ResetRole .
    RestoreDefaults = 256  # A "Restore Defaults" button defined with the ResetRole .
    Help = 512       # A "Help" button defined with the HelpRole .
    SaveAll = 1024   # A "Save All" button defined with the AcceptRole .
    Yes = 2048       # A "Yes" button defined with the YesRole .
    YesToAll = 4096  # A "Yes to All" button defined with the YesRole .
    No = 8192        # A "No" button defined with the NoRole .
    NoToAll = 16384  # A "No to All" button defined with the NoRole .
    Abort = 32768    # An "Abort" button defined with the RejectRole .
    Retry = 65536    # A "Retry" button defined with the AcceptRole .
    Ignore = 131072  # An "Ignore" button defined with the AcceptRole .
    Avoid = 262144   # An "'Don't show again'" button defined with the HelpRole, returns a default AcceptButton .

    def __init__(self, icon, title, text, detailed_text="", buttons=Cancel | Ok, parent=None):
        QDialog.__init__(self, parent=parent)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowTitleHint)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint & ~Qt.WindowMinimizeButtonHint)
        self.setObjectName('MessageBox')
        self._use_checkbox = True
        self.text = text
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setObjectName("verticalLayout")
        self.verticalLayout.setContentsMargins(1, 1, 1, 1)

        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
        # create icon
        pixmap = None
        if icon == self.NoIcon:
            pass
        elif icon == self.Question:
            pixmap = QApplication.style().standardPixmap(QStyle.SP_MessageBoxQuestion)
        elif icon == self.Information:
            pixmap = QApplication.style().standardPixmap(QStyle.SP_MessageBoxInformation)
        elif icon == self.Warning:
            pixmap = QPixmap(":icons/crystal_clear_warning_56.png")
        elif icon == self.Critical:
            pixmap = QApplication.style().standardPixmap(QStyle.SP_MessageBoxCritical)
        spacerItem = QSpacerItem(10, 60, QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.icon_label = QLabel()
        if pixmap is not None:
            self.icon_label.setPixmap(pixmap)
        self.icon_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.horizontalLayout.addWidget(self.icon_label)
        spacerItem = QSpacerItem(10, 60, QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        # add message
        self.message_label = QLabel(text)
        self.message_label.setWordWrap(True)
        self.message_label.setScaledContents(True)
        self.message_label.setOpenExternalLinks(True)
        self.horizontalLayout.addWidget(self.message_label)
        self.verticalLayout.addLayout(self.horizontalLayout)

        # create buttons
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName("buttonBox")
        self.buttonBox.setOrientation(Qt.Horizontal)

        self._accept_button = None
        self._reject_button = None
        self._buttons = buttons
        self._create_buttons(buttons)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.verticalLayout.addWidget(self.buttonBox)

        if detailed_text:
            self.btn_show_details = QPushButton(self.tr('Details...'))
            self.btn_show_details.setCheckable(True)
            self.btn_show_details.setChecked(True)
            self.btn_show_details.toggled.connect(self.on_toggled_details)
            self.buttonBox.addButton(self.btn_show_details, QDialogButtonBox.ActionRole)
            # create area for detailed text
            self.textEdit = textEdit = QTextEdit(self)
            textEdit.setObjectName("textEdit")
            textEdit.setReadOnly(True)
            textEdit.setText(detailed_text)
            # textEdit.setVisible(False)
            self.verticalLayout.addWidget(self.textEdit)
        self.resize(480, self.verticalLayout.totalSizeHint().height())
        buttons_in_box = self.buttonBox.buttons()
        if buttons_in_box:
            self.buttonBox.buttons()[0].setFocus()

    def setAcceptButton(self, button):
        '''
        Sets the button with given ID to accept button if more then one button with AcceptRole was added to this dialog.
        Adds the buttton to the box if is not already in.
        :param button: int
        '''
        if not button & self._buttons:
            self._create_buttons(button)
        self._accept_button = button

    def setRejectButton(self, button):
        '''
        Sets the button with given ID to reject button if more then one button with RejectRole was added to this dialog.
        Adds the buttton to the box if is not already in.
        :param button: int
        '''
        if not button & self._buttons:
            self._create_buttons(button)
        self._reject_button = button

    def on_toggled_details(self, checked):
        if checked:
            self.verticalLayout.addWidget(self.textEdit)
        else:
            self.verticalLayout.removeWidget(self.textEdit)
        self.textEdit.setVisible(checked)
        if not self.isMaximized():
            self.setMinimumSize(self.verticalLayout.totalMinimumSize())
            self.resize(self._current_size.width(), self.verticalLayout.totalSizeHint().height())

    @staticmethod
    def about(parent, title, text, detailed_text='', buttons=Close):
        box = MessageBox(MessageBox.Information, title, text, detailed_text=detailed_text, buttons=buttons, parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def information(parent, title, text, detailed_text='', buttons=Close):
        box = MessageBox(MessageBox.Information, title, text, detailed_text=detailed_text, buttons=buttons, parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def question(parent, title, text, detailed_text='', buttons=Yes | No | Cancel):
        box = MessageBox(MessageBox.Question, title, text, detailed_text=detailed_text, buttons=buttons, parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def warning(parent, title, text, detailed_text='', buttons=Ok):
        box = MessageBox(MessageBox.Warning, title, text, detailed_text=detailed_text, buttons=buttons, parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    @staticmethod
    def critical(parent, title, text, detailed_text='', buttons=Ok):
        box = MessageBox(MessageBox.Critical, title, text, detailed_text=detailed_text, buttons=buttons, parent=parent)
        if MessageBox.Yes & buttons:
            box.setAcceptButton(MessageBox.Yes)
        if MessageBox.Cancel & buttons:
            box.setRejectButton(MessageBox.Cancel)
        elif MessageBox.No & buttons:
            box.setRejectButton(MessageBox.No)
        return box.exec_()

    def resizeEvent(self, event):
        if not self.isMaximized():
            self._current_size = event.size()

# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%% close handling                        %%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    def exec_(self):
        if self.text in IGNORED_ERRORS:
            self.accept()
            return self.result()
        return QDialog.exec_(self)

    def accept(self):
        if self.result() == 0:
            if self._accept_button is not None:
                self.setResult(self._accept_button)
            else:
                self.setResult(1)
        self.accepted.emit()
        if self.isModal():
            self.hide()

    def reject(self):
        if self.result() == 0:
            if self._reject_button is not None:
                self.setResult(self._reject_button)
        self.rejected.emit()
        self.hide()

    def hideEvent(self, event):
        # event.ignore()
        self.close()

    def closeEvent(self, event):
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        QDialog.closeEvent(self, event)

# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%% create buttons                        %%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    def _create_buttons(self, buttons):
        if MessageBox.Ok & buttons:
            self._accept_button = MessageBox.Ok
            bt = QPushButton(self.tr("&ok"))
            bt.clicked.connect(self._on_ok_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Open & buttons:
            self._accept_button = MessageBox.Open
            bt = QPushButton(self.tr("&Open"))
            bt.clicked.connect(self._on_open_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Save & buttons:
            self._accept_button = MessageBox.Save
            bt = QPushButton(self.tr("&Save"))
            bt.clicked.connect(self._on_save_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Cancel & buttons:
            self._reject_button = MessageBox.Cancel
            bt = QPushButton(self.tr("&Cancel"))
            bt.clicked.connect(self._on_cancel_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Close & buttons:
            self._reject_button = MessageBox.Close
            bt = QPushButton(self.tr("&Close"))
            bt.clicked.connect(self._on_close_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Discard & buttons:
            bt = QPushButton(self.tr("&Discard"))
            bt.clicked.connect(self._on_discard_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.DestructiveRole)
        if MessageBox.Apply & buttons:
            bt = QPushButton(self.tr("&Apply"))
            bt.clicked.connect(self._on_apply_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ApplyRole)
        if MessageBox.Reset & buttons:
            bt = QPushButton(self.tr("&Reset"))
            bt.clicked.connect(self._on_reset_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ResetRole)
        if MessageBox.RestoreDefaults & buttons:
            bt = QPushButton(self.tr("&RestoreDefaults"))
            bt.clicked.connect(self._on_restore_defaults_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.ResetRole)
        if MessageBox.Help & buttons:
            bt = QPushButton(self.tr("&Help"))
            bt.clicked.connect(self._on_help_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.HelpRole)
        if MessageBox.SaveAll & buttons:
            self._accept_button = MessageBox.SaveAll
            bt = QPushButton(self.tr("&SaveAll"))
            bt.clicked.connect(self._on_saveall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Yes & buttons:
            bt = QPushButton(self.tr("&Yes"))
            bt.clicked.connect(self._on_yes_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.YesRole)
        if MessageBox.YesToAll & buttons:
            bt = QPushButton(self.tr("&YesToAll"))
            bt.clicked.connect(self._on_yestoall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.YesRole)
        if MessageBox.No & buttons:
            bt = QPushButton(self.tr("&No"))
            bt.clicked.connect(self._on_no_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.NoRole)
        if MessageBox.NoToAll & buttons:
            bt = QPushButton(self.tr("&NoToAll"))
            bt.clicked.connect(self._on_notoall_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.NoRole)
        if MessageBox.Abort & buttons:
            self._reject_button = MessageBox.Abort
            bt = QPushButton(self.tr("&Abort"))
            bt.clicked.connect(self._on_abort_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.RejectRole)
        if MessageBox.Retry & buttons:
            self._accept_button = MessageBox.Retry
            bt = QPushButton(self.tr("&Retry"))
            bt.clicked.connect(self._on_retry_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Ignore & buttons:
            self._accept_button = MessageBox.Ignore
            bt = QPushButton(self.tr("&Ignore"))
            bt.clicked.connect(self._on_ignore_clicked)
            self.buttonBox.addButton(bt, QDialogButtonBox.AcceptRole)
        if MessageBox.Avoid & buttons:
            if self._use_checkbox:
                checkbox = QCheckBox("&Don't show again", self)
                checkbox.stateChanged.connect(self._check_ignore)
                self.buttonBox.addButton(checkbox, QDialogButtonBox.HelpRole)
            else:
                bt = QPushButton(self.tr("&Don't show again"))
                bt.setMaximumHeight(24)
                bt.clicked.connect(self._add_to_ignore)
                self.buttonBox.addButton(bt, QDialogButtonBox.HelpRole)

    def _on_ok_clicked(self):
        self.done(MessageBox.Ok)

    def _on_open_clicked(self):
        self.done(MessageBox.Open)

    def _on_save_clicked(self):
        self.done(MessageBox.Save)

    def _on_cancel_clicked(self):
        self.done(MessageBox.Cancel)

    def _on_close_clicked(self):
        self.done(MessageBox.Close)

    def _on_discard_clicked(self):
        self.done(MessageBox.Discard)

    def _on_apply_clicked(self):
        self.done(MessageBox.Apply)

    def _on_reset_clicked(self):
        self.done(MessageBox.Reset)

    def _on_restore_defaults_clicked(self):
        self.done(MessageBox.RestoreDefaults)

    def _on_help_clicked(self):
        self.done(MessageBox.Help)

    def _on_saveall_clicked(self):
        self.done(MessageBox.SaveAll)

    def _on_yes_clicked(self):
        self.done(MessageBox.Yes)

    def _on_yestoall_clicked(self):
        self.done(MessageBox.YesToAll)

    def _on_no_clicked(self):
        self.done(MessageBox.No)

    def _on_notoall_clicked(self):
        self.done(MessageBox.NoToAll)

    def _on_abort_clicked(self):
        self.done(MessageBox.Abort)

    def _on_retry_clicked(self):
        self.done(MessageBox.Retry)

    def _on_ignore_clicked(self):
        self.done(MessageBox.Ignore)

    def _add_to_ignore(self):
        IGNORED_ERRORS.append(self.text)
        self.accept()

    def _check_ignore(self, state):
        if state:
            IGNORED_ERRORS.append(self.text)
        else:
            try:
                IGNORED_ERRORS.remove(self.text)
            except Exception:
                pass