每次另一个另一个进程更新文件时如何从文件中读取

Posted

技术标签:

【中文标题】每次另一个另一个进程更新文件时如何从文件中读取【英文标题】:How to read from a file each time another another process updates it 【发布时间】:2021-10-17 10:11:05 【问题描述】:

我正在处理我的第一个应用程序。在一个窗口中,我有一个按钮,单击该按钮时,我想从另一个模块执行一个方法。此方法执行时间不确定,并且取决于终端中的用户输入。此方法创建一个文件并重复打开它,将内容写入文件,然后关闭文件。在此运行的同时,我在窗口中有一个 matplotlib 图形小部件,每次通过读取和绘制文件最新行中的数据将新内容写入文件时,我想更新一个绘图。

据我了解,如果我让它在我的 QT 程序的主线程中运行,我的应用程序中的任何内容都不会响应,直到用户输入函数完成。为了解决这个问题,我尝试将用户输入法的执行移到工作线程中。以我这样做的方式,我不相信它正在工作。作为一项测试,我尝试制作一个 QTimer,它试图读取文件并每秒绘制一次(添加一些东西来检查文件是否存在)。这会打印出文件在长任务之前还不存在,然后什么也不做,直到长任务完成,然后每秒开始读取和绘制文件。我不确定这是否意味着我没有正确处理线程或是否发生了其他事情。

为了检查文件的更改,我尝试使用 QFileSystemWatcher。更新:现在,在 userInputFunction() 运行时没有任何反应,但是当它完成时,我得到“data/runName_Rec.txt dataFileCreated”。如果我随后以任何方式手动编辑文件,则绘图会按原样进行。但我仍然想正确地线程化它,以便在我运行 userInputFunction() 时观察者工作

这是我的代码相关部分的简化示例。对于任何不良风格问题,我们深表歉意。

from PyQt5 import QtWidgets, uic, QtCore, QtGui
from pyqtgraph import PlotWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal
import pyqtgraph as pg
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QFileDialog, QMainWindow
import os
from os.path import exists
import csv
import numpy as np
import pandas as pd

import myModule

dirname = os.path.dirname(__file__)
class Worker(QObject):
    finished = pyqtSignal()

    def run(self,param1,param2):
        """Long-running task with user input from terminal."""
        myModule.userInputFunction(param1,param2)
        self.finished.emit()

class someWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(someWindow, self).__init__(*args, **kwargs)

        #Load the UI Page
        uic.loadUi('somewindow.ui', self)

        self.directoryPath = "data"

        self.fs_watcher = QtCore.QFileSystemWatcher()

        self.fs_watcher.addPath(self.directoryPath)
        self.fs_watcher.directoryChanged.connect(self.dataFileCreated)
        
        self.StartScanButton.clicked.connect(self.startSliceScan)
        self.EndScanButton.clicked.connect(self.endScan)

    def dataFileCreated(self):
        self.filePath = os.path.join(dirname, "data/"+ self.runNameBox.toPlainText()+"_Rec.txt")
        print(self.filePath + " dataFileCreated")
        self.fs_watcher.addPath(self.filePath)
        self.fs_watcher.fileChanged.connect(self.update_graph)

    def update_graph(self):
        if exists(self.path):
            print("file exists!")
            #then read the filePath.txt and plots the data
    else:
        print("file doesn't exist yet")

    def endScan(self):
        #change some display things

    def runLongTask(self):
        # Step 2: Create a QThread object
        self.thread = QThread()
        # Step 3: Create a worker object
        self.worker = Worker()
        # Step 4: Move worker to the thread
        self.worker.moveToThread(self.thread)
        # Step 5: Connect signals and slots
        self.thread.started.connect(self.worker.run(param1,param2))
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        # Step 6: Start the thread
        self.thread.start()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = someWindow()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

正如您所解释的,当您的程序启动时,相关文件尚不存在。

来自documentation of QFileSystemWatcher:

如果路径存在,则将路径添加到文件系统观察程序。如果路径不存在,或者文件系统观察程序已经在监视它,则不会添加该路径。

解决问题的方法有两个:

    要获得新创建文件的通知,请将父目录(即您希望文件出现的目录)添加到您的观察程序(在您的情况下,只需添加“.”)。然后,当创建任何新文件时,您将收到通知(directoryChanged signal)。您的 update_graph 方法已经为这种动态做好了准备,因为它会检查您的目标文件是否存在;因此您可以像使用fileChanged 一样直接将其连接到directoryChanged。 一旦文件存在,要获得有关文件未来任何更改的通知,请将文件本身添加到观察程序,就像您已经做的那样,但这次也要在 update_graph 中进行(它可能看起来多余,但请注意,正如上面的引用,冗余添加文件不是问题;同样,当您在开始时添加路径时,您会涵盖文件已经存在的情况。 如果文件被删除,您将再次收到 directoryChanged 事件。您不需要删除已删除文件的路径。 最后,注意addPath() 返回 bool 成功。因此,您应该检查程序中的返回值。

这种行为的原因是当你调用addPath()时,在那个路径找到的文件或目录被直接添加到观察者(而不是指向它的路径)。因此,通知仅在文件(或目录)的生命周期内有效。

【讨论】:

这是有道理的。请查看我最近的编辑,我已经尝试实现这一点。我认为观察者逻辑在这里看起来不错。但是它仍然不起作用,因为在 myModule.userInputFunction() 进程运行时整个 GUI 被冻结。由于 /data 目录或文件被更改的唯一时间是在 GUI 冻结时,所以绘图永远不会更新。 在我看来这条路径不正确..self.fs_watcher.addPath("/data") 哦,是的,你是对的!我已经编辑了我的帖子。现在 userInputFunction() 运行时什么都没有发生,但是当它完成时我得到“data/runName_Rec.txt dataFileCreated”。如果我随后以任何方式手动编辑文件,则绘图会按原样进行。但是我仍然想正确地线程化它,以便观察者在我运行 userInputFunction() 时工作​​ 我很高兴您在这个问题中讨论的问题得到了解决。我建议你为你的线程问题打开另一个问题。 我提出了一个关于线程问题的新问题。由于我最初的问题也是关于线程的,并且只解决了 QFileSystemWatcher 部分,所以编辑这个问题是否是一种好形式,所以它只是关于 QFileSystemWatcher?谢谢

以上是关于每次另一个另一个进程更新文件时如何从文件中读取的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Linux 中的另一个进程触发一个进程?

Perl - Win32 - 如何从另一个进程非阻塞读取文件句柄?

单击按钮时如何从另一个 Python 文件中读取 QLineEdit 值的值

错误“该进程无法访问文件'fileDir',因为它正被另一个进程使用。”尝试读取或写入文本文件时[重复]

当一个进程写入而另一个进程读取时,我是不是需要信号量?

从 zip 文件中读取文件并将其写入另一个文件时面临的问题