In this MWE, a popup window is created and after 8 seconds it prints 'done' in the console.
If the popup window is closed before 'done' is printed, then after a few seconds it still prints 'done'.
I would like that nothing is printed if I close the popup window before 'done' is printed. How can do this?
import time
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import threading
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = ())
myDataLoop.start()
self.show()
return
def dataSendLoop():
#actually a lengthy calculation
time.sleep(8)
print('done')
if __name__== '__main__':
app =QApplication(sys.argv)
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Related
I've tried several methods to stop a Qthread, but none seem to work. When I look at the Call Stack, the thread has not ended, and while click again, another thread starts, thus getting infinite active threads and consuming more unnecessary memory. I tried to put terminate() and quit() and nothing stops the thread. So I also tried all of them together and no result
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
class MyThread(QThread):
def run(self):
x=0
print("Starting...")
while x<5:
time.sleep(1.5)
print("Running...")
x=x+1
def close(self):
self.isRunning=False
self.terminate()
self.wait()
self.quit()
self.wait()
print("Done")
class Ui_MainWindow(QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
self.worker=MyThread()
self.worker.setTerminationEnabled(True)
self.worker.finished.connect(self.worker.close)
self.worker.start()
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())
I managed to solve my problem using threading native library and with pyqtSignal. When clicking on the button, it loses its connection and starts the thread. After the end of the loop in the thread, a signal is issued and thus returning the connection to the button. This way, it prevents the user from clicking the button several times.
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import pyqtSignal
from PyQt5 import QtWidgets
from PyQt5 import QtCore
import time
import sys
import threading
class Ui_MainWindow(QMainWindow):
signal=pyqtSignal(str)
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.btn = QtWidgets.QPushButton(self)
self.btn.setGeometry(QtCore.QRect(77, 30, 50, 30))
self.btn.setText("CLICK")
self.signal.connect(self.signalResponse)
self.btn.clicked.connect(self.doSomething)
def doSomething(self,event):
print("Starting...")
self.btn.disconnect()
worker=threading.Thread(target=self.worker)
worker.start()
def signalResponse(self,response):
if response=="Finished":
self.btn.clicked.connect(self.doSomething)
print(response)
def worker(self):
x=0
while x<5:
time.sleep(0.5)
print("Running...")
x=x+1
self.signal.emit("Finished")
if __name__ == "__main__":
app = QApplication([])
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())
I use pyttsx3 engine to convert text to speech. And when I click 'start' I create a thread with I target set to 'self.start' so the app doesn't freeze while speaking. And I want to STOP the thread and therefore the engine will stop automaticly. And I tried to rasie exception to kill the thread but it was so useless for my case...
The main qustion here is 'I want to kill the thread so I can stop the engine when clicking 'end' button'
my code:
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import sys
import time
import threading
import pyttsx3
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(self.create_a_thread)
self.btn2 = QPushButton('end', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(self.end)
self.engine = pyttsx3.init()
self.rate = self.engine.getProperty('rate')
self.engine.setProperty('rate', 75)
def start(self):
self.engine.say('I want to stop this thread')
self.engine.runAndWait()
def create_a_thread(self):
thread = threading.Thread(target=self.start, daemon=True)
thread.start()
def end(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
I'm using the below code to try and capture double clicks within the main window"
import sys
from PySide6 import QtCore
from PySide6.QtWidgets import (
QApplication,
QGridLayout,
QMainWindow,
QSplitter,
QTreeView,
QWidget,
)
from PySide6.QtWebEngineWidgets import QWebEngineView
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.windowLayout = QGridLayout()
self.splitter = QSplitter()
self.webView = QWebEngineView()
self.webView.setUrl(QtCore.QUrl("http://127.0.0.1:8080"))
self.tree = QTreeView()
self.splitter.addWidget(self.webView)
self.splitter.addWidget(self.tree)
self.windowLayout.addWidget(self.splitter)
self.mainWidget = QWidget()
self.mainWidget.setLayout(self.windowLayout)
self.setCentralWidget(self.mainWidget)
def mouseDoubleClickEvent(self, e):
print('test')
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.resize(1500, 1000)
window.show()
app.exec()
And I get the following console output:
qt.pointer.dispatch: delivering touch release to same window QWindow(0x0) not QWidgetWindow(0x600003bb8e40, name="MainWindowClassWindow")
qt.pointer.dispatch: skipping QEventPoint(id=1 ts=0 pos=0,0 scn=749.346,451.159 gbl=749.346,451.159 Released ellipse=(1x1 ∡ 0) vel=0,0 press=-749.346,-451.159 last=-749.346,-451.159 Δ 749.346,451.159) : no target window
however, when I comment out self.setCentralWidget(self.mainWidget) I obviously don't see anything, but the double click triggers. Any suggestions?
EDIT: update to min repro, also note that clicking anywhere not inside the QWebEngineView correctly results in mouseDoubleClickEvent triggering. However, the above error results when I click in the QWebEngineView, and nothing happens when I click inside the QTreeView
As per musicamante's very useful comments (see original post), the solution I ended up using was
...
self.tree.doubleClicked.connect(self.treeDoubleClicked)
...
def treeDoubleClicked(self, index):
self.selectedFilepath = index.model().filePath(index)
I made a small test application to practice threading - on the first launch it works fine, with the loop printing to the console while the gui is still running. However, the kernel crashes and restarts if I close the app and then open it again afterwards.
I've had this issue before with pyqt5 applications, and it's usually solved when I add the QCoreApplication.instance() section at the end of my script. I was wondering whether this time there was an error with the way I implemented my threading.
import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from PyQt5.QtCore import QCoreApplication
class Worker(QObject):
finished = pyqtSignal()
def __init__(self):
super(Worker, self).__init__()
self.working = True
def work(self):
while self.working:
# do stuff here
print("I'm running")
time.sleep(1)
self.finished.emit() # alert our gui that the loop stopped
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 200, 150)
self.setWindowTitle("Program")
self.startbtn = QtWidgets.QPushButton("Start", self)
self.startbtn.resize(self.startbtn.minimumSizeHint())
self.startbtn.move(50, 50)
self.stopbtn = QtWidgets.QPushButton("Stop", self)
self.stopbtn.move(50, 100)
self.stopbtn.resize(self.stopbtn.minimumSizeHint())
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.startbtn.clicked.connect(self.thread.start) # start the thread when we click the start button
self.thread.started.connect(self.worker.work) # begin our worker object's loop
self.stopbtn.clicked.connect(self.stop_loop) # stop the loop on the stop button click
self.worker.finished.connect(self.loop_finished) # do something in the gui when the worker loop ends
def stop_loop(self):
self.worker.working = False
def loop_finished(self):
print('Looped Finished')
if __name__ == "__main__":
app = QCoreApplication.instance() # Fixes error with kernel crashing on second run of application
if app is None: # PyQt doesn't like multiple QApplications in the same process, therefore
app = QtWidgets.QApplication(sys.argv) # apart from the initial run, the first QApplication instance will run
window = Window()
window.show()
app.exec_()
I am starting a QProcess and would like to bring the resulting window on top everytime a command is written to QProcess.
However, if my process is "gnuplot.exe" (Win7) the plot window will be updated but always stays behind the PyQt window. I haven't yet found a way to bring it to front, or make it active, or put the focus on, or however you would call it.
Something like
self.process. ...
raise(), show(), activateWindow(), SetForegroundWindow, WindowStaysOnTopHint ... ???
These posts didn't help me further
How to find the active PyQt window and bring it to the front
https://forum.qt.io/topic/30018/solved-bring-to-front-window-application-managed-with-qprocess/3
Bring QProcess window to front (running Qt Assistant)
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())
Based on the comment and links from #buck54321, I got to the following version which seems to (almost) work.
The solution in the above links seem to be more general and a bit too complicated for my case, since I know the exact name of my window which I want to bring to front.
To my original code I just had to add three lines. I was not familiar with win32gui.
However, with my code, only after the button is pressed the second time the window will be brought to front. After the first button press hwnd is still 0 which leads to a crash. I guess this is because gnuplot.exe will start without opening a window, and opens a window only after the first command has been sent.
If anybody can show me how to bring the window to front even after the first button click that would be great.
If there are limitation or improvements I would be happy to learn about them.
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
import win32gui ####### new line
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "set term wxt 999\n plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
hwnd = win32gui.FindWindow(None, "Gnuplot (window id : 999)") ####### new line
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if hwnd != 0: win32gui.SetForegroundWindow(hwnd) ####### new line
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())