Exemple #1
0
class Worker(QObject):
    progress = QtCore.Signal(int)
    finished = QtCore.Signal()
    error = QtCore.Signal(str)

    def __init__(self, run):
        super(Worker, self).__init__()
        self.run_function = run

    def process(self):
        self.progress.emit(0)
        try:
            if isinstance(self.run_function, types.GeneratorType):
                p = 0
                self.progress.emit(p)
                while p < 100:
                    p = self.run_function()
                    self.progress.emit(p)
            else:
                self.run_function()
            self.progress.emit(100)
            self.finished.emit()
        except Exception as e:
            self.error.emit(str(e))
            raise
class ServiceThread(QtCore.QObject, threading.Thread):
    '''
  A thread to to retrieve the list of nodes from the default configuration 
  service and publish it by sending a QT signal.
  '''
    update_signal = QtCore.Signal(str, str, list)
    err_signal = QtCore.Signal(str, str, str)

    def __init__(self, service_uri, service, delay_exec=0.0, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._service_uri = service_uri
        self._service = service
        self._delay_exec = delay_exec
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._service and self._service_uri:
            try:
                if self._delay_exec > 0:
                    time.sleep(self._delay_exec)
                _, resp = nm.starter().callService(self._service_uri,
                                                   self._service, ListNodes)
                self.update_signal.emit(self._service_uri, self._service,
                                        resp.nodes)
            except:
                import traceback
                lines = traceback.format_exc().splitlines()
                rospy.logwarn(
                    "Error while retrieve the node list from %s[%s]: %s",
                    str(self._service), str(self._service_uri), str(lines[-1]))
                self.err_signal.emit(self._service_uri, self._service,
                                     lines[-1])
Exemple #3
0
class MasterListService(QtCore.QObject):
    '''
  A class to retrieve the ROS master list from a ROS service. The service
  will be determine using L{master_discovery_fkie.interface_finder.get_listmaster_service()}

  '''
    masterlist_signal = QtCore.Signal(str, str, list)
    '''@ivar: a signal with a list of the masters retrieved from the master_discovery service 'list_masters'.
  ParameterB{:} C{masteruri}, C{service name}, C{[L{master_discovery_fkie.ROSMaster}, ...]}'''
    masterlist_err_signal = QtCore.Signal(str, str)
    '''@ivar: this signal is emitted if an error while calling #list_masters' 
  service of master_discovery is failed.
  ParameterB{:} C{masteruri}, C{error}'''
    def __init__(self):
        QtCore.QObject.__init__(self)
        self.__serviceThreads = {}
        self._lock = threading.RLock()

    def stop(self):
        print "  Shutdown discovery listener..."
        for _, thread in self.__serviceThreads.iteritems():
            thread.join(3)
        print "  Discovery listener is off!"

    def retrieveMasterList(self, masteruri, wait=False):
        '''
    This method use the service 'list_masters' of the master_discovery to get 
    the list of discovered ROS master. The retrieved list will be emitted as 
    masterlist_signal.
    @param masteruri: the ROS master URI
    @type masteruri: C{str}
    @param wait: wait for the service
    @type wait: C{boolean}
    '''
        with self._lock:
            if not (self.__serviceThreads.has_key(masteruri)):
                upthread = MasterListThread(masteruri, wait)
                upthread.master_list_signal.connect(self._on_master_list)
                upthread.err_signal.connect(self._on_err)
                self.__serviceThreads[masteruri] = upthread
                upthread.start()

    def _on_master_list(self, masteruri, service_name, items):
        with self._lock:
            try:
                thread = self.__serviceThreads.pop(masteruri)
                del thread
            except KeyError:
                pass
        self.masterlist_signal.emit(masteruri, service_name, items)

    def _on_err(self, masteruri, msg):
        with self._lock:
            try:
                thread = self.__serviceThreads.pop(masteruri)
                del thread
            except KeyError:
                pass
        self.masterlist_err_signal.emit(masteruri, msg)
Exemple #4
0
class UpdateThread(QtCore.QObject, threading.Thread):
    '''
  A thread to retrieve the state about ROS master from remote discovery node and 
  publish it be sending a QT signal.
  '''
    update_signal = QtCore.Signal(MasterInfo)
    '''
  @ivar: update_signal is a signal, which is emitted, if a new 
  L{aster_discovery_fkie.MasterInfo} is retrieved.
  '''

    error_signal = QtCore.Signal(str, str)
    '''
  @ivar: error_signal is a signal (masteruri, error message), which is emitted, 
  if an error while retrieving a master info was occurred.
  '''
    def __init__(self, monitoruri, masteruri, delayed_exec=0., parent=None):
        '''
    @param masteruri: the URI of the remote ROS master
    @type masteruri: C{str}
    @param monitoruri: the URI of the monitor RPC interface of the master_discovery node
    @type monitoruri: C{str}
    @param delayed_exec: Delay the execution of the request for given seconds.
    @type delayed_exec: C{float}
    '''
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._monitoruri = monitoruri
        self._masteruri = masteruri
        self._delayed_exec = delayed_exec
        self.setDaemon(True)

    def run(self):
        '''
    '''
        try:
            delay = self._delayed_exec + 0.5 + random.random()
            #'print "wait request update", self._monitoruri, delay
            time.sleep(delay)
            #'print "request update", self._monitoruri
            socket.setdefaulttimeout(25)
            remote_monitor = xmlrpclib.ServerProxy(self._monitoruri)
            remote_info = remote_monitor.masterInfo()
            master_info = MasterInfo.from_list(remote_info)
            master_info.check_ts = time.time()
            #'print "request success", self._monitoruri
            self.update_signal.emit(master_info)
        except:
            import traceback
            #      print traceback.print_exc()
            formatted_lines = traceback.format_exc(1).splitlines()
            rospy.logwarn("Connection to %s failed:\n\t%s",
                          str(self._monitoruri), formatted_lines[-1])
            #'print "request failed", self._monitoruri
            self.error_signal.emit(self._masteruri, formatted_lines[-1])
        finally:
            if not socket is None:
                socket.setdefaulttimeout(None)
Exemple #5
0
class SupervisedPopen(QtCore.QObject, subprocess.Popen):
  '''
  The class overrides the subprocess.Popen and waits in a thread for its finish.
  If an error is printed out, it will be shown in a message dialog.
  '''
  error = QtCore.Signal(str, str, str)
  '''@ivar: the signal is emitted if error output was detected (id, decription, message)'''

  finished = QtCore.Signal(str)
  '''@ivar: the signal is emitted on exit (id)'''

  def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=subprocess.PIPE, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, id='', description=''):
    '''
    For arguments see https://docs.python.org/2/library/subprocess.html
    Additional arguments:
    :param id: the identification string of this object and title of the error message dialog 
    :type id: str
    :param description: the description string used as addiotional information 
                        in dialog if an error was occured
    :type description: str
    '''
    try:
      subprocess.Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
      QtCore.QObject.__init__(self)
      self._args = args
      self._id = id
      self._description = description
      self.error.connect(self.on_error)
      # wait for process to avoid 'defunct' processes
      thread = threading.Thread(target=self.supervise)
      thread.setDaemon(True)
      thread.start()
    except Exception as e:
      raise

