Fehler bei der Verwendung von Watchdog und Qt zum Offenhalten einer Queue vor/nach dem Senden der ersten Datei

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Die Schwierigkeit ist zu überprüfen, wann eine Datei fertig ist. Mit deiner Methode kann es passieren, dass eine Datei, die nicht fertig ist, geschickt wird. Deswegen habe ich dieses "Monstrum an Code" geschrieben habe: es testet genau wenn eine Datei fertig ist (natürlich ist das Problem, dass es nicht funktioniert, wie ich es will)
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das habe ich schon verstanden. Und deine Schlussfolgerung stimmt nicht. Es geht schlicht darum, die Generierung von Kandidaten aus dem created event sauber zu trennen von der Überprüfung der Kandidaten, bis sie wahlweise aussortiert oder eben als ok vermeldet werden. Statt das alles in einen “big ball of mud” zu rollen, der wie du ja feststellst, nicht tut, was er tun soll.

Es gibt übrigens auch close Events. Die wären ggf ein besseres Signal. Das hängt natürlich letztlich vom Erzeugungsprozess ab. Der kann natürlich auch pathologische Zugriffsmuster haben, so das man nur heuristisch weiterkommt, wie du das ja auch machst.
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Es funktioniert jetzt. Ich habe den else Teil von try verwendet:

Code: Alles auswählen

        while True:
            event = self._q.get()
            max_retry_count = 3500  # for test purposes now but want to set an upper bound on verifying a file is finished.
            retry_interval_seconds = .01  # every hundreth it will try the file to see if it finished writing
            retry_count = 0
            if event.event_type == "created" and event.src_path.lower().endswith(".hdf5"):
                while True:
                    try:
                        file = h5py.File(event.src_path, "r")
                        file.close()
                    except OSError:
                        if retry_count < max_retry_count:
                            retry_count += 1
                            print(f"h5 file <{event.src_path}> is locked, retrying {retry_count}/{max_retry_count}")
                            time.sleep(retry_interval_seconds)
                        else:
                            print(f"h5 file <{event.src_path}> reached max retry count, skipping")
                            break  # <--- looks useful here
                    except Exception as err:
                        print(f"Got unexpected Error <{type(err).__name__}> while opening <{event.src_path}> ")
                        traceback.print_exc()
                    else:
                        self.new_file.emit(event.src_path, os.path.basename(event.src_path))
                        break

Danke für die Hilfe __deets__! Dein Code war sehr hilfreich.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pyhill00: Ich würde aus dem ``while`` ein ``for`` machen und Du behandelst unerwartete Ausnahmen in einer Art und Weise dass da leicht eine Endlosschleife entstehen kann. Und zwar eine die richtig CPU verbrät weil die ohne Wartezeit immer wieder versucht die Datei zu öffnen.

Warum werden bei dem Signal so redundante Daten gesendet? Der Pfad mit Dateiname und dann nochmal nur der Dateiname? Der steckt doch im Pfad mit Dateiname bereits drin.

Ungetestet:

Code: Alles auswählen

        for event in iter(self._q.get, None):
            # for test purposes now but want to set an upper bound on verifying
            # a file is finished.
            max_retry_count = 3500
            # every hundreth it will try the file to see if it finished writing
            retry_interval_seconds = 0.01
            if (
                event.event_type == "created"
                and event.src_path.lower().endswith(".hdf5")
            ):
                for retry_count in range(1, max_retry_count + 1):
                    try:
                        h5py.File(event.src_path, "r").close()
                    except OSError:
                        print(
                            f"h5 file {event.src_path!r} is locked, retrying"
                            f" {retry_count}/{max_retry_count}"
                        )
                    except Exception as error:
                        print(
                            f"Got unexpected Error <{type(error).__name__}> "
                            f"while opening {event.src_path!r}"
                        )
                        traceback.print_exc()
                        #
                        # TODO Maybe stop retrying here‽
                        #
                    else:
                        #
                        # TODO Why those redundant values?  Why not just the
                        #   first value?
                        #
                        self.new_file.emit(
                            event.src_path, os.path.basename(event.src_path)
                        )
                        break

                    time.sleep(retry_interval_seconds)
                else:
                    print(
                        f"h5 file {event.src_path!r} reached max retry count,"
                        f" skipping"
                    )
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Hallo wieder,

ich habe einen Bug entdeckt. Erstmal funktioniert das .hdf5 Datei Hochladen gut. Hier ist ein normales Ergebnis vom Terminal:

