I am developing a multithread application using the library pyQt5. There are many threads activated in order to plot in the GUI some data extracted from many devices (one thread <-> one device). If the application runs for over 30 minutes, the application closes without any error (maybe I was not able to log it).
The problem is that I am expecting the application will run without quitting unexpectedly after 30 minutes. I create an object inside the code in order to record the logs, but I didn't log any unexpected error. I run my program on Pycharm many times in a debugging mode in run mode:
Debugging: the program does not work properly due to the many threads that are launched, so after 2 minutes I see a window error;
Run: it works properly over an hour without incurring in any python or windows error.
Do you have any idea, like memory limits on python or on windows 11 with 64 bit, that can crash my software?
This happens using the executable file generated using pyinstaller.
I increased the RAM memory in order to monitor it during the execution of the program, but the RAM does not increase in a significant way.
The only error that I catch is -1073741571 (0xC00000FD), a stack overflow. But I do not have any recursive function. Could I increase the size? How can I face it?
Complete error:
Windows fatal exception: stack overflow
Thread 0x00003220 (most recent call first):
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\common\threads\worker.py", line 24 in run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 232 in _exec_run
Thread 0x000084cc (most recent call first):
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\common\threads\worker.py", line 24 in run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 232 in _exec_run
Thread 0x00003714 (most recent call first):
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 232 in _exec_run
Thread 0x00003548 (most recent call first):
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\common\threads\ram_reader.py", line 30 in run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 237 in _new_run
Thread 0x000080e4 (most recent call first):
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 359 in wait
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\queue.py", line 180 in get
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\thread_manager\threads\thread_event_driven_base.py", line 28 in run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 237 in _new_run
Thread 0x0000846c (most recent call first):
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\data_storage\data_storage.py", line 141 in _remove_last_rows
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\data_storage\data_storage.py", line 122 in _creating_raw_csv
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\data_storage\data_storage.py", line 99 in save_raw_data
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\thread_manager\threads\thread_event_driven_base.py", line 30 in run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydev_bundle\pydev_monkey_qt.py", line 237 in _new_run
Thread 0x0000698c (most recent call first):
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 359 in wait
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 655 in wait
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\pydevd.py", line 159 in _on_run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 219 in run
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1075 in _bootstrap_inner
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1032 in _bootstrap
Thread 0x000080e0 (most recent call first):
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 291 in _on_run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 219 in run
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1075 in _bootstrap_inner
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1032 in _bootstrap
Thread 0x000050bc (most recent call first):
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 359 in wait
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\queue.py", line 180 in get
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 367 in _on_run
File "C:\Program Files\JetBrains\PyCharm 2024.3.1.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 219 in run
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1075 in _bootstrap_inner
File "C:\Users\H544210\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1032 in _bootstrap
Current thread 0x00004c04 (most recent call first):
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\transforms.py", line 2436 in get_affine
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\transforms.py", line 2410 in transform_affine
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\transforms.py", line 1495 in transform
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\transforms.py", line 465 in transformed
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\axis.py", line 2581 in get_tick_space
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\ticker.py", line 2168 in _raw_ticks
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\ticker.py", line 2238 in tick_values
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\ticker.py", line 2230 in __call__
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\axis.py", line 1525 in get_majorticklocs
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\axis.py", line 1281 in _update_ticks
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\axis.py", line 1351 in get_tightbbox
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\artist.py", line 1402 in _get_tightbbox_for_layout_only
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\axes\_base.py", line 4554 in get_tightbbox
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\artist.py", line 1402 in _get_tightbbox_for_layout_only
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\_constrained_layout.py", line 641 in get_pos_and_bbox
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\_constrained_layout.py", line 388 in make_layout_margins
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\_constrained_layout.py", line 116 in do_constrained_layout
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\layout_engine.py", line 278 in execute
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\figure.py", line 3251 in draw
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\artist.py", line 71 in draw_wrapper
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\artist.py", line 94 in draw_wrapper
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_agg.py", line 382 in draw
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 104 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 888 in _smoke_plot_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 869 in _clib_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 817 in _analog_data_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 888 in _smoke_plot_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 869 in _clib_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 817 in _analog_data_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 114 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 888 in _smoke_plot_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 869 in _clib_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 114 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 817 in _analog_data_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 114 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 888 in _smoke_plot_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 869 in _clib_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 114 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\mpl_canvas.py", line 180 in update_curve
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 817 in _analog_data_update
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\interface.py", line 961 in _update_data
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 105 in _update_generic_plot
File "C:\honeywell\dev\git\python\fire-chamber\fire_chamber\lib\interface\widgets\ram_monitor.py", line 112 in _update_plot
File "C:\honeywell\dev\git\python\fire-chamber\venv\Lib\site-packages\matplotlib\backends\backend_qt.py", line 451 in flush_events
...
Process finished with exit code -1073741571 (0xC00000FD)
Minimal example to give an idea how the project works:
import sys
import faulthandler
import time
import random as rd
from copy import deepcopy
from dataclasses import dataclass
import numpy as np
import matplotlib.pyplot as plt
from PyQt5.QtCore import QObject, pyqtSignal, QThread
from PyQt5.QtWidgets import QMainWindow, QWidget, QGridLayout, QApplication, QTabWidget, QGroupBox
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
SIG1: str = "sig_1"
SIG2: str = "sig_2"
SIG3: str = "sig_3"
SIG4: str = "sig_4"
SIGNAL_TO_PLOT: tuple = (SIG1, SIG2, SIG3, SIG4)
def set_padding(inf_value: float, sup_value: float, perc: float = 0.05) -> tuple[float, float]:
dim_range: float = np.abs(sup_value - inf_value)
padding: float = perc * dim_range
return inf_value - padding, sup_value + padding
class GlobalCommunication(QObject):
device_data_signal: pyqtSignal = pyqtSignal(object)
global_communication: GlobalCommunication = GlobalCommunication()
@dataclass
class Packet:
name: str
value: float
class Instrument:
def __init__(self, name: str):
self.id: str = name
def read(self) -> Packet:
return Packet(self.id, rd.uniform(0, 1))
class ThreadWrapper:
SAMPLING_TIME: float = 1.0 # [s]
def __init__(self, device: Instrument):
self._device: Instrument = device
self._thread: QThread = QThread()
self._worker: Worker = Worker(self._device, self.SAMPLING_TIME)
self._worker.moveToThread(self._thread)
self._thread.started.connect(self._worker.run)
self._worker.finished.connect(self._thread.quit)
self._worker.data_ready.connect(global_communication.device_data_signal.emit)
def run(self):
self._thread.start()
def stop(self):
self._worker.stop()
class Worker(QObject):
finished: pyqtSignal = pyqtSignal()
data_ready: pyqtSignal = pyqtSignal(object)
def __init__(self, device: Instrument, sampling_time: float):
super().__init__()
self._device: Instrument = device
self._sampling_time: float = sampling_time
self._running: bool = True
def run(self):
self._running = True
while self._running:
device_data_packet: Packet = self._device.read()
print(f"{device_data_packet=}")
self.data_ready.emit(device_data_packet)
time.sleep(self._sampling_time)
self.finished.emit()
def stop(self):
self._running = False
class ThreadManagerWrapper:
def __init__(self):
self._device_list: list = []
self._thread_list: list = []
self.set_configuration()
def set_configuration(self):
############ Objects creation ############
self.set_devices()
############ Thread creation ############
self.set_threads()
def set_devices(self):
# Creating the devices
self._sig_1 = Instrument(SIG1)
self._sig_2 = Instrument(SIG2)
self._sig_3 = Instrument(SIG3)
self._sig_4 = Instrument(SIG4)
# Populating the device list
self._device_list = [
self._sig_1,
self._sig_2,
self._sig_3,
self._sig_4,
]
def set_threads(self):
# Creating the analog daq thread
_sig_1_thread: ThreadWrapper = ThreadWrapper(self._sig_1)
_sig_2_thread: ThreadWrapper = ThreadWrapper(self._sig_2)
_sig_3_thread: ThreadWrapper = ThreadWrapper(self._sig_3)
_sig_4_thread: ThreadWrapper = ThreadWrapper(self._sig_4)
# Populating the thread list
self._thread_list =[
_sig_1_thread,
_sig_2_thread,
_sig_3_thread,
_sig_4_thread,
]
def run_processes(self):
# Start threads
self._run_threads()
def _run_threads(self):
try:
for thread_element in self._thread_list:
thread_element.run()
except Exception as e:
print(f"[Error thread] {str(e)}")
def stop_process(self):
for thread_element in self._thread_list:
thread_element.stop()
class ConfigurationThread(QObject):
error: pyqtSignal = pyqtSignal(str)
ready: pyqtSignal = pyqtSignal()
finished: pyqtSignal = pyqtSignal()
def __init__(self):
super().__init__()
self.configured: bool = False
self._thread: QThread = QThread()
self._worker: ConfigurationWorker = ConfigurationWorker()
self._worker.moveToThread(self._thread)
self._thread.started.connect(self._worker.run)
self._worker.configured.connect(self._on_configured)
self._worker.error.connect(self._error_manager)
self._worker.finished.connect(self._on_worker_finished)
self._thread.start()
def run(self):
self._thread.start()
def _on_configured(self, thread_manager: ThreadManagerWrapper):
self._thread_manager = thread_manager
self.configured = True
self.ready.emit()
def _on_worker_finished(self):
'''Do nothing to take alive the thread'''
pass
def run_processes(self):
if self.configured and self._thread_manager:
self._thread_manager.run_processes()
self.finished.emit()
def stop_process(self):
if self.configured and self._thread_manager:
self._thread_manager.stop_process()
self.finished.emit()
def _error_manager(self, msg: str):
self.configured = False
self.error.emit(msg)
def shutdown(self):
if self._thread.isRunning():
self._thread.quit()
self._thread.wait()
class ConfigurationWorker(QObject):
finished: pyqtSignal = pyqtSignal()
error: pyqtSignal = pyqtSignal(str)
configured: pyqtSignal = pyqtSignal(object)
def __init__(self):
super().__init__()
self._thread_manager: None | ThreadManagerWrapper = None
def run(self):
try:
if not self._thread_manager:
self._thread_manager = ThreadManagerWrapper()
self.configured.emit(self._thread_manager)
except Exception as e:
print(str(e))
finally:
self.finished.emit()
class MplCanvas(FigureCanvas):
def __init__(self, title: str):
fig, self.ax = plt.subplots(constrained_layout=True)
super().__init__(fig)
self.curve, = self.ax.plot([], [], "b-")
# Show grid
self.ax.grid(linestyle="--")
# Set the title
self.ax.set_title(title)
# Set label
self.ax.set_xlabel("Time [s]")
fig.subplots_adjust(top=0.85)
@staticmethod
def _update_x_lim(x_list: list) -> tuple:
# To not modify the original list I create a deep copy
x_total_list: list = deepcopy(x_list)
if not x_total_list:
# The figure is empty
return -1, 1
x_total_list.sort()
x_inf, x_sup = set_padding(x_total_list[0], x_total_list[-1])
return x_inf, x_sup
@staticmethod
def _update_y_lim(y_list: list) -> tuple:
if not y_list:
# The figure is empty
return -1, 1
y_inf, y_sup = set_padding(np.min(y_list), np.max(y_list))
return y_inf, y_sup
def update_curve(self, x_list: list, y_list: list):
x_inf, x_sup = self._update_x_lim(x_list)
self.ax.set_xlim(x_inf, x_sup)
y_inf, y_sup = self._update_y_lim(y_list)
self.ax.set_ylim(y_inf, y_sup)
self.curve.set_data(x_list, y_list)
self.draw()
self.flush_events()
class Interface(QMainWindow):
def __init__(self):
super().__init__()
# I create a list in order to clean all the figure during some events (now it's not used)
self._figure_dict: dict = {}
self._data: dict = {
signal_name: []
for signal_name in SIGNAL_TO_PLOT
}
self._set_figures()
# Connecting to the update data function
global_communication.device_data_signal.connect(self._update_data)
self._set_threads()
def _set_figures(self):
self.group_box: QGroupBox = QGroupBox("Signals")
self.group_box_layout: QGridLayout = QGridLayout(self.group_box)
for i, signal_name in enumerate(SIGNAL_TO_PLOT):
fig: MplCanvas = MplCanvas(signal_name)
self._figure_dict[signal_name] = fig
row: int = i % 2
column: int = int(i / 2)
self.group_box_layout.addWidget(fig, row, column)
central_widget = QWidget()
layout = QGridLayout()
layout.addWidget(self.group_box)
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def _set_threads(self):
self._configuration_thread: ConfigurationThread = ConfigurationThread()
self._configuration_thread.error.connect(self.run_error_popup)
self._configuration_thread.ready.connect(self._on_thread_ready)
self._configuration_thread.finished.connect(self._set_something)
def _update_data(self, data_packet: Packet):
signal_name: str = data_packet.name
# Store the data
self._data[signal_name].append(data_packet.value)
x_time: list = list(range(len(self._data[signal_name])))
self._figure_dict[signal_name].update_curve(x_time, self._data[signal_name])
def _on_thread_ready(self):
# Run the threads
self._configuration_thread.run_processes()
def _set_something(self):
print("Is finished [Used in the main application to detect an event in the main application]")
def run_error_popup(self, error_msg: str):
print(f"The error is: {error_msg}")
def run_ui():
# Launching the app
app: QApplication = QApplication([])
interface: Interface = Interface()
interface.show()
sys.exit(app.exec())
if __name__ == '__main__':
faulthandler.enable()
print(sys.getrecursionlimit())
run_ui()