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

Watchdog wird verwendet, um neue .hdf5-Dateien zur Verwendung an ein anderes Programm. Das Problem ist, dass mit Queue nur eine Datei gesendet werden kann und Watchdog keine weiteren Dateien sendet, selbst wenn dem Ordner neue Dateien hinzugefügt werden. Ich glaube, das hat damit zu tun, dass Queue block=True verwendet wird und das ist problematisch für Qt, aber ich bin nicht sicher, wie ich das Problem lösen kann. Ich habe versucht, anstelle von Queue eine normale Liste zu verwenden, aber da am Anfang nichts in der Liste ist, erhalte ich einen Fehler, da das Programm bereits ein Element in der Liste finden will (Beispiel für diesen Code ist unten auskommentiert, um zu verstehen, was ich meine).

Im Wesentlichen: der Code funktioniert, wenn eine .hdf5-Datei zum Ordner hinzugefügt wird, aber wenn andere Dateien hinzugefügt werden, werden sie vom Code nicht erkannt.

Hier ist der Code, der den Watchdog-Code auslöst (dieser befindet sich in einer anderen .py-Datei):

Code: Alles auswählen

        try:
            print('try to connect to event service ...')
            self.listener = watchdog_search.get_qt_listener() 
            self.listener.listener.finishedRun.connect(self.on_finished_run)
        except Exception as e:
            print(e)
Und hier ist der `watchdog_search` Code:

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
    
    
    class NewFileHandler(FileSystemEventHandler):
        """h5 file creation handler for Watchdog"""
    
        def __init__(self):
            super().__init__()
            self.file_queue = queue.Queue()
            #self.la = [] #here is the list code I was referring to. I wanted to try to avoid using Queue but the attempt didnt work since an empty list at the beginning gives an error
    
        # callback for File/Directory created event, called by Observer.
        def on_created(self, event: Union[DirCreatedEvent, FileCreatedEvent]):
            if event.src_path[-4:] == "hdf5":
                # run callback with path string
                self.file_queue.put(event.src_path)
                #self.la.append(event.src_path)
    
    class ObserverWrapper(QtCore.QObject): # New LabBusSubscriber
        """Encapsulated Observer boilerplate"""
        if hasattr(QtCore, "QString"):
            finishedRun = QtCore.Signal(QtCore.QString, QtCore.QString)
        else:
            finishedRun = QtCore.Signal(str, str)
        can_listen = True
        def __init__(self, path: str):#, recursive=True):
            super().__init__()
            self.path = path    
            self.observer = Observer()
            self.handler = NewFileHandler()
            self.observer.schedule(self.handler, path=path, recursive=True)
            self.start()
    
        def start(self):
            """
            Starts observing for filesystem events. Runs self.routine() every 1 second.
    
            :param blocking: If true, blocks main thread until keyboard interrupt.
            """
    
            self.observer.start()
        def stop(self):
            """
            Stops the observer. When running self.start(blocking=True) then you don't need to call this.
            """
    
            self.observer.stop()
            self.observer.join()
    
        def event(self, event):
            """Here we define what to do at which signal.
            In general we will transmit a QT signal to which the other components connect.
            """
            print("EVENT", event)
            if isinstance(event, QtCore.QEvent):
                # make sure the QObject code reacts on QEvents
                return QtCore.QObject.event(self, event)
            self.finishedRun.emit(event[0], event[1])
        
        def wait_for_file(self):
            """
            Wait and Process newly created files
            """
    
            max_retry_count = 3500 # for test purposes now but want to set an upper bound on verifying a file is finished.
            # will try h5 file for a max of 35 seconds (upper bound) to see if the file is finished.
            # Files are usually finished within 20-30 seconds
            #
            retry_interval_seconds = .01 # every hundreth it will try the file to see if it finished writing
    

            #file_path = self.handler.la[-1] #this expects already at the beginning an entry but there is no file until one is populated into the folder
            file_path = self.handler.file_queue.get(block=True)
            file_name = os.path.basename(file_path)
    
            # try to open the file
            retry_count = 0
            while True:
                try:
                    file = h5py.File(file_path, "r")
                    file.close()
                    self.event([file_path, file_name])
                    break
                except OSError:
                    if retry_count < max_retry_count:
                        retry_count += 1
                        print(f"h5 file <{file_path}> is locked, retrying {retry_count}/{max_retry_count}")
                        time.sleep(retry_interval_seconds)
                    else:
                        print(f"h5 file <{file_path}> reached max retry count, skipping")
    
                except Exception as err:
                    print(f"Got unexpected Error <{type(err).__name__}> while opening <{file_path}> ")
                    traceback.print_exc()
    
    class QtEventSubscriber(QtCore.QThread):
        """The listener thread"""
    
        def __init__(self):
            QtCore.QThread.__init__(self)
            self.listener = ObserverWrapper("/home/xxx/Desktop/xxx/MPQ/test_image_analyzer_files/Test_Data/")
            self.listener.moveToThread(self)
    
        def run(self):
            if self.listener.can_listen:
                print("start listener thread")
                self.listener.wait_for_file()
            else:
                print("can't listen: no listener thread")
                pass
    
    
    _qtlistener = None
    
    
    def get_qt_listener():
        global _qtlistener
        if _qtlistener is not None:
            return _qtlistener
        _qtlistener = QtEventSubscriber()
        _qtlistener.start()
        print(_qtlistener.listener.thread(), _qtlistener)
        return _qtlistener
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist doch das gleiche Problem wie beim letzten mal. Warum der neue Post?

