Verzeichnis/Dateiliste als tex-Datei ausgeben

Du hast eine Idee für ein Projekt?
BlackJack

@Melewo: Ein paar Anmerkungen zu Deinem Programm (ist etwas lang geworden — nicht erschrecken :-)).

Das Modul lässt sich nicht importieren ohne dass das Programm los läuft und `instanz` ist eine globale Variable. Ersteres verhindert das man das Modul in einer Python-Shell importieren und einzelne Funktionen oder Klassen testen oder in anderen Modulen verwenden kann. Auch Werkzeuge die Informationen per ”introspection” aus dem Modul ziehen, wie Sphinx, das Dokumentationswerkzeug welches viele Python-Projekte verwenden und mit dem auch die Python-Dokumentation erstellt wird, funktionieren nur wenn man Module importieren kann. Ebenso Testwerkzeuge für Unit- und Doctests.

`instanz` ist kein guter Name, denn der ist so generisch das er für wirklich *jeden* Wert in Python geeignet ist. Mich persönlich stört auch, dass es eine falsche Übersetzung für „instance“ ist. Oder wäre, wenn man sich nicht der ”Dummheit” der Masse beugt. :-)

Die Aufteilung des Codes in `Dateisuche` würde ich als falsch ansehen. Ein Objekt sollte nach Ablauf der `__init__()` in einem benutzbaren Zustand sein. Bei `Dateisuche` *muss* man zwingend die `layout()`-Methode aufrufen, wenn man das aber immer zwingend machen muss, dann stellt sich die Frage warum man den Benutzer der Klasse dazu zwingt. Ein guter Grund für eine Zweiteilung ist das die `__init__()` zum Aufrufer zurückkehren sollte, damit der gegebenfalls noch etwas mit dem Objekt machen kann, wenn er möchte. Also in diesem Fall bevor die GUI-Hauptschleife läuft. Aber nach der `__init__()` der Klasse die das Hauptfenster stellt, sollte das Hauptfenster auch komplett sein, soweit es diese Klasse angeht. Das heisst so ziemlich alles aus der `layout()`-Methode sollte im Zuge von `__init__()` passieren. Da die `layout()`-Methode dann nur noch den Aufruf der GUI-Hauptschleife enthält, wäre dafür der Name `mainloop()` passender. Und man könnte sich diese Methode ganz sparen wenn man die Klasse die das Hauptfenster repräsentiert von `Tk` erben lässt, was ja allgemein das Hauptfenster ist und diese Methode bereits besitzt.

Das die `StrinVar`-Objekte mit dem Wert '1' initialisiert werden damit *keine* Auswahl bei den Radiobuttons angezeigt wird ist irreführend, denn wenn der Benutzer *nichts* auswählt, dann werden ja die Werte 'alphabetisch' und 'py' verwendet. Warum wird das dem Benutzer nicht angezeigt? Wenn in der GUI kein Radiobutton ausgewählt ist, dann würde ich als Benutzer ”unsortiert” und Dateien mit beliebiger Endung erwarten.

Hinter `set_auswahl` würde man eine „setter“-Methode erwarten, aber kein `StringVar`-Objekt. Zudem ist das eine Mischung aus Englisch und Deutsch innerhalb eines Namens. `vondate` und `bisdate` ebenfalls, und ohne Trennung zwischen den beiden Namen. Insgesamt sollte man bei *einer* Sprache bleiben um Verwirrungen zu vermeiden. `auswahl` ist auch zu generisch weil der Name nicht verrät *was* da ausgewählt wird — das Sortierkriterium.

`durchlaufe` klingt auch eher nach einer Tätigkeit und kaum jemand würde den Startpfad für die Suche hinter dem Namen vermuten.

`frame_li` und `frame_re` enthalten Abkürzungen und das für ”Ortsangaben” in der GUI die so nicht unbedingt sein müssen. Man könnte sich später auch entscheiden die anders darzustellen, zum Beispiel über- statt nebeneinander, dann stimmen die Namen nicht mehr. Man könnte beide auch einfach nur `frame` nennen, wenn man sie nicht ”auf Vorrat” am Anfang definieren würde, sondern erst wenn man sie benötigt. Oder man verwendet Namen die den Inhalt beschreiben wie `result_frame` und `controls_frame` oder etwas in der Art.

Bei `scrollbr` fehlt ein 'a' — und jetzt sag bitte nicht das das kein Versehen war, sondern das Du diesen einen Konsonanten absichtlich weggelassen hast. ;-)

`drei_buttons` und `sechs_buttons`? Da werden keine `Button`\s dran gebunden und die `drei_*` stimmt noch nicht einmal, denn das Wörterbuch hat vier Einträge. Ausser in der allerneuesten Python 3-Version ist ein Wörterbuch auch keine gute Idee weil die Werte dort keine feste Reihenfolge haben. Die Reihenfolge der Radiobuttons kann also bei jedem Programmstart anders sein. Eine Liste mit Tupeln wäre hier eine sinnvollere Wahl.

`key2` und `wert2` muss auch nicht sein, da kann man `key` und `wert` für schreiben. Nach welchem Kriterium wird entschieden welchen Namen man in Deutsch und welchen in Englisch verwendet? Und wonach ob man Zahlen oder Buchstaben anhängt (`radioba`/`radiobb`)?

`radioba.bind()` und `radiobb.bind()` beziehen sich jeweils nur auf den *letzten* der erzeugten Radiobuttons, das macht keinen Sinn.

Das Erzeugen der Radiobuttons passiert in zwei fast identischen Code-Blöcken. Code- und Datenwiederholung vermeidet man als Programmierer. Programme werden dadurch schwieriger zu warten und damit fehleranfälliger, denn wenn man etwas ändert, muss immer daran denken es in allen Kopien zu ändern und aufpassen das man es überall wirklich gleich ändert.

`uebernehme_auswahl()` sollte nicht lokal definiert werden. Die Funktion ist noch nicht einmal komplett ein Closure weil auf die Hälfte der Werte ”direkt” zugegriffen wird und auf die andere über `self`. Letztlich ist die Funktion aber auch komplett überflüssig, denn warum im Hintergrund Werte übernehmen, wenn man das auch erst dann machen kann wenn man sie *braucht*. Also `von` und `bis` auch an das Objekt binden, und die ganzen redundanten Attribute loswerden und in der `main()` erst alle Werte aus der GUI auslesen.

Der Name `main()` für eine Methode die nicht das Hauptprogramm enthält ist falsch.