Code: Alles auswählen

running API pyqt5
using configuration at imageanalyser/qimageanalyser/image_analyzer.conf
no Pyro installed. Use dummy event handler.
{'coordinates': True, '_actions': {'home': <PyQt5.QtWidgets.QAction object at 0x7f8681235a60>, 'back': <PyQt5.QtWidgets.QAction object at 0x7f8681235af0>, 'forward': <PyQt5.QtWidgets.QAction object at 0x7f8681235b80>, 'pan': <PyQt5.QtWidgets.QAction object at 0x7f8681235ca0>, 'zoom': <PyQt5.QtWidgets.QAction object at 0x7f8681235dc0>, 'configure_subplots': <PyQt5.QtWidgets.QAction object at 0x7f8681235d30>, 'edit_parameters': <PyQt5.QtWidgets.QAction object at 0x7f8681235ee0>, 'save_figure': <PyQt5.QtWidgets.QAction object at 0x7f868123f040>}, 'locLabel': <PyQt5.QtWidgets.QLabel object at 0x7f8681235f70>, 'canvas': <qimageanalyser.mplwidget.MplCanvas object at 0x7f8688935940>, '_nav_stack': <matplotlib.cbook.Stack object at 0x7f868123d850>, '_lastCursor': <Cursors.POINTER: 1>, '_id_press': 2, '_id_release': 3, '_id_drag': 4, '_pan_info': None, '_zoom_info': None, 'mode': <_Mode.NONE: ''>}
try to connect to event service ...
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5> is locked, retrying 1/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5> is locked, retrying 2/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5> is locked, retrying 3/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5> is locked, retrying 4/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5> is locked, retrying 5/350
finished run test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (3rd copy).hdf5 2021_08_10_13_43_29 (3rd copy).hdf5
<HDF5 file "2021_08_10_13_43_29 (3rd copy).hdf5" (mode r+)>
analyze_image
xmean 135.47
xstd 41.31
xmean 148.42
xstd 41.17
mass:  3.81923985318e-26 [kg] size:  0.0002074740400778919 [m] tof:  0.01 [s]
mass:  3.81923985318e-26 [kg] size:  0.000122395198908796 [m] tof:  0.01 [s]
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5> is locked, retrying 1/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5> is locked, retrying 2/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5> is locked, retrying 3/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5> is locked, retrying 4/350
h5 file <test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5> is locked, retrying 5/350
finished run test_image_analyzer_files/Test_Data/2021_08_10_13_43_29 (4th copy).hdf5 2021_08_10_13_43_29 (4th copy).hdf5
<HDF5 file "2021_08_10_13_43_29 (4th copy).hdf5" (mode r+)>
analyze_image
xmean 135.47
xstd 41.31
xmean 148.42
xstd 41.17
mass:  3.81923985318e-26 [kg] size:  0.0002074740400778919 [m] tof:  0.01 [s]
mass:  3.81923985318e-26 [kg] size:  0.000122395198908796 [m] tof:  0.01 [s]


Ich habe da zwei Bilder hinzugefügt und das Programm hat geklappt. Ich habe das Programm beendet und dann habe ich Programm etwa fünf mal mehr laufen lassen (also Bilder in den Ordner tun, und testen, ob ich noch Bilder sehen kann und dann das Programm beendet). Aber dann nach etwa fünf Mal funktioniert das Programm nicht mehr. Jetzt bekomme ich das gleiche, jedes mal, das ich das Programm laufen lasse und neue Bilder hinzufüge:



Code: Alles auswählen

running API pyqt5
using configuration at imageanalyser/qimageanalyser/image_analyzer.conf
no Pyro installed. Use dummy event handler.
{'coordinates': True, '_actions': {'home': <PyQt5.QtWidgets.QAction object at 0x7f8681235a60>, 'back': <PyQt5.QtWidgets.QAction object at 0x7f8681235af0>, 'forward': <PyQt5.QtWidgets.QAction object at 0x7f8681235b80>, 'pan': <PyQt5.QtWidgets.QAction object at 0x7f8681235ca0>, 'zoom': <PyQt5.QtWidgets.QAction object at 0x7f8681235dc0>, 'configure_subplots': <PyQt5.QtWidgets.QAction object at 0x7f8681235d30>, 'edit_parameters': <PyQt5.QtWidgets.QAction object at 0x7f8681235ee0>, 'save_figure': <PyQt5.QtWidgets.QAction object at 0x7f868123f040>}, 'locLabel': <PyQt5.QtWidgets.QLabel object at 0x7f8681235f70>, 'canvas': <qimageanalyser.mplwidget.MplCanvas object at 0x7f8688935940>, '_nav_stack': <matplotlib.cbook.Stack object at 0x7f868123d850>, '_lastCursor': <Cursors.POINTER: 1>, '_id_press': 2, '_id_release': 3, '_id_drag': 4, '_pan_info': None, '_zoom_info': None, 'mode': <_Mode.NONE: ''>}
try to connect to event service ...

