I develop app in Python 3.8 with PyQt5.
I need to run and able to kill a separate process from the PyQT Form.
When I use process.start and then process.kill it kills the process.
But when I use process.startDetached() then process.kill() doesn't work.
Below is an example:
from form import Ui_MainWindow
from PyQt5 import QtWidgets
class myapp(QtWidgets.QMainWindow):
def __init__(self):
super(myapp, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.process = QProcess(self)
self.ui.pushButton.clicked.connect(self.btnClicked)
self.ui.pushButton_2.clicked.connect(self.process.kill)
def btnClicked(self):
runstr = 'ping'
args = ['localhost','-t']
self.process.startDetached(runstr, args)
app = QtWidgets.QApplication([])
application = myapp()
application.show()
sys.exit(app.exec())
Could you advise the way how to kill a process in detached mode.
When you launch a detached process then the QProcess cannot kill the process, so in these cases you must obtain the pid of the child process and use the OS methods to kill it as indicated in the answers to the following questions:
How to terminate process from Python using pid?
Is it possible to kill a process on Windows from within Python?
from PyQt5 import QtCore, QtWidgets
import psutil
from form import Ui_MainWindow
class MyApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.process = QtCore.QProcess(self)
self.ui.pushButton.clicked.connect(self.start_process)
self.ui.pushButton_2.clicked.connect(self.stop_process)
self._pid = -1
#QtCore.pyqtSlot()
def start_process(self):
runstr = "ping"
args = ["localhost", "-t"]
self.process.setProgram(runstr)
self.process.setArguments(args)
ok, pid = self.process.startDetached()
if ok:
self._pid = pid
#QtCore.pyqtSlot()
def stop_process(self):
if self._pid > 0:
p = psutil.Process(self._pid)
p.terminate()
self._pid = -1
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
application = MyApp()
application.show()
sys.exit(app.exec_())
Related
This is an example of the application that starts the QApp -> Worker -> Something that causes sys.exit() inside the worker
import sys
from PyQt5.QtCore import QRunnable, pyqtSlot, QThreadPool
from PyQt5.QtWidgets import QWidget, QApplication
class App(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.threadpool = QThreadPool()
worker = Worker()
self.threadpool.start(worker)
def init_ui(self):
self.setFixedSize(600, 300)
self.show()
class Worker(QRunnable):
def __init__(self):
super(Worker, self).__init__()
#pyqtSlot()
def run(self):
print("Running worker...")
run_application()
def run_window():
app = QApplication([])
ex = App()
sys.exit(app.exec_())
def run_application():
print('Running application...')
sys.exit(10)
if __name__ == '__main__':
run_window()
There are several cases when I run it:
exits with code 0
exits with code 10
window keeps hanging
It feels like a racing.
So what is the correct way to terminate the window in case of the sys.exit() or the exception inside the worker?
Edit: forgot to mention that I need to return the exit code and track it since I run the window through the subprocess/Popen.
If you want to close the windows then a possible solution is to terminate the eventloop using QCoreApplication::quit():
def run_application():
print("Running application...")
QCoreApplication.quit()
Or QCoreApplication::exit():
def run_application():
print("Running application...")
QCoreApplication.exit(10)
I need to display adb logs in a Pyqt window, i tried like this. When i click the button log_display function is called, which set the console output to textBrowser. I tried to use subprocess but it didn't help, the window is just freezing and not responding. What is the way to do it? Maybe i need to use new thread for it?
def log_display(self):
result = subprocess.run('adb logcat *:I', stdout=subprocess.PIPE)
self.textBrowser.setText(subprocess.run('result.stdout'))
You have to use QProcess since it launches the application but does not block the GUI since it notifies the log through signals:
from PyQt5 import QtCore, QtGui, QtWidgets
class LogView(QtWidgets.QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent)
self.setReadOnly(True)
self._process = QtCore.QProcess()
self._process.readyReadStandardOutput.connect(self.handle_stdout)
self._process.readyReadStandardError.connect(self.handle_stderr)
def start_log(self, program, arguments=None):
if arguments is None:
arguments = []
self._process.start(program, arguments)
def add_log(self, message):
self.appendPlainText(message.rstrip())
def handle_stdout(self):
message = self._process.readAllStandardOutput().data().decode()
self.add_log(message)
def handle_stderr(self):
message = self._process.readAllStandardError().data().decode()
self.add_log(message)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = LogView()
w.resize(640, 480)
w.show()
w.start_log("adb", ["logcat", "*:I"])
sys.exit(app.exec_())
When I try to run the following PyQt code for running processes and tmux, I encounter the error QProcess: Destroyed while process is still running. How can I fix this?
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class embeddedTerminal(QWidget):
def __init__(self):
QWidget.__init__(self)
self._processes = []
self.resize(800, 600)
self.terminal = QWidget(self)
layout = QVBoxLayout(self)
layout.addWidget(self.terminal)
self._start_process(
'xterm',
['-into', str(self.terminal.winId()),
'-e', 'tmux', 'new', '-s', 'my_session']
)
button = QPushButton('list files')
layout.addWidget(button)
button.clicked.connect(self._list_files)
def _start_process(self, prog, args):
child = QProcess()
self._processes.append(child)
child.start(prog, args)
def _list_files(self):
self._start_process(
'tmux', ['send-keys', '-t', 'my_session:0', 'ls', 'Enter']
)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = embeddedTerminal()
main.show()
You usually get the error QProcess: Destroyed while process is still running when the application closes and the process hadn't finished.
In your current code, your application ends at soon as it starts, because you didn't call app.exec_(). You should do something like:
if __name__ == "__main__":
app = QApplication(sys.argv)
main = embeddedTerminal()
main.show()
sys.exit(app.exec_())
Now, it works fine, but when you close the application you will still get the error message. You need to overwrite the close event to end the process properly. This works, given you replace child by self.child:
def closeEvent(self,event):
self.child.terminate()
self.child.waitForFinished()
event.accept()
Hi I have seen there are already many questions on this issue however none of them seems to answer my query .
As per below link i even tried winpexpect as i am using windows , however it dosent seems to e working for me .
Getting realtime output from ffmpeg to be used in progress bar (PyQt4, stdout)
I am running a subprogram with subprocess.Popen and want to see the real time result in a pyQt Widget. Currently it shows the result in the pyQt widget but only after the sub command is finished executing . I need to know if there s a way when we can get the output from a subprocess at real time into the window . See the code below which i tried for all this .
import sys
import os
from PyQt4 import QtGui,QtCore
from threading import Thread
import subprocess
#from winpexpect import winspawn
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class gui(QtGui.QMainWindow):
def __init__(self):
# ...
super(gui, self).__init__()
# Install the custom output stream
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.initUI()
def normalOutputWritten(self, text):
cursor = self.textEdit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.textEdit.ensureCursorVisible()
def callProgram(self):
command="ping 127.0.0.1"
#winspawn(command)
py=subprocess.Popen(command.split(),stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
result,_=py.communicate()
for line in result:
print line
print result
def initUI(self):
self.setGeometry(100,100,300,300)
self.show()
self.textEdit=QtGui.QTextEdit(self)
self.textEdit.show()
self.textEdit.setGeometry(20,40,200,200)
print "changing sys.out"
print "hello"
thread = Thread(target = self.callProgram)
thread.start()
#Function Main Start
def main():
app = QtGui.QApplication(sys.argv)
ui=gui()
sys.exit(app.exec_())
#Function Main END
if __name__ == '__main__':
main()
QProcess is very similar to subprocess, but it's much more convenient to use in (Py)Qt code. Because it utilizes signals/slots. Also, it runs the process asynchronously so you don't have use QThread.
I've modified (and cleaned) your code for QProcess:
import sys
from PyQt4 import QtGui,QtCore
class gui(QtGui.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(str(self.process.readAll()))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping',['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtGui.QHBoxLayout()
self.runButton = QtGui.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtGui.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
#Function Main Start
def main():
app = QtGui.QApplication(sys.argv)
ui=gui()
ui.show()
sys.exit(app.exec_())
#Function Main END
if __name__ == '__main__':
main()
Here is an adaptation of the accepted answer for PyQt6 and PyQt5.
PyQt6:
from PyQt6 import QtCore, QtWidgets
import sys
# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"
class gui(QtWidgets.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.MoveOperation.End)
# Here we have to decode the QByteArray
cursor.insertText(
str(self.process.readAll().data().decode(CP_console)))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping', ['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtWidgets.QVBoxLayout()
self.runButton = QtWidgets.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtWidgets.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtWidgets.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
# Function Main Start
def main():
app = QtWidgets.QApplication(sys.argv)
ui = gui()
ui.show()
sys.exit(app.exec())
# Function Main END
if __name__ == '__main__':
main()
PyQt5:
from PyQt5 import QtCore, QtWidgets
import sys
# On Windows it looks like cp850 is used for my console. We need it to decode the QByteArray correctly.
# Based on https://forum.qt.io/topic/85064/qbytearray-to-string/2
import ctypes
CP_console = f"cp{ctypes.cdll.kernel32.GetConsoleOutputCP()}"
class gui(QtWidgets.QMainWindow):
def __init__(self):
super(gui, self).__init__()
self.initUI()
def dataReady(self):
cursor = self.output.textCursor()
cursor.movePosition(cursor.End)
# Here we have to decode the QByteArray
cursor.insertText(
str(self.process.readAll().data().decode(CP_console)))
self.output.ensureCursorVisible()
def callProgram(self):
# run the process
# `start` takes the exec and a list of arguments
self.process.start('ping', ['127.0.0.1'])
def initUI(self):
# Layout are better for placing widgets
layout = QtWidgets.QVBoxLayout()
self.runButton = QtWidgets.QPushButton('Run')
self.runButton.clicked.connect(self.callProgram)
self.output = QtWidgets.QTextEdit()
layout.addWidget(self.output)
layout.addWidget(self.runButton)
centralWidget = QtWidgets.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# QProcess object for external app
self.process = QtCore.QProcess(self)
# QProcess emits `readyRead` when there is data to be read
self.process.readyRead.connect(self.dataReady)
# Just to prevent accidentally running multiple times
# Disable the button when process starts, and enable it when it finishes
self.process.started.connect(lambda: self.runButton.setEnabled(False))
self.process.finished.connect(lambda: self.runButton.setEnabled(True))
# Function Main Start
def main():
app = QtWidgets.QApplication(sys.argv)
ui = gui()
ui.show()
sys.exit(app.exec_())
# Function Main END
if __name__ == '__main__':
main()
I'm trying to create a client-server application, and when the server closes I wish that client GUI to close, which is running on another thread. I wish to access the GUI and close but I get X error:
Bad implementation(...).
How can I resolve this problem?
what you can do is emit a custom signal when the first thread goes down..
from PyQt4 import QtGui as gui
from PyQt4 import QtCore as core
import sys
import time
class ServerThread(core.QThread):
def __init__(self, parent=None):
core.QThread.__init__(self)
def start_server(self):
for i in range(1,6):
time.sleep(1)
self.emit(core.SIGNAL("dosomething(QString)"), str(i))
def run(self):
self.start_server()
class MainApp(gui.QWidget):
def __init__(self, parent=None):
super(MainApp,self).__init__(parent)
self.label = gui.QLabel("hello world!!")
layout = gui.QHBoxLayout(self)
layout.addWidget(self.label)
self.thread = ServerThread()
self.thread.start()
self.connect(self.thread, core.SIGNAL("dosomething(QString)"), self.doing)
def doing(self, i):
self.label.setText(i)
if i == "5":
self.destroy(self, destroyWindow =True, destroySubWindows = True)
sys.exit()
app = gui.QApplication(sys.argv)
form = MainApp()
form.show()
app.exec_()