#   def __del__(self):
#     print "Deleted:", self._description

  def supervise(self):
    # wait for process to avoid 'defunct' processes
    self.wait()
    result_err = self.stderr.read()
    if result_err:
      self.error.emit(self._id, self._description, result_err)
    self.finished.emit(self._id)

  def on_error(self, id, descr, msg):
    WarningMessageBox(QtGui.QMessageBox.Warning, id, '%s\n\n%s'%(descr, msg), ' '.join(self._args)).exec_()
class RawDataSelector(QGroupBox):

    dataSourceChanged = QtCore.Signal(int)

    def __init__(self, parent=None):
        super(RawDataSelector, self).__init__()
        self.parent = parent
        self.setTitle('Select bag file')

        # Init the components
        self.bag1RadioBtn = QRadioButton('ground truth')
        self.bag2RadioBtn = QRadioButton('camera')

        # connect the signals to the slots
        self.bag1RadioBtn.clicked.connect(self.btn1Clicked)
        self.bag2RadioBtn.clicked.connect(self.btn2Clicked)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(self.bag1RadioBtn)
        layout.addWidget(self.bag2RadioBtn)
        self.setLayout(layout)

    def btn1Clicked(self):
        '''
            "ground truth" is selected
        '''
        self.dataSourceChanged.emit(0)

    def btn2Clicked(self):
        '''
            "camera" is selected
        '''
        self.dataSourceChanged.emit(1)
Exemple #7
0
class AwRealField(QtWidgets.QLineEdit):

    value_updated = QtCore.Signal(float)

    def __init__(self, value):
        super(AwRealField, self).__init__()
        self.__value = value
        self.setText(str(self.__value))

    def update_value(self, value):
        if self.__value != value:
            self.__value = value
            self.value_updated.emit(value)

    def to_real(self, value):
        try:
            return float(value)
        except:
            return None

    def focusOutEvent(self, event):
        value = self.to_real(self.text())
        if value is not None:
            self.update_value(value)
        super(AwRealField, self).focusOutEvent(event)
Exemple #8
0
class SliderWithValue(QSlider, QtCore.QObject):
    slidervalue = QtCore.Signal(int)

    def __init__(self, x, parent=None):
        QtCore.QObject.__init__(self)
        self._x = x
        super(SliderWithValue, self).__init__(parent)
        self.stylesheet = """
        QSlider::groove:horizontal {
                background-color: #222;
                height: 20px;
        }
        QSlider::handle:horizontal {
            border: 1px #438f99;
            border-style: outset;
            margin: -2px 0;
            width: 10px;
            height: 20px;
            background-color: #438f99;
        }
        QSlider::sub-page:horizontal {
            background: #4B4B4B;
        }
        """
        self.setStyleSheet(self.stylesheet)

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, new_x):
        self._x = new_x
        self.slidervalue.emit(new_x)

    def paintEvent(self, event):
        QSlider.paintEvent(self, event)

        curr_value = str(self.value())
        round_value = round(float(curr_value), 4)

        painter = QPainter(self)
        painter.setPen(QPen(QtCore.Qt.white))

        font_metrics = QFontMetrics(self.font())
        font_width = font_metrics.boundingRect(str(round_value)).width()
        font_height = font_metrics.boundingRect(str(round_value)).height()

        rect = self.geometry()
        if self.orientation() == QtCore.Qt.Horizontal:
            horizontal_x_pos = rect.width() - font_width - 5
            horizontal_y_pos = rect.height() * 0.75

            painter.drawText(QtCore.QPoint(horizontal_x_pos, horizontal_y_pos),
                             str(round_value))
            self.x = self.value()
        else:
            pass
        painter.drawRect(rect)