Also es geht nicht mehr in die while-Schleife uzw. Weißt jemand woran es liegen kann. Vielleicht muss ich genau den Thread schließen? Oder vielleicht ist es wohl das Problem wie __blackjack__ erwähnt hat?


Hier ist der Code bzgl. dieses Problemes:

Code: Alles auswählen

thread = QThread(parent=self) #this part is in the DesignerMainWindow() __init__() shown below
print('try to connect to event service ...')
worker = watchdog_search.Worker("/home/test_image_analyzer_files/Test_Data/") 
worker.moveToThread(thread)
thread.started.connect(worker.work)
thread.start()
worker.new_file.connect(self.on_finished_run)


def run_app():
    try:
        # first try to get an existing app
        app = QtGui.QApplication.instance()
        reuseapp = True
        if app is None:
            app = QtGui.QApplication(sys.argv)
            reuseapp = False
    except:
        pass
    dmw = DesignerMainWindow()
    dmw.show()
    return dmw, app, reuseapp


if __name__ == "__main__":
    dmw, app, reuseapp = run_app()
    if "app" in globals() and not reuseapp:
        sys.exit(app.exec_())


`watchdog_search`:


Code: Alles auswählen

import time
import traceback
import os

import h5py
import queue
from typing import Union

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, DirCreatedEvent, FileCreatedEvent

from .tools.qt import QtCore

from PyQt5.QtCore import pyqtSignal


from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import (
    QObject,
    QThread,
    pyqtSignal,
    pyqtSlot,
)

class NewFileHandler(FileSystemEventHandler):

    def __init__(self, q, *a, **k):
        super().__init__(*a, **k)
        self._q = q

    def on_created(self, event):
        self._q.put(event)

class Worker(QObject):

    new_file = pyqtSignal(str,str)

    def __init__(self, path):
        super().__init__()
        self._q = queue.Queue()
        observer = Observer()
        handler = NewFileHandler(self._q)
        observer.schedule(handler, path=path, recursive=True)
        # starts a background thread! Thus we need to wait for the
        # queue to receive the events in work.
        observer.start()

    def work(self):
        while True:
            event = self._q.get()
            max_retry_count = 350  # for test purposes now but want to set an upper bound on verifying a file is finished.
            retry_interval_seconds = .01  # every hundreth it will try the file to see if it finished writing
            retry_count = 0
            if event.event_type == "created" and event.src_path.lower().endswith(".hdf5"):
                while True:
                    try:
                        file = h5py.File(event.src_path, "r")
                        file.close()
                    except OSError:
                        if retry_count < max_retry_count:
                            retry_count += 1
                            print(f"h5 file <{event.src_path}> is locked, retrying {retry_count}/{max_retry_count}")
                            time.sleep(retry_interval_seconds)
                        else:
                            print(f"h5 file <{event.src_path}> reached max retry count, skipping")
                            break  # <--- looks useful here
                    except Exception as err:
                        print(f"Got unexpected Error <{type(err).__name__}> while opening <{event.src_path}> ")
                        traceback.print_exc()
                    else:
                        self.new_file.emit(event.src_path, os.path.basename(event.src_path))
                        break
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Ich habe zumindest gefunden, wo das Problem ist::

Code: Alles auswählen

thread.started.connect(worker.work)
funktioniert nicht. Es ruft worker.work nicht auf. Hat jemand eine Idee wieso?
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Ich sehe gerade, dass ich die Warnung für connect bekomme: "Cannot find reference 'connect' in 'function | pyqtBoundSignal' "

Vielleicht hat __deets__ eine Idee?
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Ich habe es gelöst. Ich habe anstatt Qthread, threading mit Daemon Thread verwendet und jetzt funktioniert es einwandfrei:

Code: Alles auswählen

        worker = watchdog_search.Worker("/home/test_image_analyzer_files/Test_Data/")
        worker.new_file.connect(self.on_finished_run)
        thread = threading.Thread(target=worker.work)
        thread.setDaemon(True)
        thread.start()
Antworten