Запретить «GUI зависание» при проверке наличия файла - PullRequest
0 голосов
/ 21 января 2020

Я пытаюсь создать приложение для прослушивания файлов в pyqt5. Мой код работает, как я хочу, но я хочу улучшить его.

Есть простая кнопка Listen. Когда я нажимаю на него, он открывает блокнот и начинает всегда слушать, пока файл a.txt не существует. После того, как существует, новая кнопка Start существует, старая кнопка удаляется.

Моя проблема; мой GUI зависает во время прослушивания файла a.txt , даже если я использую threading. Я использую неправильно? Можете ли вы исправить мой код?

Мой основной код;

from PyQt5 import QtCore, QtWidgets
import sys
import os

class ListenWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ListenWindow, self).__init__(parent)
        self.setWindowTitle("Listen")

        self.button_listen = QtWidgets.QPushButton('Listen', self)
        font1 = self.button_listen.font()
        font1.setPointSize(10)
        self.button_listen.setFont(font1)
        self.button_listen.setFixedSize(200, 50)
        self.button_listen.clicked.connect(self.startToListen)

        self.v_box1 = QtWidgets.QVBoxLayout(self)
        self.v_box1.addWidget(self.button_listen)

        self.h_box1 = QtWidgets.QHBoxLayout(self)
        self.v_box1.addLayout(self.h_box1)

    def abc(self):
        while not os.path.exists('C:/Users/Wicaledon/PycharmProjects/myproject/a.txt'):
            pass

        if os.path.isfile('C:/Users/Wicaledon/PycharmProjects/myproject/a.txt'):
            self.button_start = QtWidgets.QPushButton('Start', self)
            font2 = self.button_start.font()
            font2.setPointSize(10)
            self.button_start.setFont(font2)
            self.button_start.setFixedSize(200, 50)
            self.h_box1.addWidget(self.button_start, 0, QtCore.Qt.AlignCenter)
        else:
            raise ValueError("%s isn't a file!" % 'C:/Users/Wicaledon/PycharmProjects/myproject/a.txt')
        self.v_box1.removeWidget(self.button_listen)

    def startToListen(self):
        def thread_function(my_text):
            import subprocess
            import os
            FNULL = open(os.devnull, 'w')
            args = my_text
            subprocess.call(args, stdout=FNULL, stderr=FNULL, shell=True)
            # os.system(my_text)
            return
        import threading
        my_text = "notepad"
        x = threading.Thread(target=thread_function,args=(my_text,))
        x.start()
        y = threading.Thread(target=ListenWindow.abc(self))
        y.start()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = ListenWindow()
    window.setWindowTitle('Login')
    window.show()
    sys.exit(app.exec_())

Мой код для создания a.txt;

f=open("a.txt", "w+")
f.write("delete_me")
f.close()

Ответы [ 2 ]

2 голосов
/ 21 января 2020

Не стоит использовать такой простой подход, чтобы проверить, существует ли файл, потому что он излишне потребляет много ресурсов ЦП.
Также, используя любой тип l oop (while / for или рекурсивный вызовы функций) - плохая идея при работе с пользовательским интерфейсом: как вы уже видели, он блокирует интерфейс; хотя решение , предложенное abhilb, может показаться работоспособным, оно просто делает пользовательский интерфейс отзывчивым, но в любом случае будет вызывать пики ЦП, даже после закрытия программы и создания файла.

PyQt уже имеет прослушиватель файлов, QFileSystemWatcher , и внешним системам с Qt следует избегать того, чтобы функциональность уже обеспечивала его, это учитывается не только для наблюдателя файлов, но и для потоков.

Еще один важный аспект, о котором следует помнить, это то, что Qt имеет свое собственное событие l oop, и для взаимодействия с ним не рекомендуется использовать потоки python (на самом деле, каждые * 1011) * Взаимодействие с пользовательским интерфейсом должно выполняться в основном потоке, даже при использовании потоков Qt).

Если вы действительно хотите использовать слушатель файла basi c (например, для учебных целей), вы должны по крайней мере добавьте в цикл официанта, и этот цикл имеет , чтобы быть в другом потоке, в противном случае GUI будет заблокирован в любом случае.

Ниже приведен вывод ementation полностью основан на Qt, основываясь на вашем коде.

import sys
from PyQt5 import QtCore, QtWidgets

fileToWatch = 'C:/Users/Wicaledon/PycharmProjects/myproject/a.txt'
editorProgram = 'notepad'

class ListenWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ListenWindow, self).__init__(parent)
        self.setWindowTitle("Listen")

        self.button_listen = QtWidgets.QPushButton('Listen', self)
        font1 = self.button_listen.font()
        font1.setPointSize(10)
        self.button_listen.setFont(font1)
        self.button_listen.setFixedSize(200, 50)
        self.button_listen.clicked.connect(self.startToListen)

        self.v_box1 = QtWidgets.QVBoxLayout(self)
        self.v_box1.addWidget(self.button_listen)

        # no parent with an already existing layout should be set for a new
        # layout; in this case, the current widget already has the v_box1 layout
        # set, therefore the new h_box1 layout should have no argument
        self.h_box1 = QtWidgets.QHBoxLayout()
        self.v_box1.addLayout(self.h_box1)

        self.listener = QtCore.QFileSystemWatcher(self)
        self.listener.directoryChanged.connect(self.checkFile)

    def startToListen(self):
        fileInfo = QtCore.QFileInfo(fileToWatch)
        if fileInfo.exists():
            self.createStart()
            return
        elif fileInfo.absolutePath() not in self.listener.directories():
            self.listener.addPath(fileInfo.absolutePath())

        # create an empty file so that there's no error when trying to open
        # it in the editor
        emptyFile = QtCore.QFile(fileToWatch)
        emptyFile.open(emptyFile.WriteOnly)
        emptyFile.close()

        process = QtCore.QProcess(self)
        process.start(editorProgram, [fileToWatch])

        # optional: disable the interface until the program has quit
        self.setEnabled(False)
        process.finished.connect(lambda: self.setEnabled(True))

    def checkFile(self, path):
        fileInfo = QtCore.QFileInfo(fileToWatch)
        if fileInfo.exists():
            if self.h_box1:
                # the layout already contains the start button, ignore
                return
            else:
                self.createStart()
        else:
            # file has been [re]moved/renamed, maybe do something here...
            pass

    def createStart(self):
        self.button_start = QtWidgets.QPushButton('Start', self)
        font2 = self.button_start.font()
        font2.setPointSize(10)
        self.button_start.setFont(font2)
        self.button_start.setFixedSize(200, 50)
        self.h_box1.addWidget(self.button_start, 0, QtCore.Qt.AlignCenter)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = ListenWindow()
    window.setWindowTitle('Login')
    window.show()
    sys.exit(app.exec_())
0 голосов
/ 21 января 2020

Измените while l oop в вашей функции abc на

        while not os.path.exists('b.txt'):
            QtCore.QCoreApplication.processEvents()
            pass
...