在pyqt5中退出GUI时终止正在运行的进程的正确方法是什么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在pyqt5中退出GUI时终止正在运行的进程的正确方法是什么?相关的知识,希望对你有一定的参考价值。

当我单击qbtn按钮退出GUI时,GUI消失了,但是convert(filename)中的过程继续在后台运行。

我如何做到这一点,以便当用户按下退出按钮时,GUI消失并且正在运行的进程终止?

我正在使用pyqt5 GUI(如下):

from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPixmap, QFont, QColor, QIcon
from PyQt5 import QtCore
from PyQt5 import QtGui
import sys
from pathlib import Path
import threading, queue, time

file_queue = queue.Queue()

def get_logo_path():
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = Path(sys._MEIPASS).joinpath('files').absolute()
    except Exception:
        base_path = Path(".").absolute()

    logo_path = base_path.joinpath('pi_logo.png')

    return str(logo_path.absolute())

class WorkerThread(QtCore.QRunnable):

    __keep_alive = True

    def __init__(self):
        super().__init__()
        self.queue = queue.Queue()


    def addJob(self,filepath):

        # Puts an item into the queue; if the queue is full, wait until a free slot is available before adding the item.
        self.queue.put(filepath)

    def run(self):
        while self.__keep_alive:

            # Remove and return an item from the queue. If queue is empty, wait until an item is available.
            convert(self.queue.get()) 

    def kill(self):
        self.__keep_alive = False

        # Calls the function that puts item into the queue
        self.addJob('')
        print("que: ", self.queue)

def convert(filename):

    #: main
    if filename == '':
        print("CONVERT() run when filename == ''")
        #return

    else:
        print("filename", filename)

        i2 = 1
        while i2 > 0 and i2 < 2500000:
            print("Hello, running process convert() (", str(i2), ")")
            i2 = i2 + 1



class TitleBar(QHBoxLayout):
    __layout = None

    def __init__(self):
        super().__init__()

        label = QLabel()
        pixmap = QPixmap(get_logo_path())
        label.setPixmap(pixmap)
        label.setStyleSheet("QLabel  background-color: #646464;")
        self.addWidget(label)
        qbtn = QPushButton('X')
        qbtn.setFont(QFont("Arial", weight=QFont.Bold))
        qbtn.setStyleSheet("QPushButton  background-color: #641010; color: #FFFFFF;")
        qbtn.setMaximumWidth(25)

        # Calls quit() when the ex button is clicked
        qbtn.clicked.connect(self.close_window)
        self.setContentsMargins(10, 10, 10, 10)
        self.addWidget(qbtn)

    def close_window(self):
        print('DEF CLOSE_WINDOW(SELF): QUITTING GUI ONLY!!!!')
        app = QApplication.instance()
        for widget in app.topLevelWidgets():
            print("widget: ", widget)
            if isinstance(widget, QMainWindow):
                # Closes the window
                widget.close()

class BodyLayout(QHBoxLayout):
    __navigation = None
    __body = None

    def __init__(self):
        super().__init__()
        label = QLabel(
            'Drag and drop any directory into the window to begin conversion.'
        )
        label.setStyleSheet(
            "QLabel  background-color: #646464; color: #FFFFFF;")
        label.setAlignment(QtCore.Qt.AlignCenter)
        self.addWidget(label)

class MainWidget(QWidget):
    __layout = None
    __titlebar = None
    __body = None

    def __init__(self):
        super().__init__()

        self.__titlebar = TitleBar()
        self.__body = BodyLayout()

        self.__layout = QVBoxLayout()
        self.__layout.setContentsMargins(0, 0, 0, 0)
        self.__layout.addLayout(self.__titlebar, 1)
        self.__layout.addLayout(self.__body, 10)
        self.setLayout(self.__layout)

class MainWindow(QMainWindow):
    __window = None
    oldPos = None
    oldY = None
    oldX = None
    workerThread = None
    threadPool = None

    def __init__(self):
        super().__init__()

        self.__window = MainWidget()

        self.setCentralWidget(self.__window)

        sizeObject = QDesktopWidget().screenGeometry(-1)

        self.setFixedSize(min(600, sizeObject.width()),
                          min(150, sizeObject.height()))

        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)

        self.setStyleSheet("QMainWindow  background: #646464; ")

        self.setAcceptDrops(True)

        self.workerThread = WorkerThread()
        self.threadPool = QtCore.QThreadPool()
        self.threadPool.start(self.workerThread)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        for url in event.mimeData().urls():
            filepath = Path(url.toLocalFile())
            #if filepath.name.endswith('.xlsx'):
            if filepath.is_dir():
                self.workerThread.addJob(filepath)

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()

        if self.size().height() + self.pos().y() - self.oldPos.y() < 15:
            self.oldY = event.globalPos()
        else:
            self.oldY = None

        if self.size().width() + self.pos().x() - self.oldPos.x() < 15:
            self.oldX = event.globalPos()
        else:
            self.oldX = None

        if self.oldPos.y() - self.pos().y() > 60:
            self.oldPos = None

    def mouseReleaseEvent(self, event):
        self.oldPos = None
        self.oldY = None
        self.oldX = None

    def mouseMoveEvent(self, event):
        if self.oldPos != None:
            delta = QtCore.QPoint(event.globalPos() - self.oldPos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.oldPos = event.globalPos()
        if self.oldY != None:
            delta = QtCore.QPoint(event.globalPos() - self.oldY)
            self.setFixedHeight(self.size().height() + delta.y())
            self.oldY = event.globalPos()
        if self.oldX != None:
            delta = QtCore.QPoint(event.globalPos() - self.oldX)
            self.setFixedWidth(self.size().width() + delta.x())
            self.oldX = event.globalPos()

    def closeEvent(self,event):
        print('DEF CLOSEEVENT() kills workerThread')
        self.workerThread.kill()
        #self.quit()

app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
答案

线程继续,因为直到convert从其while循环返回之前,它才能够处理队列。

如果您还想“杀死”它,则必须在每个while循环中不断检查quit条件。

一种简单的解决方案是直接在convert功能内移动run()功能。 BUT ...请注意,“终止”不会立即发生,因为某些预定的过程仍将发生(在这种情况下,是打印),这实际上是很好的,因为没有好的杀死进程的方法-出于种种原因,这很好,我在这里不做解释。

    def run(self):
        while self.__keep_alive:

            filename = self.queue.get()
            if filename == '':
                print("CONVERT() run when filename == ''")
                break
                #return

            else:
                print("filename", filename)

                i2 = 1
                while i2 > 0 and i2 < 2500000:
                    try:
                        # a non blocking get that constantly checks the queue
                        result = self.queue.get(False)
                        if result == '':
                            break
                    except:
                        pass
                    print("Hello, running process convert() (", str(i2), ")")
                    i2 = i2 + 1
                else:
                    continue
                break

当然,这是一个非常基本的实现,您可能想要为转换就位时可能发生的任何其他排队创建某种延迟队列(其表现为请求“缓冲区”),以便您以后可以处理它,但这不是此问题的范围。

以上是关于在pyqt5中退出GUI时终止正在运行的进程的正确方法是什么?的主要内容,如果未能解决你的问题,请参考以下文章

Linux_进程终止(进程退出,进程等待(阻塞与非阻塞等待))

在不终止启动 Python 脚本的情况下关闭 pyqt5 GUI

PyQt5 窗口在运行几秒钟后自动关闭 - “进程以退出代码 -1073741819 (0xC0000005) 完成”

使gdb在成功终止时自动退出?

运行另一个进程而不冻结 GUI

关闭 QMainWindow 的正确方法