Die `suche_files()` ist in gewisser Weise mit der GUI vermischt, denn sie bekommt nahezu ungeprüfte Nutzereingaben. Sie sollte Datumsobjekte bekommen und nicht vom Benutzer eingegebene Zeichenketten. Die Eingabe entgegen zu nehmen zu prüfen und zu konvertieren ist Aufgabe der GUI. Die Daten werden auch ständig aufs neue konvertiert für jeden Vergleich.

Beim Anzeigen des Ergebnisses ist der Name `dateien` falsch weil der nur an einen Dateinamen gebunden wird. Letztlich ist es auch nicht besonders effizient die Namen alle einzeln in das `Text`-Widget zu schreiben und es ist auch ziemlich verwirrend das die in umgekehrter Reihenfolge ausgegeben werden. Ich habe mich zuerst sehr gewundert warum `suche_files()` immer alles `reverse` sortiert bis ich das gesehen habe. Damit rechnet niemand und die Programmlogik sollte nichts über solche Details wissen müssen. Die Funktion formatiert auch die Ergebnisse als Zeichenketten — im Grunde ebenfalls Aufgabe der GUI.

Für die Sortierkriterien würde ich Konstanten für die Zeichenketten einführen, oder noch besser ein `enum.Enum` erstellen.

Das `file_dict` ist ein Fehler wenn nach Zeit sortiert wird, denn Du bildest dort Zeiten auf Dateinamen ab. Das funktioniert nur wenn alle Dateien garantiert unterschiedliche Zeiten haben, was so ganz und gar nicht garantiert ist. Es gibt ``touch`` oder Dateisysteme mit einer relativ niedrigen Zeitauflösung was die Metadaten angeht, da kann man ganz locker viele Dateien mit der gleichen Zeit haben. Es ist auch verwirrend das in manchen Fällen in diesem Wörterbuch Zeiten auf Namen abgebildet werden und in anderen Namen auf Zeiten. Die Ergebniswerte sollten immer die gleiche Reihenfolge haben.

`verzeichnisse` wird nur an *einen* Verzeichnispfad gebunden. Genau so falsch ist `dateinamen` in der inneren Schleife und später `filetimes`.

Es wird (zu) oft `os.path.join()` mit den gleichen Werten gemacht. Also sowohl der Code dafür als auch die tatsächliche Ausführung. Das macht man einmal am Anfang und gut ist.

Das filtern nach Anfangs-/Endzeit ist komisch gelöst. Erstens passiert es nur wenn nicht nach Name sortiert wird. Warum dann nicht? Nichts in der GUI deutet auf diese Einschränkung hin und ich finde das sehr überraschend. Dann müssen entweder beide Endpunkte eingegeben worden sein. Wird nur einer eingegeben, bleibt das Ergebnis einfach leer‽ Auch etwas unerwartet für meinen Geschmack. Man kann doch auch nur eine Grenze setzen wollen und das macht doch auch total Sinn nur nach Dateien zu suchen die älter als X sind oder jünger als Y.

Wenn man das generell für alle Sortierkriterien macht, dann bleibt in den ganzen ``if``/``elif``-Zweigen nur noch das ermitteln von `zeitpunkt` und es unterscheiden sich nur der Test auf die Sortierkonstante und die Funktion die zum ermitteln der Zeit verwendet wird. Das kann man ohne das ``if``/``elif``/… lösen mit einem Wörterbuch das die Sortierkonstanten auf die Funktionen abbildet. Schon hat man nur noch eine Stelle im Code wo Datei + Zeit zur Ergebnisliste hinzugefügt wird.

Anstelle von einer Zahl oder einer Zeichenkette wären Datums-/Zeitangaben besser als `datetime.datetime` repräsentiert. Die kann man auch vergleichen, die `repr()`-Ausgabe ist lesbarer, und man kann die `format()`-Methode verwenden.

So als zwischenergebnis lande ich erst einmal hier (kaum getestet):

Code: Alles auswählen

import os
from datetime import datetime as DateTime
from tkinter import (
    Tk, Frame, Label, Button, Text, Scrollbar, Radiobutton, Entry,
    StringVar, END, LEFT, RIGHT, Y, W
)
# 
# TODO Maybe use `enum.Enum`.
# 
SORT_BY_NAME = 'name'
SORT_BY_CREATION_TIME = 'creation_time'
SORT_BY_CHANGE_TIME = 'change_time'
SORT_BY_ACCESS_TIME = 'access_time'
SORT_BY_TIME = [SORT_BY_CREATION_TIME, SORT_BY_CHANGE_TIME, SORT_BY_ACCESS_TIME]

SORT_BY_TO_GET_TIME_FUNC = {
    SORT_BY_NAME: os.path.getctime,
    SORT_BY_CREATION_TIME: os.path.getctime,
    SORT_BY_CHANGE_TIME: os.path.getmtime,
    SORT_BY_ACCESS_TIME: os.path.getatime,
}

DATE_FORMAT = '%d.%m.%Y'


def search_files(start_path, sort_by, filename_extension, start_date, end_date):
    # 
    # TODO Own class for the result tuples or at least a
    #      `collections.namedtuple`.
    # 
    os.stat_float_times(False)

    result = list()
    for path, _, filenames in os.walk(start_path):
        for filename in filenames:
            full_path = os.path.join(path, filename)
            if os.path.splitext(filename)[1] == filename_extension:
                file_time = DateTime.fromtimestamp(
                    SORT_BY_TO_GET_TIME_FUNC[sort_by](full_path)
                )
                if (
                    (not start_date or start_date < file_time)
                    and (not end_date or file_time <= end_date)
                ):
                    result.append((full_path, file_time))

    if sort_by == SORT_BY_NAME:
        result.sort(key=lambda x: x[0].lower())
    elif sort_by in SORT_BY_TIME:
        result.sort(key=lambda x: x[1])
    else:
        raise ValueError('unknown sort criterion {0!r}'.format(sort_by))

    return result


