class SignalStream(QObject): '''SignalStream is a file-like object that emits a text signal on writing This class is used to provide threadsafe communication of data to the GUI. A SignalStream can be used in place of sys.stdout and the instance's write_signal can be connected to a slot that processes the text to where it ought to go. Since signals and slots are threadsafe, this lets you pass text from anywhere to anywhere reasonably safely SignalStream uses some intelligent buffering to prevent the signalstorm that happened the first time I used it. Signal emit only happens when flush() is called - so an application can force a flush - but in order to make sure that happens reasonable often SignalStream can be initialized with a QTimer on an interval (default: 100ms) and the QTimer will make sure to call flush() every 100ms. ''' write_signal = pyqtSignal(str) def __init__(self, interval_ms=100): '''Create a SignalStream that emits text at least every interval_ms''' super(SignalStream, self).__init__() self.mutex = QMutex() self.data = [] self.thread = QThread() self.pbar_timer = QTimer() self.pbar_timer.moveToThread(self.thread) self.pbar_timer.setInterval(interval_ms) self.pbar_timer.timeout.connect(self.flush) self.thread.started.connect(self.pbar_timer.start) self.thread.start() def __del__(self): self.thread.quit() self.thread.wait() def write(self, m): '''Add the message in m to this stream's cache''' locker = QMutexLocker(self.mutex) self.data.append(m) @pyqtSlot() def flush(self): '''Write all data in the stream and clear the stream's cache''' locker = QMutexLocker(self.mutex) if self.data: self.write_signal.emit(''.join(self.data)) self.data = [] def set_interval(self, interval_ms): '''Alter the pbar_timer period''' self.pbar_timer.setInteval(interval_ms)