class MasterListThread(QtCore.QObject, threading.Thread):
    '''
  A thread to to retrieve the list of discovered ROS master from master_discovery 
  service and publish it by sending a QT signal.
  '''
    master_list_signal = QtCore.Signal(str, str, list)
    err_signal = QtCore.Signal(str, str)

    def __init__(self, masteruri, wait, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._masteruri = masteruri
        self._wait = wait
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._masteruri:
            found = False
            service_names = interface_finder.get_listmaster_service(
                self._masteruri, self._wait)
            err_msg = ''
            for service_name in service_names:
                rospy.logdebug("service 'list_masters' found on %s as %s",
                               self._masteruri, service_name)
                if self._wait:
                    rospy.wait_for_service(service_name)
                socket.setdefaulttimeout(3)
                discoverMasters = rospy.ServiceProxy(service_name,
                                                     DiscoverMasters)
                try:
                    resp = discoverMasters()
                except rospy.ServiceException, e:
                    rospy.logwarn(
                        "ERROR Service call 'list_masters' failed: %s", str(e))
                    err_msg = ''.join(
                        [err_msg, '\n', service_name, ': ',
                         str(e)])
                else:
                    self.master_list_signal.emit(self._masteruri, service_name,
                                                 resp.masters)
                    if resp.masters:
                        found = True
                finally:
                    socket.setdefaulttimeout(None)
class PlotDialogWidget(QDialog):
    newPlotData = QtCore.Signal(object, object)

    def __init__(self, bagFiles, parent=None):
        super(PlotDialogWidget, self).__init__()
        self.parent = parent
        self.bagFiles = bagFiles
        self.setWindowTitle("Add new Graph")
        self.layout = QVBoxLayout()
        self.resize(600, 400)

        # init the components
        self.tabWidget = QTabWidget()
        self.rawDataTab = RawDataTab(bagFiles, self)
        self.compareTab = CompareDataTab(bagFiles, self)
        self.diffTab = DiffTab(bagFiles, self)
        self.tabWidget.addTab(self.rawDataTab, "Raw Data Graphs")
        self.tabWidget.addTab(self.compareTab, "Evaluation Graphs")
        self.tabWidget.addTab(self.diffTab, "Difference Graphs")
        self.layout.addWidget(self.tabWidget)

        # init the start button
        startBtn = QPushButton("Start")
        startBtn.clicked.connect(self.startPressed)
        self.layout.addWidget(startBtn)

        self.setLayout(self.layout)

    def startPressed(self):
        '''
            is called when the start button is clicked
            calls the function to get the data to plot
            dependent on what tab is selected            
        '''
        currentTab = self.tabWidget.currentIndex()
        try:

            if currentTab == 0:  # rawDataTab is active
                plotData, plotInfo = self.rawDataTab.getPlotData()
            elif currentTab == 1:  #
                plotData, plotInfo = self.compareTab.getPlotData()
            elif currentTab == 2:
                plotData, plotInfo = self.diffTab.getPlotData()

            # emit signal with data
            self.newPlotData.emit(plotData, plotInfo)

            # close dialog
            self.close()

        except Exception as e:
            msg_box = QMessageBox(QMessageBox.Critical, 'Error', str(e))
            msg_box.exec_()
class MasterRefreshThread(QtCore.QObject, threading.Thread):
    '''
  A thread to call the refresh service of master discovery.
  '''
    ok_signal = QtCore.Signal(str)
    err_signal = QtCore.Signal(str, str)

    def __init__(self, masteruri, wait, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._masteruri = masteruri
        self._wait = wait
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._masteruri:
            service_names = interface_finder.get_refresh_service(
                self._masteruri, self._wait)
            err_msg = ''
            for service_name in service_names:
                rospy.logdebug("service 'refresh' found on %s as %s",
                               self._masteruri, service_name)
                if self._wait:
                    rospy.wait_for_service(service_name)
                socket.setdefaulttimeout(3)
                refreshMasters = rospy.ServiceProxy(service_name,
                                                    std_srvs.srv.Empty)
                try:
                    resp = refreshMasters()
                    self.ok_signal.emit(self._masteruri)
                except rospy.ServiceException, e:
                    rospy.logwarn("ERROR Service call 'refresh' failed: %s",
                                  str(e))
                    self.err_signal.emit(
                        self._masteruri,
                        "ERROR Service call 'refresh' failed: %s" % err_msg)
                finally:
                    socket.setdefaulttimeout(None)
Exemple #12
0
class DelPlotDialog(QDialog):

    deletePressed = QtCore.Signal(int)

    def __init__(self, linesList, parent=None):
        super(DelPlotDialog, self).__init__()
        self.linesList = linesList  # list of the axes
        self.parent = parent

        # init the components:
        self.setWindowTitle("Delete Graph")
        self.resize(300, 200)
        self.linesListWidget = QListWidget(self)
        self.refreshList(self.linesList)
        self.__btn = QPushButton("Delete")

        # connect signals
        self.__btn.clicked.connect(self.btnPressed)

        # layout:
        layout = QVBoxLayout()
        layout.addWidget(self.linesListWidget)
        layout.addWidget(self.__btn)
        self.setLayout(layout)

    def refreshList(self, newLinesList):
        self.linesListWidget.clear()
        for line in newLinesList:
            label = line.get_label()
            self.linesListWidget.addItem(label)

    def btnPressed(self):
        '''
            this slot is called when the button 'Delete' is pressed
            and emits a signal so that the plot widget deletes the line
        '''
        if self.linesListWidget.count() == 0:
            # empty list
            return

        try:
            lineNr = self.linesListWidget.currentRow()
        except:
            message_module.showMessage("Please select a graph to delete.")

        # remove the line from the list
        self.linesListWidget.takeItem(lineNr)

        # emit the signal
        self.deletePressed.emit(lineNr)
class MasterStateTopic(QtCore.QObject):
    '''
  A class to receive the ROS master state updates from a ROS topic. The topic
  will be determine using L{master_discovery_fkie.interface_finder.get_changes_topic()}.
  '''
    state_signal = QtCore.Signal(MasterState)
    '''@ivar: a signal to inform the receiver about new master state. 
  Parameter: L{master_discovery_fkie.msg.MasterState}'''
    def registerByROS(self, masteruri, wait=False):
        '''
    This method creates a ROS subscriber to received the notifications of ROS 
    master updates. The retrieved messages will be emitted as state_signal.
    @param masteruri: the ROS master URI
    @type masteruri: C{str}
    @param wait: wait for the topic
    @type wait: C{boolean}
    '''
        found = False
        topic_names = interface_finder.get_changes_topic(masteruri, wait)
        self.stop()
        self.sub_changes = []
        for topic_name in topic_names:
            rospy.loginfo("listen for updates on %s", topic_name)
            sub_changes = rospy.Subscriber(topic_name, MasterState,
                                           self.handlerMasterStateMsg)
            self.sub_changes.append(sub_changes)
            found = True
        return found

    def stop(self):
        '''
    Unregister the subscribed topics
    '''
        if hasattr(self, 'sub_changes'):
            for s in self.sub_changes:
                try:
                    s.unregister()
                except Exception as e:
                    rospy.logwarn(
                        "Error while unregister master state topic %s" % e)
            del self.sub_changes

    def handlerMasterStateMsg(self, msg):
        '''
    The method to handle the received MasterState messages. The received message
    will be emitted as state_signal.
    @param msg: the received message
    @type msg: L{master_discovery_fkie.MasterState}
    '''
        self.state_signal.emit(msg)
Exemple #14
0
class LaunchServerUpdateThread(QtCore.QObject, threading.Thread):
    '''
  A thread to retrieve the list of pid and nodes from launch server and publish
  it by sending a QT signal.
  '''
    launch_server_signal = QtCore.Signal(str, int, list)
    error_signal = QtCore.Signal(str, str)

    def __init__(self, launch_serveruri, delayed_exec=0.0, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._launch_serveruri = launch_serveruri
        self._delayed_exec = delayed_exec
        self.setDaemon(True)

    def run(self):
        '''
    '''
        try:
            delay = self._delayed_exec + 0.5 + random.random()
            time.sleep(delay)
            socket.setdefaulttimeout(25)
            server = xmlrpclib.ServerProxy(self._launch_serveruri)
            _, _, pid = server.get_pid()  #_:=code, msg
            _, _, nodes = server.get_node_names()  #_:=code, msg
            self.launch_server_signal.emit(self._launch_serveruri, pid, nodes)
        except:
            import traceback
            #      print traceback.print_exc()
            formatted_lines = traceback.format_exc(1).splitlines()
            rospy.logwarn("Connection to launch server @ %s failed:\n\t%s",
                          str(self._launch_serveruri), formatted_lines[-1])
            #'print "request failed", self._monitoruri
            self.error_signal.emit(self._launch_serveruri, formatted_lines[-1])
        finally:
            if not socket is None:
                socket.setdefaulttimeout(None)
Exemple #15
0
class ServiceDialog(ParameterDialog):
  '''
  Adds a support for calling a service to the L{ParameterDialog}. The needed 
  input fields are created from the service request message type. The service 
  call is executed in a thread to avoid blocking GUI.
  '''
  service_resp_signal = QtCore.Signal(str, str)

  def __init__(self, service, parent=None):
    '''
    @param service: Service to call.
    @type service: L{ServiceInfo}
    '''
    self.service = service
    slots = service.get_service_class(True)._request_class.__slots__
    types = service.get_service_class()._request_class._slot_types
    ParameterDialog.__init__(self, self._params_from_slots(slots, types), buttons=QtGui.QDialogButtonBox.Close, parent=parent)
    self.setWindowTitle(''.join(['Call ', service.name]))
    self.service_resp_signal.connect(self._handle_resp)
    self.resize(450,300)
    if not slots:
      self.setText(''.join(['Wait for response ...']))
      thread = threading.Thread(target=self._callService)
      thread.setDaemon(True)
      thread.start()
    else:
      self.call_service_button = QtGui.QPushButton(self.tr("&Call"))
      self.call_service_button.clicked.connect(self._on_call_service)
      self.buttonBox.addButton(self.call_service_button, QtGui.QDialogButtonBox.ActionRole)
      self.hide_button = QtGui.QPushButton(self.tr("&Hide/Show output"))
      self.hide_button.clicked.connect(self._on_hide_output)
      self.buttonBox.addButton(self.hide_button, QtGui.QDialogButtonBox.ActionRole)
      self.hide_button.setVisible(False)
      self.showLoadSaveButtons()

  def _on_hide_output(self):
    self.setInfoActive(not self.info_field.isVisible())

  def _on_call_service(self):
    try:
      self.hide_button.setVisible(True)
      params = self.getKeywords()
      self.setText(''.join(['Wait for response ...']))
      thread = threading.Thread(target=self._callService, args=((params,)))
      thread.setDaemon(True)
      thread.start()
    except Exception, e:
      rospy.logwarn("Error while reading parameter for %s service: %s", str(self.service.name), unicode(e))
      self.setText(''.join(['Error while reading parameter:\n', unicode(e)]))
class MasterStatisticTopic(QtCore.QObject):
    '''
  A class to receive the connections statistics from a ROS topic. The topic
  will be determine using L{master_discovery_fkie.interface_finder.get_stats_topic()}
  '''
    stats_signal = QtCore.Signal(LinkStatesStamped)
    '''@ivar: a signal with a list of link states to discovered ROS masters.
  Paramter: L{master_discovery_fkie.msg.LinkStatesStamped}'''
    def registerByROS(self, masteruri, wait=False):
        '''
    This method creates a ROS subscriber to received the notifications of 
    connection updates. The retrieved messages will be emitted as stats_signal.
    @param masteruri: the ROS master URI
    @type masteruri: str
    @param wait: wait for the topic
    @type wait: boolean
    '''
        found = False
        self.stop()
        self.sub_stats = []
        topic_names = interface_finder.get_stats_topic(masteruri, wait)
        for topic_name in topic_names:
            rospy.loginfo("listen for connection statistics on %s", topic_name)
            sub_stats = rospy.Subscriber(topic_name, LinkStatesStamped,
                                         self.handlerMasterStatsMsg)
            self.sub_stats.append(sub_stats)
            found = True
        return found

    def stop(self):
        '''
    Unregister the subscribed topics.
    '''
        if hasattr(self, 'sub_stats'):
            for s in self.sub_stats:
                s.unregister()
            del self.sub_stats

    def handlerMasterStatsMsg(self, msg):
        '''
    The method to handle the received LinkStatesStamped messages. The received 
    message will be emitted as stats_signal.
    @param msg: the received message
    @type msg: L{master_discovery_fkie.LinkStatesStamped}
    '''
        self.stats_signal.emit(msg)
Exemple #17
0
class PathEditor(QtGui.QWidget):
    '''
  This is a path editor used as ItemDeligate in settings view. This editor
  provides an additional button for directory selection dialog.
  '''

    editing_finished_signal = QtCore.Signal()

    def __init__(self, path, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.path = path
        self._layout = QtGui.QHBoxLayout(self)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._layout.setSpacing(0)
        self._button = QtGui.QPushButton('...')
        self._button.setMaximumSize(QtCore.QSize(24, 20))
        self._button.clicked.connect(self._on_path_select_clicked)
        self._layout.addWidget(self._button)
        self._lineedit = QtGui.QLineEdit(path)
        self._lineedit.returnPressed.connect(self._on_editing_finished)
        self._layout.addWidget(self._lineedit)
        self.setLayout(self._layout)
        self.setFocusProxy(self._button)
        self.setAutoFillBackground(True)

    def _on_path_select_clicked(self):
        # Workaround for QtGui.QFileDialog.getExistingDirectory because it do not
        # select the configuration folder in the dialog
        self.dialog = QtGui.QFileDialog(self,
                                        caption='Select a new settings folder')
        self.dialog.setOption(QtGui.QFileDialog.HideNameFilterDetails, True)
        self.dialog.setFileMode(QtGui.QFileDialog.Directory)
        self.dialog.setDirectory(self.path)
        if self.dialog.exec_():
            fileNames = self.dialog.selectedFiles()
            path = fileNames[0]
            if os.path.isfile(path):
                path = os.path.basename(path)
            self._lineedit.setText(path)
            self.path = dir
            self.editing_finished_signal.emit()

    def _on_editing_finished(self):
        if self._lineedit.text():
            self.path = self._lineedit.text()
            self.editing_finished_signal.emit()
Exemple #18
0
class AwTextField(QtWidgets.QLineEdit):

    value_updated = QtCore.Signal(str)

    def __init__(self, value):
        super(AwTextField, self).__init__()
        self.__value = value
        self.setText(self.__value)

    def update_value(self, value):
        if self.__value != value:
            self.__value = value
            self.value_updated.emit(value)

    def focusOutEvent(self, event):
        self.update_value(self.text())
        super(AwTextField, self).focusOutEvent(event)
Exemple #19
0
class ExDoubleSlider(QSlider):

    """
    QSlider with double values instead of int values.
    """
    valueChanged = QtCore.Signal(float)

    def __init__(self, parent=None, orientation=None):
        if orientation is None:
            super(ExDoubleSlider, self).__init__(parent)
        else:
            super(ExDoubleSlider, self).__init__(orientation, parent)
        self.steps = 1000
        self._range = (0.0, 1.0)
        self.connect(self, SIGNAL('valueChanged(int)'), self._on_change)

    def setRange(self, vmin, vmax):
        if isinstance(vmin, (np.complex64, np.complex128)):
            vmin = np.abs(vmin)
        if isinstance(vmax, (np.complex64, np.complex128)):
            vmax = np.abs(vmax)
        self._range = (vmin, vmax)
        return super(ExDoubleSlider, self).setRange(0, self.steps)

    def setValue(self, value):
        vmin, vmax = self._range
        if isinstance(value, (np.complex64, np.complex128)):
            value = np.abs(value)
        try:
            v = int((value - vmin) * self.steps / (vmax - vmin))
        except (ZeroDivisionError, OverflowError, ValueError):
            v = 0
            self.setEnabled(False)
        return super(ExDoubleSlider, self).setValue(v)

    def value(self):
        v = super(ExDoubleSlider, self).value()
        return self._int2dbl(v)

    def _int2dbl(self, intval):
        vmin, vmax = self._range
        return vmin + intval * (vmax - vmin) / self.steps

    def _on_change(self, intval):
        dblval = self._int2dbl(intval)
        self.valueChanged.emit(dblval)
Exemple #20
0
class DeliverValuesThread(QtCore.QObject, threading.Thread):
    '''
  A thread to to deliver the value for given parameter to ROSparameter server 
  and publish the result by sending a QT signal.
  '''
    result_signal = QtCore.Signal(str, int, str, dict)

    def __init__(self, masteruri, params, parent=None):
        '''
    @param masteruri: The URI of the ROS parameter server
    @type masteruri: C{str}
    @param params: The dictinary the parameter name and their value, see U{http://www.ros.org/wiki/ROS/Parameter%20Server%20API#setParam}
    @type params: C{dict(str: value)}
    '''
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._masteruri = masteruri
        self._params = params
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._masteruri:
            result = dict()
            names = self._params.keys()
            for p in names:
                result[p] = None
            try:
                name = rospy.get_name()
                master = xmlrpclib.ServerProxy(self._masteruri)
                param_server_multi = xmlrpclib.MultiCall(master)
                for p, v in self._params.items():
                    param_server_multi.setParam(name, p, v)
                r = param_server_multi()
                for index, (code, msg, value) in enumerate(r):
                    result[names[index]] = (code, msg, value)
                self.result_signal.emit(self._masteruri, 1, '', result)
            except:
                import traceback
                err_msg = "Error while deliver parameter values to %s: %s" % (
                    self._masteruri, traceback.format_exc())
                rospy.logwarn(err_msg)
                #        lines = traceback.format_exc().splitlines()
                self.result_signal.emit(self._masteruri, -1, err_msg, result)
Exemple #21
0
class AdvancedAction(QtGui.QAction):
    # The overloaded signal can take an optional argument `advanced`
    _triggered = QtCore.Signal([], [bool], [bool, bool])

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        super().triggered[bool].connect(self._trigger)

    @property
    def triggered(self):
        return self._triggered

    def _trigger(self, checked):
        mods = QtGui.QApplication.keyboardModifiers()
        advanced = mods & QtCore.Qt.AltModifier == QtCore.Qt.AltModifier
        self._triggered[bool, bool].emit(checked, advanced)
        self._triggered[bool].emit(checked)
        self._triggered.emit()
Exemple #22
0
class AwTextListField(QtWidgets.QPlainTextEdit):

    value_updated = QtCore.Signal(list)

    def __init__(self, value):
        super(AwTextListField, self).__init__()
        self.__value = [v for v in value if v]
        self.setPlainText("\n".join(self.__value))

    def update_value(self, value):
        value = [v for v in value if v]
        if self.__value != value:
            self.__value = value
            self.value_updated.emit(value)

    def focusOutEvent(self, event):
        self.update_value(self.toPlainText().split("\n"))
        super(AwTextListField, self).focusOutEvent(event)
class OperationSelectorWidget(QGroupBox):
    
    selectionChanged = QtCore.Signal(str)
    
    operations = [
                  'true positive', 
                  'mismatch',
                  'false positive', 
                  'false negative', 
                  'precision',
                  'recall'
                  ]
    
    
    def __init__(self, parent=None):
        super(OperationSelectorWidget, self).__init__()
        self.parent = parent
        self.setTitle('Select Value')
        
        self.layout = QVBoxLayout()
        self.initRadioButtons()
        self._currentSelected = ''
        
        self.setLayout(self.layout)
        
        
    def initRadioButtons(self):
        for operation in self.operations:
            btn = QRadioButton(operation)
            btn.clicked.connect(lambda state, x=operation: self.switchOperation(x))  
            self.layout.addWidget(btn)
            
            
    def switchOperation(self, operation):
        self._currentSelected = operation
        self.selectionChanged.emit(operation)
        
        
    def getOperation(self):
        return self._currentSelected
        
            
        
Exemple #24
0
class RequestValuesThread(QtCore.QObject, threading.Thread):
    '''
  A thread to to retrieve the value for given parameter from ROSparameter server 
  and publish it by sending a QT signal.
  '''
    parameter_values_signal = QtCore.Signal(str, int, str, dict)

    def __init__(self, masteruri, params, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._masteruri = masteruri
        self._params = params
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._masteruri:
            result = dict()
            for p in self._params:
                result[p] = None
            try:
                name = rospy.get_name()
                master = xmlrpclib.ServerProxy(self._masteruri)
                param_server_multi = xmlrpclib.MultiCall(master)
                for p in self._params:
                    param_server_multi.getParam(name, p)
                r = param_server_multi()
                for index, (code, msg, value) in enumerate(r):
                    result[self._params[index]] = (code, msg, value)
                self.parameter_values_signal.emit(self._masteruri, 1, '',
                                                  result)
            except:
                import traceback
                #        err_msg = "Error while retrieve parameter values from %s: %s"%(self._masteruri, traceback.format_exc())
                #        rospy.logwarn(err_msg)
                #        lines = traceback.format_exc().splitlines()
                self.parameter_values_signal.emit(self._masteruri, -1,
                                                  traceback.format_exc(),
                                                  result)
Exemple #25
0
class AwBooleanField(QtWidgets.QCheckBox):

    value_updated = QtCore.Signal(bool)

    def __init__(self, value):
        super(AwBooleanField, self).__init__()
        self.__value = value
        self.setText(str(value))
        self.setCheckState(
            QtCore.Qt.Checked if value is True else QtCore.Qt.Unchecked)
        self.stateChanged.connect(self.update_event)
        self.setStyleSheet("width: 20px; height: 20px;")

    def update_value(self, value):
        if self.__value != value:
            self.__value = value
            self.value_updated.emit(value)

    def update_event(self, state):
        value = True if state == QtCore.Qt.Checked else False
        self.setText(str(value))
        self.update_value(value)
Exemple #26
0
class ColorButton(QtGui.QPushButton):

    colorChanged = QtCore.Signal([QtGui.QColor])

    def __init__(self, color=None, parent=None):
        super(ColorButton, self).__init__(parent)
        self.setMinimumWidth(50)
        if color is None:
            color = QtGui.QColor('gray')
        self.color = color
        self.clicked.connect(self.choose_color)

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        self._color = color
        self.colorChanged.emit(color)
        self.update()

    def choose_color(self):
        color = QtGui.QColorDialog.getColor(self.color, self)
        if color.isValid():
            self.color = color

    def paintEvent(self, event):
        super(ColorButton, self).paintEvent(event)
        padding = 5

        rect = event.rect()
        painter = QtGui.QPainter(self)
        painter.setBrush(QtGui.QBrush(self.color))
        painter.setPen(QtGui.QColor("#CECECE"))
        rect.adjust(
            padding, padding,
            -1-padding, -1-padding)
        painter.drawRect(rect)
Exemple #27
0
class MyComboBox(QtGui.QComboBox):

  remove_item_signal = QtCore.Signal(str)

  def __init__(self, parent=None):
    QtGui.QComboBox.__init__(self, parent=parent)
    self.parameter_description = None

  def keyPressEvent(self, event):
    key_mod = QtGui.QApplication.keyboardModifiers()
    if key_mod & QtCore.Qt.ShiftModifier and (event.key() == QtCore.Qt.Key_Delete):
      try:
        curr_text = self.currentText()
        if curr_text:
          for i in range(self.count()):
            if curr_text == self.itemText(i):
              self.removeItem(i)
              self.remove_item_signal.emit(curr_text)
              self.clearEditText()
      except:
        import traceback
        print traceback.format_exc(1)
    QtGui.QComboBox.keyPressEvent(self, event)
Exemple #28
0
class RequestListThread(QtCore.QObject, threading.Thread):
    '''
  A thread to to retrieve the parameter list from ROSparameter server 
  and publish it by sending a QT signal.
  '''
    parameter_list_signal = QtCore.Signal(str, int, str, list)

    def __init__(self, masteruri, ns, parent=None):
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self._masteruri = masteruri
        self._ns = ns
        self.setDaemon(True)

    def run(self):
        '''
    '''
        if self._masteruri:
            try:
                name = rospy.get_name()
                master = xmlrpclib.ServerProxy(self._masteruri)
                code, msg, params = master.getParamNames(name)
                #filter the parameter
                result = []
                for p in params:
                    if p.startswith(self._ns):
                        result.append(p)
                self.parameter_list_signal.emit(self._masteruri, code, msg,
                                                result)
            except:
                import traceback
                err_msg = "Error while retrieve the parameter list from %s: %s" % (
                    self._masteruri, traceback.format_exc())
                rospy.logwarn(err_msg)
                #        lines = traceback.format_exc().splitlines()
                self.parameter_list_signal.emit(self._masteruri, -1, err_msg,
                                                [])
Exemple #29
0
class PackagesThread(QtCore.QObject, threading.Thread):
    '''
  A thread to list all available ROS packages and
  publish there be sending a QT signal.
  '''
    packages = QtCore.Signal(dict)
    '''
  @ivar: packages is a signal, which is emitted, if a list with ROS packages was
  created {package : path}.
  '''
    def __init__(self):
        '''
    '''
        QtCore.QObject.__init__(self)
        threading.Thread.__init__(self)
        self.setDaemon(True)

    def run(self):
        '''
    '''
        try:
            # fill the input fields
            root_paths = [
                os.path.normpath(p)
                for p in os.getenv("ROS_PACKAGE_PATH").split(':')
            ]
            packages = {}
            for p in root_paths:
                ret = get_packages(p)
                packages = dict(ret.items() + packages.items())
            self.packages.emit(packages)
        except:
            import traceback
            formatted_lines = traceback.format_exc().splitlines()
            rospy.logwarn("Error while list packages:\n\t%s",
                          formatted_lines[-1])
class EchoDialog(QtGui.QDialog):

    MESSAGE_LINE_LIMIT = 128
    MESSAGE_HZ_LIMIT = 10
    MAX_DISPLAY_MSGS = 25
    STATISTIC_QUEUE_LEN = 5000
    '''
  This dialog shows the output of a topic.
  '''

    finished_signal = QtCore.Signal(str)
    '''
  finished_signal has as parameter the name of the topic and is emitted, if this
  dialog was closed.
  '''

    msg_signal = QtCore.Signal(object, bool)
    '''
  msg_signal is a signal, which is emitted, if a new message was received.
  '''

    text_hz_signal = QtCore.Signal(str)
    text_signal = QtCore.Signal(str)
    '''
  text_signal is a signal, which is emitted, if a new text to display was received.
  '''

    text_error_signal = QtCore.Signal(str)
    '''
  text_error_signal is a signal, which is emitted, if a new error text to display was received.
  '''

    request_pw = QtCore.Signal(object)

    def __init__(self,
                 topic,
                 msg_type,
                 show_only_rate=False,
                 masteruri=None,
                 use_ssh=False,
                 parent=None):
        '''
    Creates an input dialog.
    @param topic: the name of the topic
    @type topic: C{str}
    @param msg_type: the type of the topic
    @type msg_type: C{str}
    @raise Exception: if no topic class was found for the given type
    '''
        QtGui.QDialog.__init__(self, parent=parent)
        self._masteruri = masteruri
        masteruri_str = '' if masteruri is None else '[%s]' % masteruri
        self.setObjectName(' - '.join(['EchoDialog', topic, masteruri_str]))
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
        self.setWindowFlags(QtCore.Qt.Window)
        self.setWindowTitle('%s %s %s' %
                            ('Echo --- ' if not show_only_rate else 'Hz --- ',
                             topic, masteruri_str))
        self.resize(728, 512)
        self.verticalLayout = QtGui.QVBoxLayout(self)
        self.verticalLayout.setObjectName("verticalLayout")
        self.verticalLayout.setContentsMargins(1, 1, 1, 1)
        self.mIcon = QtGui.QIcon(":/icons/crystal_clear_prop_run_echo.png")
        self.setWindowIcon(self.mIcon)

        self.topic = topic
        self.show_only_rate = show_only_rate
        self.lock = threading.RLock()
        self.last_printed_count = 0
        self.msg_t0 = -1.
        self.msg_tn = 0
        self.times = []

        self.message_count = 0
        self._rate_message = ''
        self._scrapped_msgs = 0
        self._scrapped_msgs_sl = 0

        self._last_received_ts = 0
        self.receiving_hz = self.MESSAGE_HZ_LIMIT
        self.line_limit = self.MESSAGE_LINE_LIMIT

        self.field_filter_fn = None

        options = QtGui.QWidget(self)
        if not show_only_rate:
            hLayout = QtGui.QHBoxLayout(options)
            hLayout.setContentsMargins(1, 1, 1, 1)
            self.no_str_checkbox = no_str_checkbox = QtGui.QCheckBox(
                'Hide strings')
            no_str_checkbox.toggled.connect(self.on_no_str_checkbox_toggled)
            hLayout.addWidget(no_str_checkbox)
            self.no_arr_checkbox = no_arr_checkbox = QtGui.QCheckBox(
                'Hide arrays')
            no_arr_checkbox.toggled.connect(self.on_no_arr_checkbox_toggled)
            hLayout.addWidget(no_arr_checkbox)
            self.combobox_reduce_ch = QtGui.QComboBox(self)
            self.combobox_reduce_ch.addItems(
                [str(self.MESSAGE_LINE_LIMIT), '0', '80', '256', '1024'])
            self.combobox_reduce_ch.activated[str].connect(
                self.combobox_reduce_ch_activated)
            self.combobox_reduce_ch.setEditable(True)
            self.combobox_reduce_ch.setToolTip(
                "Set maximum line width. 0 disables the limit.")
            hLayout.addWidget(self.combobox_reduce_ch)
            #      reduce_ch_label = QtGui.QLabel('ch', self)
            #      hLayout.addWidget(reduce_ch_label)
            # add spacer
            spacerItem = QtGui.QSpacerItem(515, 20,
                                           QtGui.QSizePolicy.Expanding,
                                           QtGui.QSizePolicy.Minimum)
            hLayout.addItem(spacerItem)
            # add combobox for displaying frequency of messages
            self.combobox_displ_hz = QtGui.QComboBox(self)
            self.combobox_displ_hz.addItems([
                str(self.MESSAGE_HZ_LIMIT), '0', '0.1', '1', '50', '100',
                '1000'
            ])
            self.combobox_displ_hz.activated[str].connect(
                self.on_combobox_hz_activated)
            self.combobox_displ_hz.setEditable(True)
            hLayout.addWidget(self.combobox_displ_hz)
            displ_hz_label = QtGui.QLabel('Hz', self)
            hLayout.addWidget(displ_hz_label)
            # add combobox for count of displayed messages
            self.combobox_msgs_count = QtGui.QComboBox(self)
            self.combobox_msgs_count.addItems(
                [str(self.MAX_DISPLAY_MSGS), '0', '50', '100'])
            self.combobox_msgs_count.activated[str].connect(
                self.on_combobox_count_activated)
            self.combobox_msgs_count.setEditable(True)
            self.combobox_msgs_count.setToolTip(
                "Set maximum displayed message count. 0 disables the limit.")
            hLayout.addWidget(self.combobox_msgs_count)
            displ_count_label = QtGui.QLabel('#', self)
            hLayout.addWidget(displ_count_label)
            # add topic control button for unsubscribe and subscribe
            self.topic_control_button = QtGui.QToolButton(self)
            self.topic_control_button.setText('stop')
            self.topic_control_button.setIcon(
                QtGui.QIcon(':/icons/deleket_deviantart_stop.png'))
            self.topic_control_button.clicked.connect(
                self.on_topic_control_btn_clicked)
            hLayout.addWidget(self.topic_control_button)
            # add clear button
            clearButton = QtGui.QToolButton(self)
            clearButton.setText('clear')
            clearButton.clicked.connect(self.on_clear_btn_clicked)
            hLayout.addWidget(clearButton)
            self.verticalLayout.addWidget(options)

        self.display = QtGui.QTextBrowser(self)
        self.display.setReadOnly(True)
        self.verticalLayout.addWidget(self.display)
        self.display.document().setMaximumBlockCount(500)
        self.max_displayed_msgs = self.MAX_DISPLAY_MSGS
        self._blocks_in_msg = None
        self.display.setOpenLinks(False)
        self.display.anchorClicked.connect(self._on_display_anchorClicked)

        self.status_label = QtGui.QLabel('0 messages', self)
        self.verticalLayout.addWidget(self.status_label)

        # subscribe to the topic
        errmsg = ''
        try:
            self.__msg_class = message.get_message_class(msg_type)
            if not self.__msg_class:
                errmsg = "Cannot load message class for [%s]. Did you build messages?" % msg_type
#        raise Exception("Cannot load message class for [%s]. Did you build messages?"%msg_type)
        except Exception as e:
            self.__msg_class = None
            errmsg = "Cannot load message class for [%s]. Did you build messagest?\nError: %s" % (
                msg_type, e)
#      raise Exception("Cannot load message class for [%s]. Did you build messagest?\nError: %s"%(msg_type, e))
# variables for Subscriber
        self.msg_signal.connect(self._append_message)
        self.sub = None

        # vairables for SSH connection
        self.ssh_output_file = None
        self.ssh_error_file = None
        self.ssh_input_file = None
        self.text_signal.connect(self._append_text)
        self.text_hz_signal.connect(self._append_text_hz)
        self._current_msg = ''
        self._current_errmsg = ''
        self.text_error_signal.connect(self._append_error_text)

        # decide, which connection to open
        if use_ssh:
            self.__msg_class = None
            self._on_display_anchorClicked(QtCore.QUrl(self._masteruri))
        elif self.__msg_class is None:
            errtxt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">\n%s</pre>' % (
                errmsg)
            self.display.setText('<a href="%s">open using SSH</a>' %
                                 (masteruri))
            self.display.append(errtxt)
        else:
            self.sub = rospy.Subscriber(self.topic, self.__msg_class,
                                        self._msg_handle)

        self.print_hz_timer = QtCore.QTimer()
        self.print_hz_timer.timeout.connect(self._on_calc_hz)
        self.print_hz_timer.start(1000)

#    print "======== create", self.objectName()
#
#  def __del__(self):
#    print "******* destroy", self.objectName()

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

    def closeEvent(self, event):
        if not self.sub is None:
            self.sub.unregister()
            del self.sub
        try:
            self.ssh_output_file.close()
            self.ssh_error_file.close()
            # send Ctrl+C to remote process
            self.ssh_input_file.write('%s\n' % chr(3))
            self.ssh_input_file.close()
        except:
            pass
        self.finished_signal.emit(self.topic)
        if self.parent() is None:
            QtGui.QApplication.quit()
#    else:
#      self.setParent(None)

    def create_field_filter(self, echo_nostr, echo_noarr):
        def field_filter(val):
            try:
                #        fields = val.__slots__
                #        field_types = val._slot_types
                for f, t in zip(val.__slots__, val._slot_types):
                    if echo_noarr and '[' in t:
                        continue
                    elif echo_nostr and 'string' in t:
                        continue
                    yield f
            except:
                pass

        return field_filter

    def on_no_str_checkbox_toggled(self, state):
        self.field_filter_fn = self.create_field_filter(
            state, self.no_arr_checkbox.isChecked())

    def on_no_arr_checkbox_toggled(self, state):
        self.field_filter_fn = self.create_field_filter(
            self.no_str_checkbox.isChecked(), state)

    def combobox_reduce_ch_activated(self, ch_txt):
        try:
            self.line_limit = int(ch_txt)
        except ValueError:
            try:
                self.line_limit = float(ch_txt)
            except ValueError:
                self.combobox_reduce_ch.setEditText(str(self.line_limit))

    def on_combobox_hz_activated(self, hz_txt):
        try:
            self.receiving_hz = int(hz_txt)
        except ValueError:
            try:
                self.receiving_hz = float(hz_txt)
            except ValueError:
                self.combobox_displ_hz.setEditText(str(self.receiving_hz))

    def on_combobox_count_activated(self, count_txt):
        try:
            self.max_displayed_msgs = int(count_txt)
            self._blocks_in_msg = None
        except ValueError:
            self.combobox_msgs_count.setEditText(str(self.max_displayed_msgs))

    def on_clear_btn_clicked(self):
        self.display.clear()
        with self.lock:
            self.message_count = 0
            self._scrapped_msgs = 0
            del self.times[:]

    def on_topic_control_btn_clicked(self):
        try:
            if self.sub is None and self.ssh_output_file is None:
                if self.__msg_class:
                    self.sub = rospy.Subscriber(self.topic, self.__msg_class,
                                                self._msg_handle)
                else:
                    self._on_display_anchorClicked(QtCore.QUrl(
                        self._masteruri))
                self.topic_control_button.setText('stop')
                self.topic_control_button.setIcon(
                    QtGui.QIcon(':/icons/deleket_deviantart_stop.png'))
            else:
                if not self.sub is None:
                    self.sub.unregister()
                    self.sub = None
                elif not self.ssh_output_file is None:
                    self.ssh_output_file.close()
                    self.ssh_error_file.close()
                    self.ssh_output_file = None
                self.topic_control_button.setText('play')
                self.topic_control_button.setIcon(
                    QtGui.QIcon(':/icons/deleket_deviantart_play.png'))
                self.no_str_checkbox.setEnabled(True)
                self.no_arr_checkbox.setEnabled(True)
        except Exception as e:
            rospy.logwarn('Error while stop/play echo for topic %s: %s' %
                          (self.topic, e))

    def _msg_handle(self, data):
        self.msg_signal.emit(data,
                             (data._connection_header['latching'] != '0'))

    def _append_message(self, msg, latched):
        '''
    Adds a label to the dialog's layout and shows the given text.
    @param msg: the text to add to the dialog
    @type msg: message object
    '''
        current_time = time.time()
        self._count_messages(current_time)
        # skip messages, if they are received often then MESSAGE_HZ_LIMIT
        if self._last_received_ts != 0 and self.receiving_hz != 0:
            if not latched and current_time - self._last_received_ts < 1.0 / self.receiving_hz:
                self._scrapped_msgs += 1
                self._scrapped_msgs_sl += 1
                return
        self._last_received_ts = current_time
        if not self.show_only_rate:
            # convert message to string and reduce line width to current limit
            msg = message.strify_message(msg,
                                         field_filter=self.field_filter_fn)
            if isinstance(msg, tuple):
                msg = msg[0]
            msg = self._trim_width(msg)
            msg = msg.replace('<', '&lt;').replace('>', '&gt;')
            # create a notification about scrapped messages
            if self._scrapped_msgs_sl > 0:
                txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">scrapped %s message because of Hz-settings</pre>' % self._scrapped_msgs_sl
                self.display.append(txt)
                self._scrapped_msgs_sl = 0
            txt = '<pre style="background-color:#FFFCCC; font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (
                datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), msg)
            # set the count of the displayed messages on receiving the first message
            self._update_max_msg_count(txt)
            self.display.append(txt)
        self._print_status()

    def _count_messages(self, ts=time.time()):
        '''
    Counts the received messages. Call this method only on receive message.
    '''
        current_time = ts
        with self.lock:
            # time reset
            if self.msg_t0 < 0 or self.msg_t0 > current_time:
                self.msg_t0 = current_time
                self.msg_tn = current_time
                self.times = []
            else:
                self.times.append(current_time - self.msg_tn)
                self.msg_tn = current_time
            # keep only statistics for the last 5000 messages so as not to run out of memory
            if len(self.times) > self.STATISTIC_QUEUE_LEN:
                self.times.pop(0)
            self.message_count += 1

    def _trim_width(self, msg):
        '''
    reduce line width to current limit
    :param msg: the message
    :type msg: str
    :return: trimmed message
    '''
        result = msg
        if self.line_limit != 0:
            a = ''
            for l in msg.splitlines():
                a = a + (l if len(l) <= self.line_limit else
                         l[0:self.line_limit - 3] + '...') + '\n'
            result = a
        return result

    def _update_max_msg_count(self, txt):
        '''
    set the count of the displayed messages on receiving the first message
    :param txt: text of the message, which will be added to the document
    :type txt: str
    '''
        if self._blocks_in_msg is None:
            td = QtGui.QTextDocument(txt)
            self._blocks_in_msg = td.blockCount()
            self.display.document().setMaximumBlockCount(
                self._blocks_in_msg * self.max_displayed_msgs)

    def _on_calc_hz(self):
        if rospy.is_shutdown():
            self.close()
            return
        if self.message_count == self.last_printed_count:
            return
        with self.lock:
            # the code from ROS rostopic
            n = len(self.times)
            if n < 2:
                return
            mean = sum(self.times) / n
            rate = 1. / mean if mean > 0. else 0
            #std dev
            std_dev = math.sqrt(sum((x - mean)**2 for x in self.times) / n)
            # min and max
            max_delta = max(self.times)
            min_delta = min(self.times)
            self.last_printed_count = self.message_count
            self._rate_message = "average rate: %.3f\tmin: %.3fs   max: %.3fs   std dev: %.5fs   window: %s" % (
                rate, min_delta, max_delta, std_dev, n + 1)
            if self._scrapped_msgs > 0:
                self._rate_message += " --- scrapped msgs: %s" % self._scrapped_msgs
            self._print_status()
            if self.show_only_rate:
                self.display.append(self._rate_message)

    def _print_status(self):
        self.status_label.setText('%s messages   %s' %
                                  (self.message_count, self._rate_message))

    def _append_text(self, text):
        '''
    Append echo text received through the SSH.
    '''
        with self.lock:
            self._current_msg += text
            if self._current_msg.find('---') != -1:
                messages = self._current_msg.split('---')
                for m in messages[:-1]:
                    current_time = time.time()
                    self._count_messages(current_time)
                    # limit the displayed text width
                    m = self._trim_width(m)
                    txt = '<pre style="background-color:#FFFCCC; font-family:Fixedsys,Courier; padding:10px;">---------- %s --------------------\n%s</pre>' % (
                        datetime.now().strftime("%d.%m.%Y %H:%M:%S.%f"), m)
                    # set the count of the displayed messages on receiving the first message
                    self._update_max_msg_count(txt)
                    self.display.append(txt)
                self._current_msg = messages[-1]
            self._print_status()

    def _append_error_text(self, text):
        '''
    Append error text received through the SSH.
    '''
        with self.lock:
            self._current_errmsg += text
            if self._current_errmsg.find('\n') != -1:
                messages = self._current_errmsg.split('\n')
                for m in messages[:-1]:
                    txt = '<pre style="color:red; font-family:Fixedsys,Courier,monospace; padding:10px;">%s</pre>' % m
                    self.display.append(txt)
                self._current_errmsg = messages[-1]

    def _append_text_hz(self, text):
        '''
    Append text received through the SSH for hz view.
    '''
        with self.lock:
            self._current_msg += text
            if self._current_msg.find('\n') != -1:
                messages = self._current_msg.split('\n')
                for m in messages[:-1]:
                    txt = '<div style="font-family:Fixedsys,Courier;">%s</div>' % (
                        m)
                    self.display.append(txt)
                self._current_msg = messages[-1]

    def _on_display_anchorClicked(self, url, user=None, pw=None):
        try:
            ok = False
            if self.show_only_rate:
                self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh(
                ).ssh_exec(url.host(), ['rostopic hz %s' % (self.topic)],
                           user,
                           pw,
                           auto_pw_request=True,
                           get_pty=True)
                self.status_label.setText('connected to %s over SSH' %
                                          url.host())
            else:
                self.combobox_displ_hz.setEnabled(False)
                nostr = '--nostr' if self.no_str_checkbox.isChecked() else ''
                noarr = '--noarr' if self.no_arr_checkbox.isChecked() else ''
                self.ssh_input_file, self.ssh_output_file, self.ssh_error_file, ok = nm.ssh(
                ).ssh_exec(
                    url.host(),
                    ['rostopic echo %s %s %s' % (nostr, noarr, self.topic)],
                    user,
                    pw,
                    auto_pw_request=True,
                    get_pty=True)
            if ok:
                self.display.clear()
                target = self._read_output_hz if self.show_only_rate else self._read_output
                thread = threading.Thread(target=target,
                                          args=((self.ssh_output_file, )))
                thread.setDaemon(True)
                thread.start()
                thread = threading.Thread(target=self._read_error,
                                          args=((self.ssh_error_file, )))
                thread.setDaemon(True)
                thread.start()
            elif self.ssh_output_file:
                self.ssh_output_file.close()
                self.ssh_error_file.close()
        except Exception as e:
            self._append_error_text('%s\n' % e)
#      import traceback
#      print traceback.format_exc()

    def _read_output_hz(self, output_file):
        try:
            while not output_file.closed:
                text = output_file.read(1)
                if text:
                    self.text_hz_signal.emit(text)
        except:
            pass
#      import traceback
#      print traceback.format_exc()

    def _read_output(self, output_file):
        while not output_file.closed:
            text = output_file.read(1)
            if text:
                self.text_signal.emit(text)

    def _read_error(self, error_file):
        try:
            while not error_file.closed:
                text = error_file.read(1)
                if text:
                    self.text_error_signal.emit(text)
        except:
            pass