Reputation: 103
What I wonder about is why in debugging mode the Dummy-N thread in VSCode Call Stack Viewer is considered as Running even after closing the thread. I use latest versions of VSCode (1.97.2) and Microsoft Python Debugger (2025.1.2025022401). Python 3.11.6, PyQt5 version 5.15.10.
__main__.py
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from signal_generator.signals import SignalManager
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.signalmanager = SignalManager(parent=self)
self.setWindowTitle("Python oscilloscope")
self.setWindowTitle("Checkable Button Example")
self.setGeometry(100, 100, 300, 200)
# Create a button with checkable state
self.button = QPushButton("Checkable Button", self)
self.button.setCheckable(True)
self.button.setGeometry(100, 80, 100, 40)
# Connect button signal to a slot function
self.button.clicked.connect(self.on_button_click)
self.channel1_thread = None
self.channel1_generator = None
def on_button_click(self):
if self.button.isChecked():
self.button.setText("Run background task")
self.signalmanager.start_signal_generator(1)
else:
self.button.setText("Stop background task")
self.signalmanager.stop_signal_generator(1)
def closeEvent(self, event):
self.close()
if __name__ == "__main__":
app = QApplication([])
window = Window()
# TESTING
window.show()
app.exec_()
signal_generator\signals.py
import time
from PyQt5.QtCore import QObject, pyqtSignal, QThread
import sys
import debugpy
has_trace = hasattr(sys, 'gettrace') and sys.gettrace() is not None
has_breakpoint = sys.breakpointhook.__module__ != "sys"
isdebug = has_trace or has_breakpoint
class SignalManager:
def __init__(self, parent):
self.parent = parent
def start_signal_generator(self, channel: int):
if channel == 1:
# Ensure the previous thread is properly finished
if hasattr(self, "channel1_thread") and self.channel1_thread is not None:
self.stop_signal_generator(channel)
# Define worker thread
self.channel1_thread = QThread()
self.channel1_generator = SignalGenerator(self.parent)
self.channel1_generator.moveToThread(self.channel1_thread)
self.channel1_thread.started.connect(self.channel1_generator.run)
self.channel1_generator.finished.connect(self.channel1_thread.quit)
self.channel1_generator.finished.connect(self.channel1_generator.deleteLater)
self.channel1_thread.finished.connect(self.channel1_thread.deleteLater)
self.channel1_generator.progress.connect(self._reportProgress)
self.channel1_thread.start()
def stop_signal_generator(self, channel):
if channel == 1 and self.channel1_thread is not None and self.channel1_generator.is_running():
self.channel1_generator.stop()
self.channel1_thread.quit()
self.channel1_thread.wait()
self.channel1_thread = None
def _reportProgress(self):
"""This function will be responsible for plotting updated signal."""
pass
class SignalGenerator(QObject):
finished = pyqtSignal()
progress = pyqtSignal()
def __init__(self, parent, *args, **kwargs):
super().__init__()
self.parent = parent
self.running = True
if "noise_std_dev" in kwargs:
self.noise_std_dev = kwargs["noise_std_dev"]
def run(self):
"""Long-running task of generating/updating the signal"""
if isdebug:
debugpy.debug_this_thread()
while self.running:
if not self.running:
break
time.sleep(1)
self.progress.emit()
self.finished.emit()
self.stop()
def stop(self):
self.running = False
def is_running(self):
return self.running
launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File in Current Workspace with Qt",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/__main__.py",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"justMyCode": false
}
}
This minimal working example reproduces the situation I ask about. If one checks the call stack viewer in VSCode Dummy-N thread persists and new one is created when the background task is restarted.
Upvotes: 0
Views: 24