class Dateisuche(Tk):

    def __init__(self):
        Tk.__init__(self)
        self.title('Dateisuche')
        self['bg'] = 'gray50'

        self.sort_criterion = StringVar(value=SORT_BY_NAME)
        self.filename_extension = StringVar(value='.py')
        self.start_path = '.'
        # 
        # Linker Frame.
        # 
        frame = Frame(self, bg='gray50', padx=2)
        self.result_text = Text(frame, width=92, height=38)
        self.result_text.pack(pady=0, padx=2)
        scrollbar = Scrollbar(frame)
        scrollbar.config(command=self.result_text.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.result_text.config(yscrollcommand=scrollbar.set)
        frame.pack(side=LEFT)
        # 
        # Rechter Frame.
        # 
        self.background_color = '#6f6352'
        self.font_name = 'cambria'
        frame = Frame(self, bg=self.background_color, padx=2)

        self._create_radiobuttons(
            frame,
            'Auswahl Sortierung',
            self.sort_criterion,
            [
                ('Alphabetisch', SORT_BY_NAME),
                ('Datei angelegt', SORT_BY_CREATION_TIME),
                ('Letzte Änderung', SORT_BY_CHANGE_TIME),
                ('Letzter Zugriff', SORT_BY_ACCESS_TIME),
            ],
        )
        self._create_radiobuttons(
            frame,
            'Auswahl Endung',
            self.filename_extension,
            [
                ('doc', '.doc'),
                ('docx', '.docx'),
                ('htm', '.htm'),
                ('html', '.html'),
                ('pdf', '.pdf'),
                ('py', '.py'),
            ],
        )

        self._create_headline(frame, 'Auswahl Zeitraum')
        options = dict(
            bg=self.background_color,
            font=(self.font_name, 10),
            fg='#ffce58',
            justify=LEFT,
        )
        Label(
            frame, text='Beispiel: 28.06.2017\n\nVon:', **options
        ).pack(padx=10, anchor=W)
        self.start_date_entry = Entry(frame)
        self.start_date_entry.pack(padx=10, anchor=W)

        Label(frame, text='Bis:', **options).pack(padx=10, anchor=W)
        self.end_date_entry = Entry(frame)
        self.end_date_entry.pack(padx=10, anchor=W)

        search_button = Button(
            frame,
            text='Suche',
            font=(self.font_name, 10, 'bold'),
            padx=18,
            command=self.do_search,
        )
        search_button.pack(pady=18)

        frame.pack(side=LEFT, fill=Y)

    def _create_headline(self, parent, text):
        Label(
            parent,
            text=text,
            fg='#ffe9b3',
            bg=self.background_color,
            font=(self.font_name, 11),
            justify=LEFT,
        ).pack(padx=10, pady=12, anchor=W)

    def _create_radiobuttons(self, parent, headline_text, variable, choices):
        self._create_headline(parent, headline_text)
        for text, value in choices:
            Radiobutton(
                parent,
                text=text,
                fg='#ffce58',
                bg=self.background_color,
                selectcolor='#606060',
                activebackground=self.background_color,
                variable=variable,
                value=value,
            ).pack(padx=8, anchor=W)

    def do_search(self):
        try:
            start_date = DateTime.strptime(
                self.start_date_entry.get(), DATE_FORMAT
            )
        except ValueError:
            start_date = None

        try:
            end_date = DateTime.strptime(self.end_date_entry.get(), DATE_FORMAT)
        except ValueError:
            end_date = None

        paths = search_files(
            self.start_path,
            self.sort_criterion.get(),
            self.filename_extension.get(),
            start_date,
            end_date,
        )
        self.result_text.delete(1.0, END)
        self.result_text.insert(
            1.0, '\n'.join(map('{0[0]} - {0[1]:%d.%m.%Y}'.format, paths))
        )
        self.result_text.insert(END, '\n')


def main():
    Dateisuche().mainloop()


if __name__ == '__main__':
    main()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ja, das ist reichlich viel, gehe jetzt nur auf einige Punkte ein und den Rest muss ich erst verarbeiten.
BlackJack hat geschrieben:Das heisst so ziemlich alles aus der `layout()`-Methode sollte im Zuge von `__init__()` passieren.
Gerade in dieser Beziehung hatte ich mir die Bemerkungen von Sirius3 zu Herzen genommen und auf alles was nicht unbedingt zu sein brauchte, unter __init__ verzichtet. Jedenfalls hatte ich es so aufgefassst.
Sirius3 hat geschrieben:Die ganzen GUI-Elemente an Attribute zu binden, ist unnötig. Eigentlich braucht man bei TK außer ein paar IntVar/StrVar-Objekten fast nichts mehr nach der Initialisierung.
viewtopic.php?f=9&t=40640

Dann:
BlackJack hat geschrieben:Das die `StrinVar`-Objekte mit dem Wert '1' initialisiert werden damit *keine* Auswahl bei den Radiobuttons angezeigt wird ist irreführend, denn wenn der Benutzer *nichts* auswählt, dann werden ja die Werte 'alphabetisch' und 'py' verwendet. Warum wird das dem Benutzer nicht angezeigt?
Die werden bei mir richtig angezeigt, vorher wurden alle mit Punkt angezeigt, jetzt nur noch die ausgewählten, deshalb hatte ich doch nach einer Lösung gesucht und die bei Stack Overflow gefunden.
BlackJack hat geschrieben:Bei `scrollbr` fehlt ein 'a' — und jetzt sag bitte nicht das das kein Versehen war, sondern das Du diesen einen Konsonanten absichtlich weggelassen hast.
Beim ersten Script mit der Animation hatte ich das so geschrieben, bei dem hatte ich den Code nur kopiert und nun sehe ich es.
BlackJack hat geschrieben:Das Erzeugen der Radiobuttons passiert in zwei fast identischen Code-Blöcken.
Sind zwischenzeitlich nicht mehr und die unteren wurden ersetzt für eine Mehrfachauswahl mit Checkbuttons für Endungen.

Das ist der Stand von gestern Abend/heute Morgen und außer das a bei scrollbar habe ich jetzt nichts mehr verändert, weil ich mir erst noch einmal alles durchlesen und anschauen möchte, was Du geschrieben und geändert hast.

Code: Alles auswählen

from tkinter import Tk, Frame, Label, Button, Text, Scrollbar, Radiobutton, \
                    Checkbutton, Entry, StringVar, IntVar, END
from tkinter.filedialog import asksaveasfile
import os
import time

# Wandelt Datum 00.00.0000 in Timestamp
def wandle_datum(data):
    return int(time.mktime(time.strptime(data, "%d.%m.%Y")))

# Wandelt Timestamp in Datum 00.00.0000
def wandle_zeitformat(filetimes):
    return time.strftime("%d.%m.%Y", time.localtime(filetimes))

# Formatierung für Listeneintrag, wobei .\\ entfernt wird.
def formatiere_string(files, zeitformat):
    return "{0:s} - ({1:s})".format(os.path.relpath(files), zeitformat)

# Die eigentliche Sortier- und Suchfunktion
def suche_files(durchlaufe, suche, auswahl, endungsliste, vondate, bisdate):
    os.stat_float_times(False)
    file_dict = {}
    suchliste = []
    rueckgabe = []

    # Suche in Dateien und zähle Zeilen mit Treffern
    def zaehle_treffer(file, gesucht, zeit):
        treffer = 0
        with open(file, "r") as datei:
            for zeile in datei:
                if gesucht in zeile:
                    treffer += 1

        if treffer > 0:
            # Einfügen mit Anzahl Treffer - Pfad/Dateiname (Datum) in Liste
            suchliste.append("{0:s} - {1:s} - ({2:s})".format(
                str(treffer), os.path.relpath(file), wandle_zeitformat(zeit)))  

    for verzeichnisse, unterordner, dateien in os.walk(durchlaufe):
        for dateinamen in dateien:
            extension = os.path.splitext(dateinamen)[1]
            if extension in endungsliste:
                pfad = os.path.join(verzeichnisse, dateinamen)
                
                # Ohne Sucheingabe: Pfad/Dateiname als Schlüssel
                if auswahl == "alphabetisch":
                    zeitpunkt = os.path.getctime(pfad)
                    if suche == False:
                        file_dict[pfad] = zeitpunkt
                    else:
                        zaehle_treffer(pfad, suche, zeitpunkt)
                elif auswahl == "angelegt":
                    zeitpunkt = os.path.getctime(pfad)
                elif auswahl == "aenderung":
                    zeitpunkt = os.path.getmtime(pfad)
                elif auswahl == "aufgerufen":
                    zeitpunkt = os.path.getatime(pfad)
                    
                # Ohne Sucheingabe: Timestamp als Schlüssel
                if (auswahl == "angelegt" or auswahl == "aenderung"
                        or auswahl == "aufgerufen"):
                    if vondate == False or bisdate == False:
                        if suche == False:
                            file_dict[zeitpunkt] = pfad
                        else:
                            zaehle_treffer(pfad, suche, zeitpunkt)
                    elif vondate != False and bisdate != False:
                        # Verglichen werden die einzelnen Timestamps
                        if (zeitpunkt >= wandle_datum(vondate) and 
                            zeitpunkt <= wandle_datum(bisdate)):
                            if suche == False:
                                file_dict[zeitpunkt] = pfad
                            else:
                                zaehle_treffer(pfad, suche, zeitpunkt)                            

    # Nur ausführen, falls ein Suchbegriff übergegeben wurde.
    if suche != False:
        rueckgabe = sorted(suchliste, reverse = True)

    # Reihenfolge im Schleifenkopf: files und filetimes
    elif auswahl == "alphabetisch":
        sortiert = sorted(file_dict.items(), key = lambda i: i[0].lower())

        for files, filetimes in sortiert:
            zeitformat = wandle_zeitformat(filetimes)
            rueckgabe.append(formatiere_string(files, zeitformat))

    # Reihenfolge im Schleifenkopf: filetimes und files
    elif (auswahl == "angelegt" or auswahl == "aenderung" 
            or auswahl == "aufgerufen"):
        sortiert = sorted(file_dict.items(), reverse = True)

        for filetimes, files in sortiert:
            zeitformat = wandle_zeitformat(filetimes)
            rueckgabe.append(formatiere_string(files, zeitformat))

    return rueckgabe

class Dateisuche:

    def __init__(self):
        self.fenster = Tk()
        self.durchlaufe = "."            # Startet Durchlauf oder Suche bei "."
        self.endungslis = []             # Liste mit ausgewählten Dateiendungen
        self.sortierung = StringVar(value="1")
        self.textfeld = None
        self.suche_in = None
        self.warnung  = None
        self.von = None
        self.bis = None
        self.auswahl_buttons = {
            "Alphabetisch" : "alphabetisch",
            "Datei angelegt"  : "angelegt",
            "Letzte Änderung" : "aenderung",
            "Letzter Zugriff" : "aufgerufen"
        }
        self.endungen = {
            "doc" : 0,
            "docx": 0,
            "pdf" : 0,
            "txt *" : 0,
            "htm *" : 0,
            "html *": 0,
            "py *"  : 0,
            "pyw *" : 0,
            "php *" : 0
        }

    def main(self):
        hinweise = ""
        # Ein Suchbegriff braucht nicht unbedingt gesetzt zu sein.
        suche_nach = self.suche_in.get()

        if suche_nach is None or suche_nach == "":
            suche_nach = False
            hinweise += "Ein Suchbegriff wurde nicht eingegeben.\n"
        elif len(suche_nach) < 3:
            suche_nach = False
            hinweise += "Der eingegebene Suchbegriff war zu kurz.\n"
        else:
            hinweise += "Suchbegriff: {0:s}\n".format(suche_nach)

        # Sortierungsmethode auf alphabetische oder zeitliche Auswahl pruefen.
        sortmethode = self.sortierung.get()
        sortmethode = ("alphabetisch" if sortmethode == "1" else sortmethode)

        # Nur für Ausgabe der Hinweise
        if suche_nach == False:
            hinweise += "Sortierungsmethode: {0:s} \n".format(sortmethode)
        else:
            hinweise += "Sortierungsmethode: Anzahl Zeilen mit Treffern.\n"

        # Endungsliste leeren, damit bereits ausgwählte Endungen nicht bei
        # jedem weiteren Klick erneut hinzugefügt werden.
        self.endungslis = []
        # Auswahl von Dateiendungen pruefen und notfalls abbrechen.
        for key, value in self.endungen.items():
            status = value.get()
            if status != False:
                endung = ".{0:s}".format(key.replace(" *", ""))
                self.endungslis.append(endung)

        if not self.endungslis:
            self.warnung = True
            warnhinweis  = "Es wurde keine Endung ausgewählt!\n"
        else:
            self.warnung = False
            hinweise += "Ausgewählte Endungen: {}\n".format(self.endungslis)

        # Auf Auswahl eines Zeitraums pruefen.
        vondate = self.von.get()
        bisdate = self.bis.get()

        if vondate is None or vondate == "":
            vondate = False
            vondato = "?"
        else:
            vondato = vondate
        if bisdate is None or bisdate == "":
            bisdate = False
            bisdato = "?"
        else:
            bisdato = bisdate
        # Nur für Ausgabe der Hinweise
        hinweise += "Zeitraum: {0:s} - {0:s}\n".format(vondato, bisdato)

        # Aufruf der Sortier- und Suchfunktion
        if self.warnung == False:
            gefunden = suche_files(self.durchlaufe, suche_nach, sortmethode,
                       self.endungslis, vondate, bisdate)

        # Ausgabe der Hinweise
        self.textfeld.delete(1.0, END)
        if self.warnung == True:
            self.textfeld.insert(1.0, "{0:s}\n".format(warnhinweis))
        else:
            self.textfeld.insert(1.0, "{0:s}\n".format(hinweise))
            # Die von suche_files() zurückgegebene Liste am Ende einfügen.
            for dateien in gefunden:
                self.textfeld.insert(END, "{0:s}\n".format(dateien))

    def speichere_suchergebnis(self):
        datei = asksaveasfile(mode = "a", 
                              filetypes = [("Text Datei", "*.txt")])
        if datei is not None:
            datei.write(self.textfeld.get("1.0", END))
            datei.close()
            self.textfeld.delete("1.0", END)
            self.textfeld.insert(END, "Gespeichert!")
            
    def layout(self):
        self.fenster.title("Dateisuche")
        self.fenster.geometry("980x740")
        self.fenster["bg"] = "#808080"

        # Aufteilung in linken und rechten Frame
        frame_li = Frame(self.fenster, bg = "#808080", padx = 2, pady = 4)
        frame_re = Frame(self.fenster, bg = "#6f6352", padx = 2, pady = 4)

        # Linker Frame:
        # Textfeld für Hinweise, Meldungen und Ergebnisse
        self.textfeld = Text(
                        frame_li, pady = 8, padx = 8, width = 98, height = 46)
        scrollbar = Scrollbar(frame_li)
        scrollbar.config(command = self.textfeld.yview)
        self.textfeld.config(yscrollcommand = scrollbar.set)

        frame_li.pack(side = "left")
        scrollbar.pack(side = "right", fill = "y")
        self.textfeld.pack(pady = 0, padx = 2)

        # Rechter Frame:
        # Suche in mit * gekennzeichneten Dateien
        frame_re.pack(side = "right", fill = "y")
        Label(
            frame_re,
            text = "Suche in *",
            bg = "#6f6352",
            font = ("cambria", 11),
            fg = "#ffe9b3",
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        self.suche_in = Entry(frame_re)
        self.suche_in.pack(padx = 10, anchor = "w")

        # Auswahl Sortierungsmethode
        Label(
            frame_re,
            text = "Auswahl Sortierung",
            bg = "#6f6352",
            font = ("cambria", 11),
            fg = "#ffe9b3",
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        # Sortierungsmethode alphabetisch oder nach zeitlichen Vorgaben
        for key, wert in self.auswahl_buttons.items():
            Radiobutton(
                frame_re, text = key,
                bg = "#6f6352",
                fg = "#ffce58",
                selectcolor = "#606060",
                activebackground = "#6f6352",
                variable = self.sortierung,
                value = wert).pack(padx = 8, anchor = "w")

        # Auswahl von Dateiendungen
        Label(
            frame_re,
            text = "Auswahl Endung",
            bg = "#6f6352",
            font = ("cambria", 11),
            fg = "#ffe9b3",
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        # Auswahl von Dateinamenserweiterungen
        for key in self.endungen:
            self.endungen[key] = IntVar()
            check_endung = Checkbutton(
                frame_re,
                text = key,
                bg = "#6f6352",
                fg = "#ffce58",
                selectcolor = "#606060",
                activebackground = "#6f6352",
                variable=self.endungen[key])
            check_endung.pack(padx = 8, anchor = "w")

        # Auswahl eines Zeitraums
        Label(
            frame_re,
            text = "Auswahl Zeitraum",
            bg = "#6f6352",
            font = ("cambria", 11),
            fg = "#ffe9b3",
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        Label(
            frame_re, 
            text = "Beispiel: 28.06.2017\n\nVon:",
            font = ("cambria", 10), 
            fg = "#ffce58", 
            bg = "#6f6352",
            justify = "left").pack(padx = 10, anchor = "w")
        self.von = Entry(frame_re)
        self.von.pack(padx = 10, anchor = "w")
        Label(
            frame_re,
            text = "Bis:",
            bg = "#6f6352",
            font = ("cambria", 10),
            fg = "#ffce58",
            justify = "left").pack(padx = 10, anchor = "w")
        self.bis = Entry(frame_re)
        self.bis.pack(padx = 10, anchor = "w")

        # Durchlauf mit Sortierung oder Suche starten
        Button(
            frame_re, 
            text = "Suche",
            font = ("cambria", 10, "bold"), padx = 20,
            command = self.main).pack(side = "top", pady = 10)
        # Speichern unter ... 
        Button(
            frame_re, 
            text = "Speichern",
            font = ("cambria", 10, "bold"), padx = 8,
            command = self.speichere_suchergebnis).pack(side = "top")

        self.fenster.mainloop()

if __name__ == "__main__":
    instanz = Dateisuche()
    instanz.layout()
BlackJack

Sirius3 hat im anderen Thema gesagt das nicht alles an das Objekt gebunden werden muss, nicht das es nicht in der `__init__()` passieren sollte.

Zum `StringVar`-Wert '1': Mir ist schon klar was Du da gemacht hast, aber ich bin der Meinung das ist falsch. Denn wenn Du die (oder im neuen das eine) auf '1' setzt dann wird beim starten der GUI *kein* Radiobutton ausgewählt dargestellt. Aber wenn man selber nichts auswählt und auf die Suchen-Fläche klickt, dann wird ja sortiert: alphabetisch. Und darum sollte das auch beim Start des Programm ausgewählt sein, damit der Benutzer das *sieht* das das passiert. Und als die Endungen noch Radiobuttons waren, galt das auch für *.py als Endung — danach wurde gesucht wenn man nichts ausgewählt hat, aber es wurde nicht in der GUI angezeigt.

Jetzt im neuen könntest Du Dir Zeile 145 sparen wenn da nicht mit '1' sondern mit 'alphabetisch' initialisiert werden würde, und der Benutzer würde von Anfang sehen nach was sortiert wird wenn er selber nichts anderes auswählt.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ich bin ja noch nicht fertig, werde in den nächsten Tagen noch einmal alles durchgehen und schauen, was sich noch verbessern lässt. Erkenne z.B. keinen zeitlichen Unterschied zwischen getctime und getatime, so dass minimal meine Bezeichnung falsch ist oder vom System abhängig.

Augenblicklich ist es noch so, dass das Script nach dem Start noch nichts ausgibt, also nur ein leeres Textfeld und minimal eine Endung ausgewählt werden muss. Wird der Button ohne Auswahl einer Endung betätigt, wird die Funktion in Zeile 189 nicht aufgerufen und stattdessen in Zeile 195 nur der Hinweis "Es wurde keine Endung ausgewählt!" aus Zeile 165 ins Textfeld geschrieben. Wurde mindestens eine Endung ausgewählt, so ist alles andere optional.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

BlackJack hat geschrieben:`frame_li` und `frame_re` enthalten Abkürzungen und das für ”Ortsangaben” in der GUI die so nicht unbedingt sein müssen. Man könnte sich später auch entscheiden die anders darzustellen, zum Beispiel über- statt nebeneinander, dann stimmen die Namen nicht mehr.
Na ja, doch das kann sich eigentlich derjenige umgestalten oder umbenennen, den mein Design nicht passt. Mir gefällt die Aufteilung so wie sie ist, sonst hätte ich das Fenster anders gestaltet.
BlackJack hat geschrieben:Letztlich ist es auch nicht besonders effizient die Namen alle einzeln in das `Text`-Widget zu schreiben
Diesen Punkt habe ich noch geändert, hielt ich es selbst nicht für eine gute Lösung, musste nur erst einmal schauen, wie das besser geschrieben werden könnte und wusste es bis vor wenigen Tagen noch nicht besser. Ich denke, dass es nun besser ist.

Was ich noch geändert habe, wenn ein Suchbegriff mit Umlauten eingegeben wurde, wurden nur Treffer in mit UTF-8 Kodierung gespeicherten Seiten gefunden. nun findet die Treffer in UTF-8 und ISO-Seiten.

Für einen Test das Script in mein htdocs-Verzeichnis auf dem Rechner abgelegt. Windows 10 erzählte mir unter Eigenschaften, dass dieses Verzeichnis 71.378 Dateien in 6.410 Ordner enthalten würde. Gut, die wenigsten davon dürften die Endung *.htm haben, doch ein paar Tausend könnten es auch sein. Jedenfalls um diese 6.410 Ordner zu durchqueren und in allen Dateien mit der Endung *.htm nach dem Suchwort "description" zu suchen, hat die Suche etwa 80 Sekunden benötigt und ich denke, damit kann ich gut leben.

Eigentlich bin ich nun so wie die ist und läuft fürs Erste zufrieden. Einzig, die hätte mir bei der Gelegenheit nicht nur die Seiten mit den meisten Treffern oben listen können, sondern auch die Gesamtzahl der Seiten mit Treffern anzeigen können. Doch darüber mache ich mir vielleicht später noch einmal Gedanken, das kann ja nicht mehr so ein großes Problem sein.

Code: Alles auswählen

from tkinter import Tk, Frame, Label, Button, Text, Scrollbar, Radiobutton, \
                    Checkbutton, Entry, StringVar, IntVar, END
from tkinter.filedialog import asksaveasfile
import time
import os

# Wandelt Datum 00.00.0000 in Timestamp
def wandle_datum(data):
    return int(time.mktime(time.strptime(data, "%d.%m.%Y")))

# Wandelt Timestamp in Datum 00.00.0000
def wandle_zeitformat(filetimes):
    return time.strftime("%d.%m.%Y", time.localtime(filetimes))

# Formatierung für Listeneintrag, wobei .\\ entfernt wird.
def formatiere_string(files, zeitformat):
    return "{0:s} - ({1:s})".format(os.path.relpath(files), zeitformat)

# Umwandlung, falls UTF-8 und ISO-kodierte Dateien mit einem Umlaute 
# enthaltenen Suchbegriff durchsucht werden.
def wandle_umlaute(data):
    umlaute = {
        "\\xc4": "Ä",
        "\\xd6": "Ö",
        "\\xdc": "Ü",
        "\\xe4": "ä",
        "\\xf6": "ö",
        "\\xfc": "ü",
        "\\xdf": "ß"
    }
    for umlaut in umlaute:
        data = data.replace(umlaut, umlaute[umlaut])
    return data.lower()

# Die eigentliche Sortier- und Suchfunktion
def suche_files(durchlaufe, suche, auswahl, endungsliste, vondate, bisdate):
    os.stat_float_times(False)
    file_dict = {}
    suchliste = []
    rueckgabe = []

    # Suche in Dateien und zähle Zeilen mit Treffern
    def zaehle_treffer(file, gesucht, zeit):
        treffer = 0
        with open(file, "r", encoding="utf-8",
                  errors="backslashreplace") as datei:
            for zeile in datei:
                if gesucht.lower() in wandle_umlaute(zeile):
                    treffer += 1

        if treffer > 0:
            # Einfügen mit Anzahl Treffer - Pfad/Dateiname (Datum) in Liste
            suchliste.append("{0:s} - {1:s} - ({2:s})".format(
                str(treffer), os.path.relpath(file), wandle_zeitformat(zeit)))

    for verzeichnisse, unterordner, dateien in os.walk(durchlaufe):
        for dateinamen in dateien:
            extension = os.path.splitext(dateinamen)[1]
            if extension in endungsliste:
                pfad = os.path.join(verzeichnisse, dateinamen)

                # Ohne Sucheingabe: Pfad/Dateiname als Schlüssel
                if auswahl == "alphabetisch":
                    zeitpunkt = os.path.getctime(pfad)
                    if suche == False:
                        file_dict[pfad] = zeitpunkt
                    else:
                        zaehle_treffer(pfad, suche, zeitpunkt)
                elif auswahl == "angelegt":
                    zeitpunkt = os.path.getctime(pfad)
                elif auswahl == "aenderung":
                    zeitpunkt = os.path.getmtime(pfad)
                elif auswahl == "aufgerufen":
                    zeitpunkt = os.path.getatime(pfad)

                # Ohne Sucheingabe: Timestamp als Schlüssel
                if (auswahl == "angelegt" or auswahl == "aenderung"
                        or auswahl == "aufgerufen"):
                    if vondate == False or bisdate == False:
                        if suche == False:
                            file_dict[zeitpunkt] = pfad
                        else:
                            zaehle_treffer(pfad, suche, zeitpunkt)
                    elif vondate != False and bisdate != False:
                        # Verglichen werden die einzelnen Timestamps
                        if (zeitpunkt >= wandle_datum(vondate) and
                            zeitpunkt <= wandle_datum(bisdate)):
                            if suche == False:
                                file_dict[zeitpunkt] = pfad
                            else:
                                zaehle_treffer(pfad, suche, zeitpunkt)

    # Nur ausführen, falls ein Suchbegriff übergegeben wurde.
    if suche != False:
        rueckgabe = sorted(suchliste, reverse = True)

    # Reihenfolge im Schleifenkopf: files und filetimes
    elif auswahl == "alphabetisch":
        sortiert = sorted(file_dict.items(), key = lambda i: i[0].lower())

        for files, filetimes in sortiert:
            zeitformat = wandle_zeitformat(filetimes)
            rueckgabe.append(formatiere_string(files, zeitformat))

    # Reihenfolge im Schleifenkopf: filetimes und files
    elif (auswahl == "angelegt" or auswahl == "aenderung"
            or auswahl == "aufgerufen"):
        sortiert = sorted(file_dict.items(), reverse = True)

        for filetimes, files in sortiert:
            zeitformat = wandle_zeitformat(filetimes)
            rueckgabe.append(formatiere_string(files, zeitformat))

    return rueckgabe

class Dateisuche:

    def __init__(self):
        self.fenster = Tk()
        self.durchlaufe = "."            # Startet Durchlauf oder Suche bei "."
        self.endungslis = []             # Liste mit ausgewählten Dateiendungen
        self.sortierung = StringVar(value="1")
        self.textfeld = None
        self.suche_in = None
        self.warnung  = None
        self.von = None
        self.bis = None
        self.auswahl_buttons = {
            "Alphabetisch": "alphabetisch",
            "Datei angelegt" : "angelegt",
            "Letzte Änderung": "aenderung",
            "Letzter Zugriff": "aufgerufen"
        }
        self.endungen = {
            "doc" : 0,
            "docx": 0,
            "pdf" : 0,
            "txt *" : 0,
            "htm *" : 0,
            "html *": 0,
            "py *"  : 0,
            "pyw *" : 0,
            "php *" : 0
        }

    # Daten von der Eingabe und für Ausgabe prüfen
    def pruefe_daten(self):
        hinweise = ""
        # Ein Suchbegriff braucht nicht unbedingt gesetzt zu sein.
        suche_nach = self.suche_in.get()

        if suche_nach is None or suche_nach == "":
            suche_nach = False
            hinweise += "Ein Suchbegriff wurde nicht eingegeben.\n"
        elif len(suche_nach) < 3:
            suche_nach = False
            hinweise += "Der eingegebene Suchbegriff war zu kurz.\n"
        else:
            hinweise += "Suchbegriff: {0:s}\n".format(suche_nach)

        # Sortierungsmethode auf alphabetische oder zeitliche Auswahl pruefen.
        sortmethode = self.sortierung.get()
        sortmethode = ("alphabetisch" if sortmethode == "1" else sortmethode)

        # Nur für Ausgabe der Hinweise
        if suche_nach == False:
            hinweise += "Sortierungsmethode: {0:s} \n".format(sortmethode)
        else:
            hinweise += "Sortierungsmethode: Anzahl Zeilen mit Treffern.\n"

        # Endungsliste leeren, damit bereits ausgwählte Endungen nicht bei
        # jedem weiteren Klick erneut hinzugefügt werden.
        self.endungslis = []
        # Auswahl von Dateiendungen pruefen und notfalls abbrechen.
        for key, value in self.endungen.items():
            status = value.get()
            if status != False:
                endung = ".{0:s}".format(key.replace(" *", ""))
                self.endungslis.append(endung)

        if not self.endungslis:
            self.warnung = True
            warnhinweis  = "Es wurde keine Endung ausgewählt!\n"
        else:
            self.warnung = False
            hinweise += "Ausgewählte Endungen: {}\n".format(self.endungslis)

        # Auf Auswahl eines Zeitraums pruefen.
        vondate = self.von.get()
        bisdate = self.bis.get()

        if vondate is None or vondate == "":
            vondate = False
            vondato = "?"
        else:
            vondato = vondate
        if bisdate is None or bisdate == "":
            bisdate = False
            bisdato = "?"
        else:
            bisdato = bisdate
        # Nur für Ausgabe der Hinweise
        hinweise += "Zeitraum: {0:s} - {0:s}\n".format(vondato, bisdato)

        # Aufruf der Sortier- und Suchfunktion
        if self.warnung == False:
            gefunden = suche_files(self.durchlaufe, suche_nach, sortmethode,
                       self.endungslis, vondate, bisdate)

        # Ausgabe der Hinweise
        self.textfeld.delete(1.0, END)
        if self.warnung == True:
            self.textfeld.insert(1.0, "{0:s}\n".format(warnhinweis))
        elif gefunden:
            self.textfeld.insert(1.0, "{0:s}\n".format(hinweise))
            # Die von suche_files() zurückgegebene Liste am Ende einfügen.
            self.textfeld.insert(END, "\n".join(map(str, gefunden)))
        else:
            hinweise += "\nEin Fehler trat auf oder " \
                        "es wurden keine Dateien gefunden!"
            self.textfeld.insert(1.0, "{0:s}\n".format(hinweise))

    def speichere_suchergebnis(self):
        datei = asksaveasfile(mode = "a",
                              filetypes = [("Text Datei", "*.txt")])
        if datei is not None:
            datei.write(self.textfeld.get(1.0, END))
            datei.close()
            self.textfeld.delete(1.0, END)
            self.textfeld.insert(END, "Gespeichert!")

    def layout(self):
        self.fenster.title("Meine-Suche")
        self.fenster.geometry("980x740")
        self.fenster["bg"] = "#808080"

        # Aufteilung in linken und rechten Frame
        frame_li = Frame(self.fenster, bg = "#808080", padx = 2, pady = 4)
        frame_re = Frame(self.fenster, bg = "#6f6352", padx = 2, pady = 4)

        # Linker Frame:
        # Textfeld für Hinweise, Meldungen und Ergebnisse
        self.textfeld = Text(
                        frame_li, pady = 8, padx = 8, width = 98, height = 46)
        scrollbar = Scrollbar(frame_li)
        scrollbar.config(command = self.textfeld.yview)
        self.textfeld.config(yscrollcommand = scrollbar.set)
        frame_li.pack(side = "left")
        scrollbar.pack(side = "right", fill = "y")
        self.textfeld.pack(pady = 0, padx = 2)

        # Rechter Frame:
        # Suche in mit * gekennzeichneten Dateien
        frame_re.pack(side = "right", fill = "y")
        Label(
            frame_re,
            text = "Suche in *",
            bg = "#6f6352",
            fg = "#ffe9b3",
            font = ("cambria", 11),
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        self.suche_in = Entry(frame_re)
        self.suche_in.pack(padx = 10, anchor = "w")
        # Auswahl Sortierungsmethode
        Label(
            frame_re,
            text = "Auswahl Sortierung",
            bg = "#6f6352",
            fg = "#ffe9b3",
            font = ("cambria", 11),
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        # Sortierungsmethode alphabetisch oder nach zeitlichen Vorgaben
        for key, wert in self.auswahl_buttons.items():
            Radiobutton(
                frame_re,
                text = key,
                bg = "#6f6352",
                fg = "#ffce58",
                selectcolor = "#606060",
                activebackground = "#6f6352",
                variable = self.sortierung,
                value = wert).pack(padx = 8, anchor = "w")
        # Auswahl von Dateiendungen
        Label(
            frame_re,
            text = "Auswahl Endung",
            bg = "#6f6352",
            fg = "#ffe9b3",
            font = ("cambria", 11),
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        # Auswahl von Dateinamenserweiterungen
        for key in self.endungen:
            self.endungen[key] = IntVar()
            check_endung = Checkbutton(
                frame_re,
                text = key,
                bg = "#6f6352",
                fg = "#ffce58",
                selectcolor = "#606060",
                activebackground = "#6f6352",
                variable=self.endungen[key])
            check_endung.pack(padx = 8, anchor = "w")
        # Auswahl eines Zeitraums
        Label(
            frame_re,
            text = "Auswahl Zeitraum",
            bg = "#6f6352",
            fg = "#ffe9b3",
            font = ("cambria", 11),
            justify = "left").pack(padx = 10, pady = 12, anchor = "w")
        Label(
            frame_re,
            text = "Beispiel: 28.06.2017\n\nVon:",
            bg = "#6f6352",
            fg = "#ffce58",
            font = ("cambria", 10),
            justify = "left").pack(padx = 10, anchor = "w")
        self.von = Entry(frame_re)
        self.von.pack(padx = 10, anchor = "w")
        Label(
            frame_re,
            text = "Bis:",
            bg = "#6f6352",
            fg = "#ffce58",
            font = ("cambria", 10),
            justify = "left").pack(padx = 10, anchor = "w")
        self.bis = Entry(frame_re)
        self.bis.pack(padx = 10, anchor = "w")
        # Durchlauf mit Sortierung oder Suche starten
        Button(
            frame_re,
            text = "Suche",
            font = ("cambria", 10, "bold"), padx = 20,
            command = self.pruefe_daten).pack(side = "top", pady = 10)
        # Speichern unter ...
        Button(
            frame_re,
            text = "Speichern",
            font = ("cambria", 10, "bold"), padx = 8,
            command = self.speichere_suchergebnis).pack(side = "top")

        self.fenster.mainloop()

def main():
    Dateisuche().layout()

if __name__ == "__main__":
    main()
BlackJack

@Melewo: Ja klar, schlechte Namen können später immer andere umbenennen. Oft ist man aber selbst der andere der da nach ein paar Monaten wieder mit konfrontiert ist.

Bei `file_dict` ist immer noch der Fehler das Zeitpunkte auf Dateinamen/-pfade abbilden nicht eindeutig ist. Dort ein Wörterbuch zu nehmen ist nicht sinnvoll. Da reicht eine Liste mit Tupeln völlig aus. Und da dann bitte immer Tupel mit der gleichen Reihenfolge von Elementen speichern. Das ist sonst sehr verwirrend und führt gegen Ende der Funktion auch zu kopiertem Code.

Und ich finde es auch weiterhin nicht nachvollziehbar warum bei alphabetischer Sortierung nicht nach Datum gefiltert werden kann, und warum man entweder kein Datum oder beide angeben muss, und die anderen beiden Fälle, ohne irgenwie für den Nutzer erkennbar warum das so ist, einfach ein leeres Suchergebnis produzieren.

Mit literalen `True`- und `False`-Werten vergleicht man nicht. Da kommt ja eh nur wieder ein `True` oder `False` bei heraus, also hätte man gleich den Wert nehmen können den man schon hatte oder gegebenfalls seine Negation mit ``not``. Das funktioniert an einigen Stellen bei Dir nicht weil Du Namen hast die auch an etwas anderes als `True` oder `False` gebunden werden können. Damit rechnet niemand. Wenn ich den Code richtig lese kann `suche_nach` die Werte `None`, oder eine Zeichenkette, oder `False` annehmen — das man in Python jeden Typ an einen Namen binden kann, heisst nicht das es eine gute Idee ist das auch tatsächlich zu machen. Es sollte immer der gleiche „duck type“ sein. `None` kann dabei für jeden Typ verwendet werden, aber Zeichenkette und Wahrheitswert sind nicht so einfach austauschbar.

Auf die Kritik mit der (Nicht-)Auswahl der Sortierung bist Du auch nicht eingegangen. Und die Aufteilung auf die `__init__()` und `layout()` ist auch immer noch falsch.

Die lokale Suchfunktion sollte nicht lokal sein.

Insgesamt ist die Funktion in der die Programmlogik steht zu langt, und viel zu verworren und enthält an ein paar Stellen deswegen Code der nahezu identisch ist. Das wäre sinnvoller aufgeteilt in eine Funktion welche die Pfade zum Beispiel als Generator liefert, und auf den dann die verschiedenen Filter nacheinander angewendet werden und das Ergebnis dann in einem `sorted()` mit dem entsprechenden `key`-Argument aggregiert wird.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

False habe ich bei suche_nach und vondato und bisdato nach None geändert, die Abfragen entsprechend angepasst sowie das letzte Entry-Feld mit dem aktuellen Datum vorbelegt, wobei das Datum grau bleibt, bis ein Course bei Entry gesetzt wird. Nun kann es geändert oder bei aktuell belassen werden.
Ändern könnte man sicherlich noch mehr, doch dann könnte der Sommer um sein und was ich übers Jahr noch alles erledigen wollte, schleift vor sich hin. Doch so eine Zwischenübung war nicht verkehrt, ich merkte ja bei dem Animations-Script, was ich noch für Lücken habe und dieses sollte ja dann doch etwas werden, was sich mehr sehen lassen kann.
Antworten