Liste in Tinker bearbeiten - Einträge verschieben etc.

Fragen zu Tkinter.
Antworten
Benutzeravatar
HeiDieLX
User
Beiträge: 3
Registriert: Freitag 26. März 2021, 10:00

Hallo,
es ist meine Premiere hier im Forum.

Hierzu habe ich keine Idee wie es zu realisieren ist. hiermit

Code: Alles auswählen

    filenames = fd.askopenfilenames(
        title='Open files',
        initialdir='/home/hd-arbeit/Dokumente',
        filetypes=filetypes
hole ich mir div. Dateien. Es wird eine Liste zurückgeliefert mit Pfad und Dateiname.
Das splitten zwischen Pfad und Dateiname läuft so:

Code: Alles auswählen

        dateinamen = []
        pfadname = []
        pfadname.append(filenames[0].rsplit('/',1)[0])
        for nx in range(len(filenames)):
            dateinamen.append(filenames[nx].rsplit('/',1)[1])
Damit habe ich den Pfad und die Namen separiert.

Wir zeige ich die Dateinamen nun an? Ich möchte diese Einträge hernach auch rauf bzw. runter verschieben können.

Danke vorab für die Infos
MfG
Swity vom Bienwald
openSuse Tumbleweed
PostgreSQL 15
LibreOffice 24.8.3.2
Python 3.8
Benutzeravatar
__blackjack__
User
Beiträge: 13808
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@HeiDieLX: Pfade sind keine einfachen Zeichenketten, die kann/darf man nicht einfach mit Zeichenkettenoperationen bearbeiten. Dafür gibt es das `pathlib`-Modul.

Es gibt ein paar wenige Module bei denen es üblich ist die Abzukürzen. `filedialog` gehört nicht dazu. Da würde ich einfach direkt die Funktion importieren.

Deutsch und englisch mischen ist keine gute Idee. Jetzt gibt es `filenames` und `dateinamen` — da weiss man nicht was da der Unterschied ist wenn man sich die Namen anschaut.

Der Code geht auch davon aus, das mindestens eine Datei ausgewählt wurde, man kann den Dialog aber auch einfach so schliessen, was dann zu einer Ausnahme führt.

from pathlib import Path
from tkinter.filedialog import askopenfilenames

DOCUMENT_BASE_PATH = Path("/home/hd-arbeit/Dokumente")

Zwischenstand:

Code: Alles auswählen

    ...
    filetypes = ...

    filenames = list(
        map(
            Path,
            askopenfilenames(
                title="Open files",
                initialdir=DOCUMENT_BASE_PATH,
                filetypes=filetypes,
            ),
        )
    )
An den Dateinamen kommt man über das `name`-Attribut, und der Pfad ist das `parent`-Attribut. Falls man nur den Namen anzeigen will und der Pfad dorthin bei allen gleich ist, würde man das wahrscheinlich besser als Wörterbuch das den Namen auf das `Path`-Objekt abbildet speichern, damit man die Namen aus der GUI am Ende wieder den `Path`-Objekten zuordnen kann.

Wie auch immer Du das dann verschiebbar darstellen willst, Du musst das in Tkinter selbst programmieren. Das einfachste wäre wahrscheinlich eine `Listbox` und Schaltflächen für Hoch-/Runterverschieben der/des Ausgewählten Inhalts.
Die drei Todfeinde des Programmieres:
Sonnenlicht, frische Luft und das unerträgliche Gebrüll der Vögel.
Benutzeravatar
HeiDieLX
User
Beiträge: 3
Registriert: Freitag 26. März 2021, 10:00

Hallo _blackjack_

danke für die Hinweise. Einiges konnte ich umsetzen.
Derzeit arbeite ich mich an der Listbox ab. Wie kann ich das machen, dass die Listbox die Größe der Rahmens behält oder erhält.
Die Größe des Frames (width=395, height=200) ist definiert, wenn die Listbox angezeigt wird ist der Frame weg und die Listbox wird angezeigt jedoch wesentlich kleiner als gewünscht.
Ich hätte halt gerne das die Listbox annähernd die Größe des Frames einnimmt.

Code: Alles auswählen

# Frame 1 Datein zusammenführen
def load_files():
    filetypes = (
        ('PDF files', '*.pdf'),
        ('text files', '*.txt'),
        ('All files', '*.*')
    )

    pathfiles = filedialog.askopenfilenames(
        title='Laden von Dateien',
        initialdir='/home/hd-arbeit/Dokumente',
        filetypes=filetypes
    )

    if len(pathfiles) > 0:
        sfilesname = []
        spath = []
        spath.append(pathfiles[0].rsplit('/',1)[0])
        opath = Path(spath[0])    # Betriebssystem konformer Pfad
        for nx in range(len(pathfiles)):
            sfilesname.append(pathfiles[nx].rsplit('/',1)[1])

        var = tk.Variable(value=sfilesname)
        
        listbox = tk.Listbox(listFrame, listvariable=var, height=10, selectmode=tk.EXTENDED)

        listbox.pack(side=LEFT)

        # link a scrollbar to a list
        scrollbar = ttk.Scrollbar(listFrame, orient=tk.VERTICAL, command=listbox.yview)

        listbox['yscrollcommand'] = scrollbar.set

        scrollbar.pack(side=RIGHT, expand=False, fill=Y)
                
    else:
        showwarning(title='Nichts ausgewählt', message='keine Elemente ausgewählt!')

#hier kommt verschiedenes wieder weg, einige Einträge nur zur Entwicklung
irahmen1 = ttk.Frame(frame1, width=790, height=20, borderwidth=1, relief="ridge") # hilfselement zur orientierung
listFrame = tk.Frame(frame1, width=395, height=200, borderwidth=1, relief="ridge", bg='mint cream')
irahmen1.grid(row=0, column=0, columnspan=5)
listFrame.grid(row=1, column=0, columnspan=5, sticky=tk.W)

F1Butt_laden = ttk.Button(frame1, text='Dateien laden', command=load_files)
F1Butt_laden.place(x=10, y=265, width=180, height=30, anchor=tk.SW)
MfG
Swity vom Bienwald
openSuse Tumbleweed
PostgreSQL 15
LibreOffice 24.8.3.2
Python 3.8
Benutzeravatar
__blackjack__
User
Beiträge: 13808
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

`load_files()` greift einfach so magisch auf `listFrame` zu, das geht ja nicht wenn man da keine globalen Variablen definiert. Funktionen und Methoden bekommen alles ausser Konstangen als Argument(e) übergeben.

Dann ist da immer noch Pfade als Zeichenketten behandeln und an "/"-aufteilen im Code.

`spath` ist eine Liste die genau *ein* Element enthält. Was nicht wirklich sinnvoll ist. Und was soll das `s` bedeuten?

`opath` wird definiert und nicht verwendet. Und auch da wieder: was soll das `o` bedeuten.

``for i in range(len(sequence)):`` nur um dann mit `i` auf die Elemente von `sequence` zuzugreifen ist in Python ein „anti-pattern“. Man kann direkt über die Elemente von `sequence` iterieren, ohne den Umweg über einen Laufindex.

Absolute Pixelgrössen- und Positionen sind ein No-Go. Das mag auf dem System funktionieren an dem es programmiert wurde, aber auch da nur solange bis der Monitor ausgetauscht wird, oder Systemeinstellungen wie Bildschirmauflösung oder Schriftgrössen geändert werden. Bei mir auf dem Rechner ist die Schaltfläche beispielsweise gar nicht zu sehen. Wenn man `place()` verwendet hat Tk keine Ahnung wie gross das Fenster gemacht werden soll, darum verwendet man `place()` nicht.`load_files()` greift einfach so magisch auf `listFrame` zu, das geht ja nicht wenn man da keine globalen Variablen definiert. Funktionen und Methoden bekommen alles ausser Konstangen als Argument(e) übergeben.

Dann ist da immer noch Pfade als Zeichenketten behandeln und an "/"-aufteilen im Code.

`spath` ist eine Liste die genau *ein* Element enthält. Was nicht wirklich sinnvoll ist. Und was soll das `s` bedeuten?

`opath` wird definiert und nicht verwendet. Und auch da wieder: was soll das `o` bedeuten.

``for i in range(len(sequence)):`` nur um dann mit `i` auf die Elemente von `sequence` zuzugreifen ist in Python ein „anti-pattern“. Man kann direkt über die Elemente von `sequence` iterieren, ohne den Umweg über einen Laufindex.

Absolute Pixelgrössen- und Positionen sind ein No-Go. Das mag auf dem System funktionieren an dem es programmiert wurde, aber auch da nur solange bis der Monitor ausgetauscht wird, oder Systemeinstellungen wie Bildschirmauflösung oder Schriftgrössen geändert werden. Bei mir auf dem Rechner ist die Schaltfläche beispielsweise gar nicht zu sehen. Wenn man `place()` verwendet hat Tk keine Ahnung wie gross das Fenster gemacht werden soll, darum verwendet man `place()` nicht.

Das könnte beispielsweise so aussehen:

Code: Alles auswählen

import tkinter as tk
from pathlib import Path
from tkinter import filedialog, ttk
from tkinter.messagebox import showwarning


def load_files(filenames_var):
    filepaths = list(
        map(
            Path,
            filedialog.askopenfilenames(
                title="Laden von Dateien",
                initialdir="/home/hd-arbeit/Dokumente",
                filetypes=(
                    ("PDF-Dateien", "*.pdf"),
                    ("Textdateien", "*.txt"),
                    ("Alle Dateien", "*.*"),
                ),
            ),
        )
    )
    if filepaths:
        name_to_path = {path.name: path for path in filepaths}
        assert len(filepaths) == len(name_to_path)
        filenames_var.set(list(name_to_path.keys()))
    else:
        showwarning(
            title="Nichts ausgewählt", message="keine Elemente ausgewählt!"
        )


def main():
    window = tk.Tk()
    # hilfselement zur orientierung
    tk.Frame(
        window,
        width=790,
        height=20,
        borderwidth=1,
        relief=tk.RIDGE,
        background="light green",
    ).grid(row=0, column=0)

    filenames_frame = ttk.Frame(window, borderwidth=1, relief=tk.RIDGE)
    filenames_frame.grid(row=1, column=0, sticky=tk.W)

    filenames_var = tk.Variable(value=[])
    filenames_listbox = tk.Listbox(
        filenames_frame,
        listvariable=filenames_var,
        width=30,
        height=10,
        selectmode=tk.EXTENDED,
    )
    filenames_listbox.pack(side=tk.LEFT)
    scrollbar = ttk.Scrollbar(
        filenames_frame, orient=tk.VERTICAL, command=filenames_listbox.yview
    )
    filenames_listbox["yscrollcommand"] = scrollbar.set
    scrollbar.pack(side=tk.RIGHT, expand=False, fill=tk.Y)

    ttk.Button(
        window, text="Dateien laden", command=lambda: load_files(filenames_var)
    ).grid(row=2, column=0, sticky=tk.W)

    window.mainloop()


if __name__ == "__main__":
    main()
Wobei das hier jetzt nur das ”optische” löst. Wenn man da irgendwie sinnvoll mit der Auswahl arbeiten möchte, dann muss das in einem Objekt gekapselt werden, also eine Klasse geschrieben werden, denn sonst kann man mit den Pfad-Objekten ausserhalb der Funktion nicht arbeiten.
Die drei Todfeinde des Programmieres:
Sonnenlicht, frische Luft und das unerträgliche Gebrüll der Vögel.
Antworten