tkinter filedialog.askopenfilenames() Problem

Fragen zu Tkinter.
Antworten
coethen25
User
Beiträge: 13
Registriert: Freitag 5. August 2016, 13:21

Ich schreibe mir gerade ein kleines Programm zum Umbenennen von mehreren Fotos gleichzeitig (so als Übung für Anfänger). Da bin ich auf ein Problem bei der Verwendung der ausgegebenen Dateiliste des askopenfilenames-Dialoges gestoßen:
Ausgegeben wird eine Dateiliste als Tupel. Wenn ich diese weiter benutzen will, ist s auf einmal ein String? Hier mal der Code:

Code: Alles auswählen

    def openFiles():
         images = filedialog.askopenfilenames()
         print (type(images))                # hier noch <class 'tuple'> 
         textbox1.insert(1.0, images)
        
            
    def renamedFiles():
         i = 0
         infile = textbox1.get(1.0, "end")
         print (type(infile))               # ab hier <class 'str'> , weiterer Ablauf der Funktion nicht möglich
         for i in range(len(infile)):
             img = Image.open(infile[i])
             outfile = str(os.path.split(infile[i])[0] + "/" + newname.get() + ".jpg")
             img.save(outfile)
             textbox2.insert(1.0, outfile)
             i += 1

Warum bleibt die Ausgabe des askopenfilenames-Dialoges nicht einfach ein Tupel, wenn ich sie in eine Textbox einfüge und wieder auslese?
Was muss ich heir ändern, dass es funktioniert?
BlackJack

@coethen25: Eine Textbox zeigt Text, also eine Zeichenkette an, beziehungsweise erlaubt dem Benutzer Text, also eine Zeichenkette zu bearbeiten. (Es geht eigentlich noch ein bisschen mehr, also Formatierungsinformationen, Grafiken, usw.) Aber nun mal keine Datenstrukturen. Widgets sind zum Anzeigen und eingeben von Daten da, nicht um sie dort zu speichern.

Die ”Funktionen” greifen auf einiges zu worauf sie nicht zugreifen sollten. Alles was eine Funktion oder Methode verwendet (ausser Konstanten) sollte als Argument übergeben werden. Sonst wird es sehr schnell unübersichtlich, nicht testbar, und damit schwer zu verstehen, zu warten, und zu erweitern. Bei GUI-Programmierung kommt man nicht um objektorientierte Programmierung herum. Namen wie `textbox1` und `textbox2` sind schlecht. Da weiss man als Leser nicht was die im Kontext des Programms *bedeuten*.

`openFiles()` öffnet keine Dateien, ist also ein irreführender Name. `renamedFiles()` steht nicht für umbenannte Dateien, sondern ist eine Funktion die Grafikdateien nicht nur umbenennt, sondern konvertiert. An der Stelle würde ich prüfen ob die Ausgangsdatei schon ein JPG ist, denn laden und speichern so einer Datei geht in aller Regel nicht verlustfrei von statten. Da möchte man eher nur die Datei kopieren.

Die Zuweisung an `i` ist überflüssig und das erhöhen um 1 auch. Insgesamt ist `i` als Laufvariable hier „unpythonisch“ weil der Umweg über einen Index total unnötig ist. Man kann in Python direkt über die Elemente von Sequenzen wie Listen und Tupeln iterieren.

Edit: Um Pfadteile zusammen zu setzen sollte man `os.path.join()` verwenden und das `os.path.split()` gefolgt vom Indexzugriff wäre ein `os.path.dirname()`.

Edit 2: Das konvertieren einer Datei sollte man von der GUI trennen und dann von der GUI aus die Schleife mit der `after_idle()`-Methode ersetzen die einmal pro Datei aufgerufen wird, um die GUI nicht für den gesamten Konvertierungsvorgang zu blockieren.
coethen25
User
Beiträge: 13
Registriert: Freitag 5. August 2016, 13:21

Ich glaube, da hast Du was falsch verstanden, ich möchte die Fotos, die ich von der SD-Karte der Kamera in einen Ordner gepackt habe, nicht konvertieren, sondern ihnen einfach einen anderen Namen (mit gleicher Endung) geben.