Deine Vermutung ist falsch. Das die Queue blockiert ist schon richtig, denn wenn nichts da ist, kannst du eh auch nichts anderes machen, als wieder zu pruefen, ob etwas angekommen ist. Das verschwendet dann einfach nur CPU-Zyklen, ohne Wert.

Das ganze scheint ausserdem eh nur mit einer Datei umgehen zu koennen, denn wait_for_file beendet sich, wenn schon ein ein einziges File gefunden wurde.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

So, das hier ist ein eigenstaendiges Beispiel, das funktioniert. Als erstes Argument an das Skript bekommt es den Pfad, den es ueberwachen soll.

Code: Alles auswählen

import sys
import time
import queue
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

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)

    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()
            if event.event_type == "created" and event.src_path.lower().endswith(".hdf5"):
                self.new_file.emit(event.src_path)

class GuiObject(QObject):

    def receive_hdf5_file(self, path):
        print("receive_hdf5_file", path)


def main():
    thread = QThread()
    worker = Worker(sys.argv[1])
    worker.moveToThread(thread)
    thread.started.connect(worker.work)
    thread.start()

    gui_object = GuiObject()
    # Important, this connection needs to happend *AFTER* the
    # moveToThread!
    worker.new_file.connect(gui_object.receive_hdf5_file)
    app = QApplication(sys.argv)
    app.exec_()

if __name__ == '__main__':
    main()
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

__deets__ hat geschrieben: Donnerstag 28. Oktober 2021, 11:10 Das ganze scheint ausserdem eh nur mit einer Datei umgehen zu koennen, denn wait_for_file beendet sich, wenn schon ein ein einziges File gefunden wurde.

Das ist was ich versuche zu lösen. Dein Beispiel ist schon sehr hilfreich (vielen Dank dafür) aber das einzige Ding, das ich nicht lösen kann, ist, dass `wait_for_file` noch für weitere Daten suchen soll. In der `run()` Funktion gibt es `self.listener.wait_for_file()`, das noch "listening" soll aber nach einer Datei hört es auf.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Weil du das so programmiert hast. Das ist alles sehr konvolut & ich würde von meinem Beispiel ausgehen, das belegbar Signale verschickt wenn eine Datei angelegt wird. Denn einen Weg das bestehende umzumodeln kann ich nicht erkennen.
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

Danke für die Hilfe. Ich habe Vorschritt gemacht aber der neue Code hat das gleiche Problem: neue Dateien werden nicht anerkannt. Hier ist der Code:

Code: Alles auswählen

       thread = QThread(parent=self)
        print('try to connect to event service ...')
        worker = watchdog_search.Worker("/home/xxx/Desktop/xxx/MPQ/test_image_analyzer_files/Test_Data/")
        worker.moveToThread(thread)
        thread.started.connect(worker.work)
        thread.start()
        worker.new_file.connect(self.on_finished_run)

Code: Alles auswählen

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):
        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
        event = self._q.get() 
        while True:
            #event = self._q.get()
            try:
                file = h5py.File(event.src_path, "r")
                file.close()
                self.new_file.emit(event.src_path, os.path.basename(event.src_path))
                break  # return file_path, file_name
            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")

            except Exception as err:
                print(f"Got unexpected Error <{type(err).__name__}> while opening <{event.src_path}> ")
                traceback.print_exc()


Ein Paar DInge:

1. Ich musste `event = self._q.get()` aus der while Schleife tun, sodass es funktionieren konnte.
2. Wie du wieder siehst, habe ich dieses `try` hinzugefügt und `event.event_type == "created"` entfernt. Der Grund dafür, ist, dass `event.event_type == "created"` mit `.hdf5` Dateien nicht funktioniert. `hdf5` Dateien brauchen eine bestimmt Zeit, um verwendet zu werden. Ohne diese Zeit wird `event.event_type == "created"` eine nicht fertig geschriebene Datei zum Programm weiterschicken aber dann nichts wird funktionieren. Wenn ich dann dieses `try` hinzugefügt habe, hat das Programm wieder funktioniert allerdings nur mit einer Datei (wie am Anfang). Das ergibt natürlich Sinn -- `try` macht ein `break`. Ich habe versucht dieses Problem mit einer neuen Klasse zu lösen aber meine Idee hat nicht geklappt. Hier ist meine Idee:


