How to debug PyQt5 threads in Visual Studio Code? - python

I am right now developing a PyQT5 application an use multithreading to avoid freezing of the GUI. Unfortuneately the Visual Studio Code debugger does not stop on breakpoints inside the executed thread. I tried all suggestions from the following page without fixing the problem. https://github.com/microsoft/ptvsd/issues/428. I think VS Code switched debugger from ptvsd to debugpy so all the suggestions do not hold any more. Maybe somebody has an idea how to fix this issue.
import time
import sys
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget, QLabel
class Worker(QObject):
sig_msg = pyqtSignal(str) # message to be shown to user
def __init__(self):
super().__init__()
#pyqtSlot()
def work(self):
self.sig_msg.emit('Hello from inside the thread!')
result = 1 + 1
result2 = 1 + 2
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 200)
self.button_start_threads = QPushButton("Start")
self.button_start_threads.clicked.connect(self.start_threads)
self.label = QLabel()
form_layout.addWidget(self.label)
form_layout.addWidget(self.button_start_threads)
QThread.currentThread().setObjectName('main')
self.__threads = None
def start_threads(self):
self.__threads = []
worker = Worker()
thread = QThread()
thread.setObjectName('thread')
self.__threads.append((thread, worker)) # need to store worker too otherwise will be gc'd
worker.moveToThread(thread)
worker.sig_msg.connect(self.label.setText)
thread.started.connect(worker.work)
thread.start()
if __name__ == "__main__":
app = QApplication([])
form = MyWidget()
form.show()
sys.exit(app.exec_())
You can reproduce the error by setting a breakpoint at self.sig_msg.emit('Hello from inside the thrad!') in my case the debugger does not stop at this position. I use VS Code Version 1.65.2. The code is taken from the post mentioned above.

I have recently experienced the same issue with VS Code and PyQt5. Following the advice at https://code.visualstudio.com/docs/python/debugging#_troubleshooting, I was able to hit the breakpoint in your example code by importing debugpy and adding debugpy.debug_this_thread() in the work method e.g.
def work(self):
debugpy.debug_this_thread()
self.sig_msg.emit('Hello from inside the thread!')
result = 1 + 1
result2 = 1 + 2
Hopefully that can help others facing the same issue.

Related

why mne raw.plot doesn't block in a Qt Application on macOS?