Einige Deiner Ratschläge konnte ich nach intensivem Suchen im Web und probieren beherzigen. z.B. die Laufvariablen "i" und "j" habe ich rausgelassen und mit "for item in ...." die schleife laufen lassen.

Nur rename heißt übersetzt umbenennen und genau das tue ich in diesem Programm. Ich zeige Dir einfach mal den jetzigen Code. Ist lauffähig und macht das, was ich mit gedacht habe.

Code: Alles auswählen

#!/usr/bin/env python3.5
# *-* coding: utf-8 *-*

import tkinter as tk
from tkinter import filedialog as fd, messagebox as mb
from PIL import Image
import os


class FotoRename():
    def __init__(self):
        
        def chooseFiles():                                                         # choose imagefiles and show these in the window
            self.images = fd.askopenfilenames()
            for item in self.images:
                originals.insert(1.0, str(os.path.basename(item)+", "))
                originals.yview("end")
            return self.images

        def renameFiles():                                                        # these files rename and show in the window
            if newname.get() == "":
               mb.showwarning('Achtung', 'Bitte erst einen Namen eingeben\n(ohne Dateiendung z.B. bild_ )')  # remember a new name if it is forgotten
            else:
                for item in self.images:
                    self.img = Image.open(item)
                    self.outfile = os.path.join(os.path.dirname(item), (newname.get()+str(self.images.index(item)+1)+os.path.splitext(item)[1]))
                    modified.insert(1.0, str(os.path.basename(self.outfile)+", "))
                    modified.yview("end")
                return self.outfile
                
        def saveFiles():                                                           # save the rename image files in the same or different folder     
            folder = fd.askdirectory()
            for item in self.images:
                self.img = Image.open(item)
                self.outfile = os.path.join(folder, (newname.get()+str(self.images.index(item)+1)+os.path.splitext(item)[1]))
                self.img.save(self.outfile)
            if os.path.exists(self.outfile):
                modified["fg"] = "#070"
                modified.insert("end", "\n\n  Die Fotos wurden erfolgreich gespeichert!")
                modified.yview("end")
            else:
                modified["fg"] = "#f00"
                modified.insert("end", "\n\n  Die Fotos konnten nicht gespeichert werden.\n  Fehler im System!")
                modified.yview("end")
            return 
                
        
        def reset():
            originals.delete('1.0', 'end')
            modified.delete('1.0', 'end')

        
        root = tk.Tk()
        root.title("Fotos umbenennen")
        f = tk.Frame(root, bg="#ccc", bd="2", relief="groove")
        f.pack(fill="x")
        l = tk.Label(f, bg="#8b8b8b", text="")
        l.pack(fill="x")
        b1 = tk.Button(l, text="Fotos öffnen", bg="#8b0000", fg="#ffffaa", activebackground="#ffffaa",
                       activeforeground="#8b0000", command=chooseFiles)
        b1.pack(side="left", padx=7)
        b2 = tk.Button(l, text="Fotos ändern", bg="#8b0000", fg="#ffffaa",activebackground="#ffffaa",
                       activeforeground="#8b0000", command=renameFiles)
        b2.pack(side="left", padx=7)
        b3 = tk.Button(l, text="Reset", bg="#8b0000", fg="#ffffaa",activebackground="#ffffaa",
                       activeforeground="#8b0000", command=reset)
        b3.pack(side="right", padx=7)
        f2 =tk.Frame(root, bg="#ccc", bd="2", relief="groove" )
        f2.pack()
        originals = tk.Text(f2, bg="#fafafa", width=60, height=7)
        originals.pack()
        f3 =tk.Frame(root, bg="#ccc", bd="2", relief="groove" )
        f3.pack()
        l2 = tk.Label(f3, bg="#ccc", text="Bitte neuen Namen eingeben:")
        l2.pack(side="left", padx=11)
        newname = tk.Entry(f3, width=24, font="Helvetia 10")
        newname.pack(side="right", padx=10)
        f4 = tk.Frame(root, bg="#ccc", bd="2", relief="groove" )
        f4.pack()
        modified = tk.Text(f4, bg="#fafafa", fg="#000", width=60, height=7)
        modified.pack()
        f5 = tk.Frame(root, bg="#8b8b8b", bd="2", relief="groove")
        f5.pack(fill="x")
        b5 = tk.Button(f5, text="schliessen", bg="#8b0000", fg="#ffffaa", activebackground="#ffffaa",
                       activeforeground="#8b0000",command=root.destroy)
        b5.pack(side="right", padx=10)
        b6 = tk.Button(f5, text="speichern", bg="#8b0000", fg="#ffffaa", activebackground="#ffffaa",
                       activeforeground="#8b0000",command=saveFiles)
        b6.pack(side="left", padx=10)
        root.mainloop()
    