Code: Alles auswählen

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)
        self.warte = Wait()
        # 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()
            if self.warte.waitt(event) and event.src_path.lower().endswith(".hdf5"):
                self.new_file.emit(event.src_path, os.path.basename(event.src_path))

class Wait():

    def __init__(self):
        self.max_retry_count = 3500  # for test purposes now but want to set an upper bound on verifying a file is finished.
        self.retry_interval_seconds = .01  # every hundreth it will try the file to see if it finished writing
        self.retry_count = 0
    def waitt(self, event):
        try:
            file = h5py.File(event.src_path, "r")
            file.close()
            return True 

        except OSError:
            if self.retry_count < self.max_retry_count:
                self.retry_count += 1
                print(f"h5 file <{event.src_path}> is locked, retrying {self.retry_count}/{self.max_retry_count}")
                time.sleep(self.retry_interval_seconds)
                return False
            else:
                print(f"h5 file <{event.src_path}> reached max retry count, skipping")
                return False

        except Exception as err:
            print(f"Got unexpected Error <{type(err).__name__}> while opening <{event.src_path}> ")
            traceback.print_exc()
            return False


Die `while True` Schleife macht aber keine Schleife. Ich habe ein `print("aaaa")vor der if-Schleife getan und es wird nur einmal geprintet. Ich verstehe nicht, warum das nicht klappt.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was machst du denn da alles in dieser Methode? Warum schickst du nicht einfach das Signal? Was macht dieses Monstrum an code? Und was glaubst du ist die Wirkung eines breaks in einer Schleife?
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pyhill00: Natürlich funktioniert das nur einmal wenn Du nur *einmal* *vor* der Schleife ein Ereignis aus der Queue holst. Warum hast Du das vor die Schleife verschoben?

Und hör mal auf dem komischen Impuls nachzugeben tausend neue sinnfreie Trivialklassen zu basteln. Das ist Python und nicht Java.
„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

@__deets__ ich habe klar in der Post geschrieben was der Code mit "try" macht und ich habe auch erklärt, dass es nicht gut ist, break zu benutzen aber sonst wird die while Schleife im try bleiben.

@__blackjack__ das war nur zum Debugging. Es soll in der Schleife sein.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pyhill00: Was meinst Du mit „sonst wird die Schleife im ``try`` bleiben“? Soll sie doch. Nur halt nicht immer mit dem selben `event`, also pack das abfragen der Queue da wieder in die Schleife.
„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

@__blackjack__ das habe ich schon gemacht:

Code: Alles auswählen

  while True:
            event = self._q.get()
            while True:
                try:
                    file = h5py.File(event.src_path, "r")
                    file.close()
                    self.new_file.emit(event.src_path, os.path.basename(event.src_path))
                    print("a") 
                   # break  
                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")

                except Exception as err:
                    print(f"Got unexpected Error <{type(err).__name__}> while opening <{event.src_path}> ")
                    traceback.print_exc()
Aber ja eben: es soll nur einmal ein event schicken aber es hört nicht auf das erste event zu schicken.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es gibt kein Break in try/except. Wenn das stimmte, hätte man hier eine endlose Ausgabe.

Code: Alles auswählen

while True:
    try:
        print('einmal')
        break
    except:
        pass
Hat man aber nicht. Darum ist auch klar, warum diese while Schleife sinnlos ist. Das ist also nicht nur nicht gut, es ist falsch.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und nochmal eine Anmerkung zu deinem HDF5-Problem: das hat erstmal mit HDF5 nichts zu tun. Sondern einfach damit, dass es kein Event für “fertig geschrieben” gibt. Das könnte auch eine Datei mit nur einem Buchstaben drin sein. Wenn der erst nach 10 Minuten nach dem öffnen geschrieben wird.

Um sowas zu lösen, macht man entweder atomare Umbenennungen, wenn man Kontrolle über den schreibprozess hat. Hast du die?

Oder man steckt die Kandidaten in eine Datenstruktur, und der sie dann periodisch geprüft werden. Mit einem Timer, der da regelmäßig reinschaut. Nicht so einen Monster-Verhau aus drölfzig Klassen.
pyhill00
User
Beiträge: 30
Registriert: Mittwoch 20. März 2019, 22:39

__deets__ hat geschrieben: Montag 1. November 2021, 20:51 Um sowas zu lösen, macht man entweder atomare Umbenennungen, wenn man Kontrolle über den schreibprozess hat. Hast du die?
Ne habe ich nicht.

Dann die Idee mit der Datenstruktur. Kannst du mir einen Tipp geben, wie ich damit anfangen soll?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zb ein Wörterbuch, mit Schlüssel Namen der Datei, und Wert die Anzahl der Versuche, die man probiert hat, die Datei zu öffnen. Wenn die eine schwellwert übersteigt, wird der Eintrag einfach entfernt. Genauso wie bei Erfolg, nur das dann die Datei zusätzlich per weiterem Signal als “kann geöffnet werden” vermeldet wird. Und diese Datenstruktur prüft man eben via QTimer alle paar Sekunden ab.
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: 14528
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
Antworten