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])
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)
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)
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)
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)
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)
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)
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)
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)
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()
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)
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)
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)
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()
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
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)
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)
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)
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)
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, [])
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('<', '<').replace('>', '>') # 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