Fenster = FotoRename()
        
BlackJack

@coethen25: Ist ja kein Wunder das ich Dich falsch verstehe, denn Du willst etwas, *machst* aber etwas anderes. Du benennst *nicht* um wie der Funktionsnam suggeriert, sondern *konvertierst* die Bilder in der Funktion. Für's umbennen macht `PIL.Image` keinen Sinn, dafür gibt es `os.rename()`. Und wie gesagt, führt das konvertieren von JPG zu JPG zu einem Qualitätsverlust.

Die ”Klasse” macht so keinen Sinn. Du hast da lauter ”Funktionen” lokal in der `__init__()` in der das komplette Programm abläuft, statt Methoden zu schreiben. Die lokalen ”Funktionen” sind dann aber auch wieder keine Funktionen weil die Zustand verändern *und* Werte zurückgeben, wobei diese Rückgabewerte nicht verwendet werden. Ja sogar nicht verwendet werden *können*, weil die GUI-Hauptschleife mit dn Rückgabewerten von den Rückruffunktionen nichts anzufangen weiss.

Die Abkürzungen von `filedialog` und `messagebox` sollte man sich sparen. Das macht man bei `tk` weil dieses Modul *richtig* viele Namen enthält, von denen man *relativ* viele benötigt. Wenn man aus einem Modul nur ein paar Namen braucht, dann sollte man die explizit importieren.

`saveFiles()` konvertiert die Bilder auch noch mal. Hilfe.

Es wird auch viel zu viel in diesen verschachtelten ”Funktionen” an das Objekt gebunden was dort nichts zu suchen hat sondern einfach nur lokale Namen wären.

Anhand der Existenz der letzten konvertieren Bilddatei wird entschieden dem Benutzer eine Meldung zu geben das die Aktion erfolgreich oder nicht erfolgreich war? Was sollte denn zu einem Misserfolg führen das vorher *nicht* zu einer Ausnahme führt, die dafür sorgt, dass der Code für die Misserfolgsnachricht gar nicht ausgeführt werden kann? Was denkst Du was passiert wenn keine Dateien ausgewählt wurden und der Programmfluss zu dem Test auf das vorhandensein der letzten Datei kommt, also der letzten von 0 Dateien? Welchen Wert hat `self.outfile` dann? Was passiert wenn der Nutzer zweimal „Fotos öffnen” klickt und jeweils andere Dateinamen auswählt? Was wird ihm dann in der GUI angezeigt, und stimmt das dann mit der Aktion überein, die er mit den beiden Schaltflächen zum konvertieren auslösen kann? (Tipp: Nein, tut es nicht!)

Einbuchstabige und dann auch noch teilweise durchnummerierte Namen sind Murks. Gute Namen sind wichtig, weil die beim verstehen des Quelltextes helfen.

Das Programm ist von der Form und vom Verhalten noch ziemlich fehlerhaft.
BlackJack

@coethen25: Einige Zeile sind durch Kommentare am Zeilenende viel zu lang. Früher war die Längenempfehlung 80 Zeichen pro Zeile. Das ist mittlerweile bei 120, aber ich würde trotzdem bei 80 bleiben, denn es gibt noch genug Stellen wo Code angeschaut werden kann die sich an dieser traditionellen Terminalbreite orientieren und wo es schwer lesbar wird wenn Code länger ist und dann umgebrochen wird oder nicht mehr vollständig sichtbar ist.

Die Kommentare bei den Funktionen würden sich auch eher als Docstrings eignen.

Es wird mal 'end' als Zeichenkette verwendet und mal die Konstante `tk.END`.

Beim erstellen des neuen Dateinamens mit `index()` die Position in der Liste zu ermitteln ist ineffizient. Die fortlaufende Zahl erstellt man am besten mit `enumerate()`.

