def __init__(self): # gui self.app = QApplication([]) self.main_window = MainWindow(controller=self) # device self.device = Device() # fps stats self.fps_timer = QTimer() self.fps_timer.timeout.connect(self.update_ui_fps) self.spf = 1 # seconds per frame self.timestamp_last_capture = 0 # acquisition thread self.continuous_acquisition = False self.worker_wait_condition = QWaitCondition() self.acquisition_worker = AcquisitionWorker(self.worker_wait_condition, device=self.device) self.acquisition_thread = QThread() self.acquisition_worker.moveToThread(self.acquisition_thread) self.acquisition_thread.started.connect(self.acquisition_worker.run) self.acquisition_worker.finished.connect(self.acquisition_thread.quit) # self.acquisition_worker.finished.connect(self.acquisition_thread.deleteLater) # self.acquisition_thread.finished.connect(self.acquisition_worker.deleteLater) self.acquisition_worker.data_ready.connect(self.data_ready_callback) self.acquisition_thread.start() # default timebase self.set_timebase("20 ms") # on app exit self.app.aboutToQuit.connect(self.on_app_exit)
def __init__(self, parent=None): super(FortuneThread, self).__init__(parent) self.quit = False self.hostName = '' self.cond = QWaitCondition() self.mutex = QMutex() self.port = 0
def __init__(self, parent=None): super(RenderThread, self).__init__(parent) self.mutex = QMutex() self.condition = QWaitCondition() self.centerX = 0.0 self.centerY = 0.0 self.scaleFactor = 0.0 self.resultSize = QSize() self.colormap = [] self.restart = False self.abort = False for i in range(RenderThread.ColormapSize): self.colormap.append(self.rgbFromWaveLength(380.0 + (i * 400.0 / RenderThread.ColormapSize)))
class RenderThread(QThread): ColormapSize = 512 renderedImage = Signal(QImage, float) def __init__(self, parent=None): super(RenderThread, self).__init__(parent) self.mutex = QMutex() self.condition = QWaitCondition() self.centerX = 0.0 self.centerY = 0.0 self.scaleFactor = 0.0 self.resultSize = QSize() self.colormap = [] self.restart = False self.abort = False for i in range(RenderThread.ColormapSize): self.colormap.append( self.rgbFromWaveLength(380.0 + (i * 400.0 / RenderThread.ColormapSize))) def stop(self): self.mutex.lock() self.abort = True self.condition.wakeOne() self.mutex.unlock() self.wait(2000) def render(self, centerX, centerY, scaleFactor, resultSize): locker = QMutexLocker(self.mutex) self.centerX = centerX self.centerY = centerY self.scaleFactor = scaleFactor self.resultSize = resultSize if not self.isRunning(): self.start(QThread.LowPriority) else: self.restart = True self.condition.wakeOne() def run(self): while True: self.mutex.lock() resultSize = self.resultSize scaleFactor = self.scaleFactor centerX = self.centerX centerY = self.centerY self.mutex.unlock() halfWidth = resultSize.width() // 2 halfHeight = resultSize.height() // 2 image = QImage(resultSize, QImage.Format_RGB32) NumPasses = 8 curpass = 0 while curpass < NumPasses: MaxIterations = (1 << (2 * curpass + 6)) + 32 Limit = 4 allBlack = True for y in range(-halfHeight, halfHeight): if self.restart: break if self.abort: return ay = 1j * (centerY + (y * scaleFactor)) for x in range(-halfWidth, halfWidth): c0 = centerX + (x * scaleFactor) + ay c = c0 numIterations = 0 while numIterations < MaxIterations: numIterations += 1 c = c * c + c0 if abs(c) >= Limit: break numIterations += 1 c = c * c + c0 if abs(c) >= Limit: break numIterations += 1 c = c * c + c0 if abs(c) >= Limit: break numIterations += 1 c = c * c + c0 if abs(c) >= Limit: break if numIterations < MaxIterations: image.setPixel( x + halfWidth, y + halfHeight, self.colormap[numIterations % RenderThread.ColormapSize]) allBlack = False else: image.setPixel(x + halfWidth, y + halfHeight, qRgb(0, 0, 0)) if allBlack and curpass == 0: curpass = 4 else: if not self.restart: self.renderedImage.emit(image, scaleFactor) curpass += 1 self.mutex.lock() if not self.restart: self.condition.wait(self.mutex) self.restart = False self.mutex.unlock() def rgbFromWaveLength(self, wave): r = 0.0 g = 0.0 b = 0.0 if wave >= 380.0 and wave <= 440.0: r = -1.0 * (wave - 440.0) / (440.0 - 380.0) b = 1.0 elif wave >= 440.0 and wave <= 490.0: g = (wave - 440.0) / (490.0 - 440.0) b = 1.0 elif wave >= 490.0 and wave <= 510.0: g = 1.0 b = -1.0 * (wave - 510.0) / (510.0 - 490.0) elif wave >= 510.0 and wave <= 580.0: r = (wave - 510.0) / (580.0 - 510.0) g = 1.0 elif wave >= 580.0 and wave <= 645.0: r = 1.0 g = -1.0 * (wave - 645.0) / (645.0 - 580.0) elif wave >= 645.0 and wave <= 780.0: r = 1.0 s = 1.0 if wave > 700.0: s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0) elif wave < 420.0: s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0) r = pow(r * s, 0.8) g = pow(g * s, 0.8) b = pow(b * s, 0.8) return qRgb(r * 255, g * 255, b * 255)
class Controller: def __init__(self): # gui self.app = QApplication([]) self.main_window = MainWindow(controller=self) # device self.device = Device() # fps stats self.fps_timer = QTimer() self.fps_timer.timeout.connect(self.update_ui_fps) self.spf = 1 # seconds per frame self.timestamp_last_capture = 0 # acquisition thread self.continuous_acquisition = False self.worker_wait_condition = QWaitCondition() self.acquisition_worker = AcquisitionWorker(self.worker_wait_condition, device=self.device) self.acquisition_thread = QThread() self.acquisition_worker.moveToThread(self.acquisition_thread) self.acquisition_thread.started.connect(self.acquisition_worker.run) self.acquisition_worker.finished.connect(self.acquisition_thread.quit) # self.acquisition_worker.finished.connect(self.acquisition_thread.deleteLater) # self.acquisition_thread.finished.connect(self.acquisition_worker.deleteLater) self.acquisition_worker.data_ready.connect(self.data_ready_callback) self.acquisition_thread.start() # default timebase self.set_timebase("20 ms") # on app exit self.app.aboutToQuit.connect(self.on_app_exit) def run_app(self): self.main_window.show() return self.app.exec_() def get_ports_names(self): return [p.device for p in serial.tools.list_ports.comports()] def update_ui_fps(self): fps = 1 / self.spf self.main_window.control_panel.stats_panel.fps_label.setText( f"{fps:.2f} fps") def set_timebase(self, timebase: str): # send timebase to device self.device.timebase = timebase if self.is_device_connected(): self.device.write_timebase() # adjust timebase in the screen seconds_per_sample = (float(timebase.split()[0]) / 10 * { "ms": 1e-3, "us": 1e-6 }[timebase.split()[1]]) self.data_time_array = (np.arange(0, self.device.BUFFER_SIZE) * seconds_per_sample) self.main_window.screen.setXRange(0, self.device.BUFFER_SIZE * seconds_per_sample, padding=0.02) self.main_window.screen.setYRange(0, 5) def set_trigger_state(self, on): self.device.trigger_on = on if self.is_device_connected(): self.device.write_trigger_state() def set_trigger_slope(self, slope): self.device.trigger_slope = slope if self.is_device_connected(): self.device.write_trigger_slope() def connect_to_device(self, port): if port == "": QMessageBox.about( self.main_window, "Connection failed", "Could not connect to device. No port is selected.", ) elif port not in self.get_ports_names(): QMessageBox.about( self.main_window, "Connection failed", f"Could not connect to device. Port {port} not available. Refresh and try again.", ) else: self.device.connect(port) def disconnect_device(self): self.device.disconnect() def is_device_connected(self): return self.device.is_connected() def show_no_connection_message(self): QMessageBox.about( self.main_window, "Device not connected", "No device is connected. Connect a device first.", ) def oscilloscope_single_run(self): if self.device.is_connected(): self.continuous_acquisition = False self.device.clean_buffers() self.worker_wait_condition.notify_one() return True else: self.show_no_connection_message() return False def oscilloscope_continuous_run(self): if self.device.is_connected(): self.timestamp_last_capture = time.time() self.spf = 1 self.fps_timer.start(500) self.continuous_acquisition = True self.device.clean_buffers() self.worker_wait_condition.notify_one() return True else: self.show_no_connection_message() return False def oscilloscope_stop(self): self.continuous_acquisition = False self.fps_timer.stop() def data_ready_callback(self): curr_time = time.time() self.spf = 0.9 * (curr_time - self.timestamp_last_capture) + 0.1 * self.spf self.timestamp_last_capture = curr_time self.main_window.screen.update_ch(self.data_time_array, self.acquisition_worker.data) if self.continuous_acquisition == True: self.worker_wait_condition.notify_one() def on_app_exit(self): print("exiting")
class FortuneThread(QThread): newFortune = Signal(str) error = Signal(int, str) def __init__(self, parent=None): super(FortuneThread, self).__init__(parent) self.quit = False self.hostName = '' self.cond = QWaitCondition() self.mutex = QMutex() self.port = 0 def __del__(self): self.mutex.lock() self.quit = True self.cond.wakeOne() self.mutex.unlock() self.wait() def requestNewFortune(self, hostname, port): locker = QMutexLocker(self.mutex) self.hostName = hostname self.port = port if not self.isRunning(): self.start() else: self.cond.wakeOne() def run(self): self.mutex.lock() serverName = self.hostName serverPort = self.port self.mutex.unlock() while not self.quit: Timeout = 5 * 1000 socket = QTcpSocket() socket.connectToHost(serverName, serverPort) if not socket.waitForConnected(Timeout): self.error.emit(socket.error(), socket.errorString()) return while socket.bytesAvailable() < 2: if not socket.waitForReadyRead(Timeout): self.error.emit(socket.error(), socket.errorString()) return instr = QDataStream(socket) instr.setVersion(QDataStream.Qt_4_0) blockSize = instr.readUInt16() while socket.bytesAvailable() < blockSize: if not socket.waitForReadyRead(Timeout): self.error.emit(socket.error(), socket.errorString()) return self.mutex.lock() fortune = instr.readQString() self.newFortune.emit(fortune) self.cond.wait(self.mutex) serverName = self.hostName serverPort = self.port self.mutex.unlock()