I'm a beginner to Pyside6 and mne-python. I want the raw.plot to block so I can interact with the figure. However, the program continue to run and print 'after window close' after the figure opened. According to mne documentation, https://mne.tools/stable/generated/mne.viz.plot_raw.html?highlight=plot_raw#mne.viz.plot_raw
I have set the parameter block to True, but it doesn't work.
import matplotlib
import mne, sys
from PySide6.QtWidgets import QMainWindow, QPushButton, QApplication
matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS']
matplotlib.rcParams['axes.unicode_minus'] = False
matplotlib.use('qtAgg')
class Test(QMainWindow):
def __init__(self):
super(Test, self).__init__()
self.button = QPushButton('Plot raw')
self.button.clicked.connect(self.Show)
self.setCentralWidget(self.button)
def Show(self):
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = (sample_data_folder / 'MEG' / 'sample' /
'sample_audvis_filt-0-40_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.plot(block=True)
print('after window close')
app = QApplication(sys.argv)
main = Test()
main.show()
app.exec()
sys.exit()
When the program is running, it shows some information like this:
CoreApplication::exec: The event loop is already running.
I search for it, but don't find a solution to my problem. Is this an waring or error and can anyone give me a solution?

App crashes after closing multiple QMessageBoxes

i have an odd problem with the GUI i am working on with Python using PyQt5.
The GUI has a single button that calls a class method which executes a Worker QThread.
In the Worker's run()-method I emit two consecutive signals that call the main GUIs show_messagebox()-method which displays a messagebox containing the information from the signal.
Displaying multiple QMessageBoxes works fine, but after closing the last one, the whole program including the main GUI exits/crashes. Unfortunately I don't get an error message.
Calling self.show_messagebox.emit() only once in the run()-method works as intended and doesn't close the whole program.
I assume that for some reason the program thinks that the last remaining QMessageBox (the first one that gets emitted) becomes the parent and after closing it the program is done?
Unfortunately I haven't found a solution on how to solve this issue, which is why I decided to ask.
Here is the full source code:
import sys
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QMainWindow, QWidget, QMessageBox
from PyQt5 import QtWidgets
class Worker(QObject):
finished = pyqtSignal()
show_messagebox = pyqtSignal(str, str)
def run(self):
self.show_messagebox.emit("Test1", "Test Text 1")
self.show_messagebox.emit("Test2", "Test Text 2")
self.finished.emit()
class WindowMain(QMainWindow):
def __init__(self):
super(WindowMain, self).__init__()
layout = QVBoxLayout()
self.button = QPushButton("Start")
self.messagebox = QMessageBox()
layout.addWidget(self.button)
self.button.clicked.connect(self.execute)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
def execute(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.show_messagebox.connect(self.show_messagebox)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
def show_messagebox(self, title, text, icon=QMessageBox.Information):
messagebox = QMessageBox()
messagebox.setWindowTitle(title)
messagebox.setText(text)
messagebox.setIcon(icon)
messagebox.exec_()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = WindowMain()
window.show()
sys.exit(app.exec_())
Thanks in advance for any help!

Unable to run pyshark.FileCapture(pcap) inside a QThread class in linux Ubuntu 16.04

I have a PySide2 Application that works in windows. What it does is that it opens a network packet (pcap) file using the python pyshark library inside a QThread class. It is able to open that pcap file but when i tried to run the same app on ubuntu, it throws an error. RuntimeError: Cannot add child handler, the child watcher does not have a loop attached.
I have searched online for solutions and stumbled across this site -> https://github.com/KimiNewt/pyshark/issues/303
It seems that pyshark can only run in the main thread and not in the sub-thread.
I have also created a minimal example that replicates my problem as shown below.
import sys
import pyshark
import asyncio
from PySide2.QtCore import QThread, Qt, Slot
from PySide2.QtWidgets import QApplication, QMainWindow, QFrame, QHBoxLayout, QPushButton
class SubThread(QThread):
def __init__(self):
super(SubThread, self).__init__()
self.Input_file = "profibus.pcap"
def run(self):
print("thread is running!")
cap = pyshark.FileCapture(self.Input_file)
iter_obj = iter(cap)
pkt = next(iter_obj)
print(pkt)
cap.close()
class TopLayout(QFrame):
def __init__(self, sub_thread):
self.frame = QFrame()
self.layout = QHBoxLayout()
self.sub_thread = sub_thread
self.toggle_button_box = QHBoxLayout()
self.toggle_button = QPushButton("Start")
self.toggle_button.setCheckable(True)
self.toggle_button_box.addWidget(self.toggle_button)
self.layout.addLayout(self.toggle_button_box)
self.layout.setAlignment(Qt.AlignCenter)
self.frame.setLayout(self.layout)
self.toggle_button.clicked.connect(self.on_toggle_button_clicked)
#Slot()
def on_toggle_button_clicked(self):
self.sub_thread.start()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.SubThread = SubThread()
self.top_layout = TopLayout(self.SubThread)
self.setCentralWidget(self.top_layout.frame)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
# Execute application
sys.exit(app.exec_())
My current environment configs are:
Python 3.7,
Pyside2 5.13.2,
Pyshark 0.4.2.9 (latest)

Timers cannot be stopped from another thread - Remove Focus

import sys
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit
class Worker(QThread):
def __init__(self, textBox):
super().__init__()
self.textBox = textBox
def run(self):
while True:
if self.textBox.text() == "close":
app.quit()
break
if self.textBox.text() == "removeFocus":
self.textBox.clearFocus()
class window(QWidget):
def __init__(self):
super().__init__()
vBox = QVBoxLayout()
self.setLayout(vBox)
self.resize(600, 400)
textBox = QLineEdit()
vBox.addWidget(textBox)
worker = Worker(textBox)
worker.start()
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = window()
sys.exit(app.exec())
When I type "close" in the textBox it works very fine but when I type "removeFocus", it still works but I get this error:
QObject::killTimer: Timers cannot be stopped from another thread
Why am I getting such an error even though the program is running?
(Since the process I want to do is very simple, I don't think I can go into much detail. I've just started learning Python. This is the first time I use this site. I'm sorry if I made a mistake while creating a post. Thank you)
In Qt you must not access or modify the GUI information from another thread (see this for more information) since it does not guarantee that it works (the GUI elements are not thread-safe), in your case luckily you have no problems but It is dangerous to use your approach in real.
In your case it is also unnecessary to use threads since it is enough to use the textChanged signal from QLineEdit.
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit
class Window(QWidget):
def __init__(self):
super().__init__()
vBox = QVBoxLayout(self)
self.resize(600, 400)
self.textBox = QLineEdit()
vBox.addWidget(self.textBox)
self.textBox.textChanged.connect(self.on_text_changed)
#pyqtSlot(str)
def on_text_changed(self, text):
if text == "close":
QApplication.quit()
elif text == "removeFocus":
self.textBox.clearFocus()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

PyQt5 interface unresponsive when waiting too long

I am working on a GUI in python 3.5 with PyQt5 for a small chat bot. The problem i have is that the pre-processing, post-processing and brain are taking too much time to give back the answer for the user provided input.
The GUI is very simple and looks like this: http://prntscr.com/dsxa39 it loads very fast without connecting it to other modules. I mention that using sleep before receiving answer from brain module will still make it unresponsive.
self.conversationBox.append("You: "+self.textbox.toPlainText())
self.textbox.setText("")
time.sleep(20)
self.conversationBox.append("Chatbot: " + "message from chatbot")
this is a small sample of code, the one that i need to fix.
And this is the error I encounter: http://prnt.sc/dsxcqu
I mention that I've searched for the solution already and everywhere I've found what I've already tried, to use sleep. But again, this won't work as it makes the program unresponsive too.
Slow functions, such as sleep, will always block unless they are running asynchronously in another thread.
If you want to avoid threads a workaround is to break up the slow function. In your case it might look like:
for _ in range(20):
sleep(1)
self.app.processEvents()
where self.app is a reference to your QApplication instance. This solution is a little hacky as it will simply result in 20 short hangs instead of one long hang.
If you want to use this approach for your brain function then you'll need it to break it up in a similar manner. Beyond that you'll need to use a threaded approach.
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QMainWindow, QGridLayout, QLabel, QApplication, QWidget, QTextBrowser, QTextEdit, \
QPushButton, QAction, QLineEdit, QMessageBox
from PyQt5.QtGui import QPalette, QIcon, QColor, QFont
from PyQt5.QtCore import pyqtSlot, Qt
import threading
import time
textboxValue = ""
FinalAnsw = ""
class myThread (threading.Thread):
print ("Start")
def __init__(self):
threading.Thread.__init__(self)
def run(self):
def getAnswer(unString):
#do brain here
time.sleep(10)
return unString
global textboxValue
global FinalAnsw
FinalAnsw = getAnswer(textboxValue)
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'ChatBot'
self.left = 40
self.top = 40
self.width = 650
self.height = 600
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
pal = QPalette();
pal.setColor(QPalette.Background, QColor(40, 40, 40));
self.setAutoFillBackground(True);
self.setPalette(pal);
font = QtGui.QFont()
font.setFamily("FreeMono")
font.setBold(True)
font.setPixelSize(15)
self.setStyleSheet("QTextEdit {color:#3d3838; font-size:12px; font-weight: bold}")
historylabel = QLabel('View your conversation history here: ')
historylabel.setStyleSheet('color: #82ecf9')
historylabel.setFont(font)
messagelabel = QLabel('Enter you message to the chat bot here:')
messagelabel.setStyleSheet('color: #82ecf9')
messagelabel.setFont(font)
self.conversationBox = QTextBrowser(self)
self.textbox = QTextEdit(self)
self.button = QPushButton('Send message', self)
self.button.setStyleSheet(
"QPushButton { background-color:#82ecf9; color: #3d3838 }" "QPushButton:pressed { background-color: black }")
grid = QGridLayout()
grid.setSpacing(10)
self.setLayout(grid)
grid.addWidget(historylabel, 1, 0)
grid.addWidget(self.conversationBox, 2, 0)
grid.addWidget(messagelabel, 3, 0)
grid.addWidget(self.textbox, 4, 0)
grid.addWidget(self.button, 5, 0)
# connect button to function on_click
self.button.clicked.connect(self.on_click)
self.show()
def on_click(self):
global textboxValue
textboxValue = self.textbox.toPlainText()
self.conversationBox.append("You: " + textboxValue)
th = myThread()
th.start()
th.join()
global FinalAnsw
self.conversationBox.append("Rocket: " + FinalAnsw)
self.textbox.setText("")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
app.exec_()
So creating a simple thread solved the problem, the code above will still freeze because of the sleep function call, but if you replace that with a normal function that lasts long it won't freeze anymore. It was tested by the brain module of my project with their functions.
For a simple example of building a thread use https://www.tutorialspoint.com/python/python_multithreading.htm
and for the PyQt GUI I've used examples from this website to learn http://zetcode.com/gui/pyqt5/

Categories

Resources