„Fotos ändern“ und „speichern“ ist fast identisch, bis auf die Art wie das Zielverzeichnis ermittelt wird. Das kann man in einer Funktion zusammen fassen. Bein einer Aktion wird der Benutzer gezwungen einen neuen Namen einzugeben, bei der anderen nicht, obwohl beide den Namen benötigen. Die Ausgaben für den Benutzer sind unterschiedlich, obwohl die Aktionen fast identisch sind.

Schaltflächen in ein `Label` zu stecken ist eher ungewöhnlich. Dazu sind `Frame`\s vorgesehen.

Wie schon im letzten Beitrag: Einbuchstabige und einbuchstabige durchnummerierte Namen sind schlecht.

Man sollte sowohl Code als auch Datenwiederholungen vermeiden. Wenn alle Schaltflächen den gleichen Satz an Argumenten beim erstellen bekommen, sollte man die nur *einmal* schreiben und nicht immer und immer wieder. Das bläht den Quelltext unnötig auf und macht es schwieriger Änderungen an diesen Daten vorzunehmen die für viele Anzeigeelemente gelten.

Die `destroy()`-Methode ist ziemlich hart wenn man eigentlich einfach nur die GUI-Hauptschleife verlassen möchte. Dafür ist die `quit()`-Methode da.

Ich lande als Zwischenergebnis ungefähr bei so etwas (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3.5
# coding: utf-8
 
import os
import shutil
from functools import partial
import tkinter as tk
from tkinter.filedialog import askdirectory, askopenfilenames
from tkinter.messagebox import showwarning


def rename_and_copy(filenames, new_basename, destination_path=None):
    for i, filename in enumerate(filenames):
        new_filename = '{0}{1}{2}'.format(
            new_basename, i, os.path.splitext(filename)[1]
        )
        destination = os.path.join(
            destination_path if destination_path else os.path.dirname(filename),
            new_filename
        )
        shutil.copy(filename, destination)
        yield new_filename


class FotoRename(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent, bg='#ccc', bd=2, relief=tk.GROOVE)
        self.filenames = set()

        button_options = {
            'background': '#8b0000',
            'foreground': '#ffffaa',
            'activebackground': '#ffffaa',
            'activeforeground': '#8b0000',
        }
        button_frame_background = '#8b8b8b'
        text_options = {'width': 60, 'height': 7, 'background': '#fafafa'}
        frame = tk.Frame(self, background=button_frame_background)
        frame.pack(fill=tk.X)
        tk.Button(
            frame,
            text='Fotos öffnen',
            command=self.add_filenames,
            **button_options
        ).pack(side=tk.LEFT, padx=7)
        tk.Button(
            frame,
            text='Fotos ändern',
            command=partial(self.copy_and_rename, False),
            **button_options
        ).pack(side=tk.LEFT, padx=7)
        tk.Button(
            frame, text='Reset', command=self.reset, **button_options
        ).pack(side=tk.RIGHT, padx=7)

        frame_options = {'background': '#ccc', 'border': 2, 'relief': tk.GROOVE}

        frame = tk.Frame(self, **frame_options)
        frame.pack()
        self.originals = tk.Text(frame, **text_options)
        self.originals.pack()
        
        frame = tk.Frame(self, **frame_options)
        frame.pack()
        tk.Label(
            frame, bg='#ccc', text='Bitte neuen Namen eingeben:'
        ).pack(side=tk.LEFT, padx=11)
        self.new_name = tk.Entry(frame, width=24, font='Helvetia 10')
        self.new_name.pack(side=tk.RIGHT, padx=10)
        
        frame = tk.Frame(self, **frame_options)
        frame.pack()
        self.modified = tk.Text(frame, **text_options)
        self.modified.pack()
        
        frame_options['background'] = button_frame_background
        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        tk.Button(
            frame, text='schliessen', command=self.quit, **button_options
        ).pack(side=tk.RIGHT, padx=10)
        tk.Button(
            frame,
            text='speichern',
            command=partial(self.copy_and_rename, True),
            **button_options
        ).pack(side=tk.LEFT, padx=10)
   
    def add_filenames(self):
        self.filenames.update(askopenfilenames())
        self.originals.delete(1.0, tk.END)
        self.originals.insert(1.0, ', '.join(sorted(self.filenames)))
        self.originals.yview(tk.END)

    def copy_and_rename(self, ask_for_destination):
        new_name = self.new_name.get()
        if not new_name:
            showwarning(
                'Achtung',
                'Bitte erst einen Namen eingeben\n'
                '(ohne Dateiendung z.B. bild_ )'
            )
        else:
            destination_path = askdirectory() if ask_for_destination else None
            self.modified.delete(1.0, tk.END)
            self.modified.insert(
                1.0,
                ', '.join(
                    rename_and_copy(
                        sorted(self.filenames), new_name, destination_path
                    )
                )
            )
            self.modified.yview(tk.END)

    def reset(self):
        self.filenames = set()
        self.originals.delete(1.0, tk.END)
        self.modified.delete(1.0, tk.END)


def main():
    root = tk.Tk()
    root.title('Fotos umbenennen')
    main_ui = FotoRename(root)
    main_ui.pack(fill=tk.X)
    root.mainloop()


if __name__ == '__main__':
    main()
coethen25
User
Beiträge: 13
Registriert: Freitag 5. August 2016, 13:21

@BlackJack Danke für Deine Mühe und Deine Hilfe! Ich schau mir das in Ruhe an und versuche es nachzuvollziehen und zu verstehen. Hatte es gerade mit os.rename versucht und auch hinbekommen. Dein Code läuft zwar noch nicht, aber das werde ich schon schaffen.
coethen25
User
Beiträge: 13
Registriert: Freitag 5. August 2016, 13:21

@BlackJack Ich habe mal versucht, die Hinweise anzuwenden und habe den Code umgeändert. Wie vorher schon geschrieben, lief Dein Code bei mir nicht. Er hat mir von den geöffneten Fotos immer den kompletten Pfad angezeigt (sollte nur den Dateinamen) und bei den umbenannten Fotos (self.modified) immer nur das letzte Foto. Naja, egal! Jetzt tut es das, was ich wollte.
Bei den Labels habe ich nicht **label_options verwendet, da dort nur eine Option(bg="#ccc") gleich ist. Den "schliessen"-Button habe ich in die Main-Funktion gepackt, weil ich dort für root.destroy auch auf root zugreifen kann, denn ich möchte, dass das Fenster auch verschwindet, wenn ich "schliessen" anklicke und nicht nur ein Programmablauf beendet wird, wie bei self.quit.
Weiterhin habe ich noch eine Spinbox für eine Zählernummer eingerichtet, da bei mir manchmal die Fotos eben nicht bei 1 anfangen, sondern z.B. bei 88 (Garten2016_88.jpg), weil ich schon 87 Fotos in diesem Ordner habe. Und unter dem Fragezeichen befindet sich noch ein Toplevel (Hilfe), welches ich als Modul ausgelagert habe. (wird nicht benötigt,nur zur Übung). Auf alle Fälle läuft das Programm. Die Kommentare habe ich mal weggelassen.

Code: Alles auswählen

#!/usr/bin/env python3.5
# *-* coding: utf-8 *-*

import tkinter as tk
from tkinter.filedialog import askopenfilenames, askdirectory
from tkinter.messagebox import showwarning, showinfo
from mymoduls import helpwindow
import os


class FotoRename(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent, bg='#ccc', bd=2, relief=tk.GROOVE)
        
        frame_options = {'background': '#ccc', 'border': 2, 'relief': tk.GROOVE}
        button_options = {
            'background': '#8b0000',
            'foreground': '#ffffaa',
            'activebackground': '#ffffaa',
            'activeforeground': '#8b0000',
        }
        text_options = {'width': 60, 'height': 7, 'background': '#fafafa'}

        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        tk.Button(frame, text="Fotos öffnen", **button_options,
                  command=self.openFiles).pack(side=tk.LEFT, padx=10)
        tk.Button(frame, text="Umbenennen ", **button_options,
                  command=self.rename_and_save).pack(side=tk.LEFT, padx=10)
        tk.Button(frame, text="?", bg="#aaa", fg="#ff0",
                  activebackground="#ccc", activeforeground="#8b0000",
                  font="Georgia 12 bold", command=helpwindow.open_help).pack(
                      side=tk.RIGHT, padx=10)
        tk.Button(frame, text="reset", **button_options,
                  command=self.reset).pack(side=tk.RIGHT, padx=10)
        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        self.originals = tk.Text(frame, **text_options)
        self.originals.pack()
        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        tk.Label(frame, text="Bitte neuen Namen eingeben:",  bg="#ccc"
                 ).pack(side=tk.LEFT, padx=6)
        self.newname = tk.Entry(frame, width=24, font="Helvetia 10")
        self.newname.pack(side=tk.RIGHT, padx=4)
        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        tk.Label(frame, text="anfangen bei Zähler Nr.:",  bg="#ccc"
                 ).pack(side=tk.LEFT, padx=6)
        self.countnumber = tk.Spinbox(frame, width=3, from_=1, to_=999,
                                      justify=tk.RIGHT)
        self.countnumber.pack(side=tk.LEFT, padx=60)
        tk.Label(frame, text="z.B. bild_1.jpg",  bg="#ccc"
                 ).pack(side=tk.LEFT)
        frame = tk.Frame(self, **frame_options)
        frame.pack(fill=tk.X)
        self.modified = tk.Text(frame, **text_options)
        self.modified.pack()
        
    
    def openFiles(self):
        self.filenames = askopenfilenames()
        for file in self.filenames:
            self.originals.insert(1.0, str(os.path.basename(file)+', '))
            self.originals.yview(1.0)
        

    def rename_and_save(self):
        self.new_name = self.newname.get()
        if not self.new_name:
            showwarning('Achtung', 'Bitte erst'
                        'einen Namen eingeben\n'
                        '(ohne Dateiendung z.B. bild_ )')  
        else:
            folder = askdirectory()
            self.new_number = self.countnumber.get()
            for i,file in enumerate(self.filenames):
                self.newfilename = str(self.new_name+str(
                       str(i+int(self.new_number))+os.path.splitext(file)[1]))
                self.newfile = os.path.join(folder, self.newfilename)
                os.rename(file, self.newfile)
                self.modified.insert(1.0, str(self.newfilename+", "))
                self.modified.yview(1.0)

        if os.path.exists(self.newfile):
            showinfo('Information', 'Die Fotos wurden erfolgreich'
                     ' umbenannt und gespeichert!')  
        else:
            pass


    def reset(self):
        self.originals.delete('1.0', tk.END)
        self.modified.delete('1.0', tk.END)
        self.newname.delete(0, tk.END)


def main():    
    root = tk.Tk()
    root.title('Fotos umbenennen')
    root.geometry("+500+130")
    main_ui = FotoRename(root)
    main_ui.pack(fill=tk.X)
    close = tk.Button(main_ui, text="schliessen", bg="#8b0000", fg="#ffffaa",
                      activebackground="#ffffaa", activeforeground="#8b0000",
                      command=root.destroy)
    close.pack(side=tk.RIGHT, padx=10)
    root.mainloop()
    
if __name__ == '__main__':
    main()

Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@coethen25: warum nimmst Du nicht das parent-Argument zum Schließen des Fensters, denn den Button im Hauptprogramm zu erzeugen, ist der falsche Ort. In __init__ sollten alle Attribute angelegt werden, also auch "self.filenames". new_name, new_number, newfilename und newfile werden außerhalb von rename_and_save nicht verwendet, sollten also keine Attribute von self sein. enumerate kennt als zweites Argument den Startwert, so dass Du das Zusammenrechnen bei jedem filename sparen kannst. Das Nutzen von Stringformatierung macht das Erzeugen von filename lesbarer:

Code: Alles auswählen

            for i,file in enumerate(self.filenames, int(self.new_number)):
                self.newfilename = "{}{}{}".format(self.new_name, i, os.path.splitext(file)[1])
                [...]
coethen25
User
Beiträge: 13
Registriert: Freitag 5. August 2016, 13:21

@Sirius3 Ich bin noch Anfänger mit Python. Dank Dir für die Hinweise! Klappt alles wunderbar, was Du genannt hast. Wieder was dazugelernt :lol:
Antworten