Datei aus Ordner löschen

Fragen zu Tkinter.
Antworten
BlackJack

@Philipp68: Das sieht nach Programmieren durch wildes raten oder sich wünschen was etwas bedeuten soll aus. So funktioniert programmieren aber nicht. Auch wenn das natürlich super praktisch wäre wenn Funktione, Methoden, und Syntax einfach das machen und bedeuten würden was man sich wünscht, so muss man leider damit leben das Syntax und APIs nur das tun was dokumentiert ist.

Also schau Dir mal an was die `curselection()`-Methode als Ergebnis liefert und was `os.remove()` als Argument erwartet. Dazwischen muss dann Code liegen den Du schreiben musst, der dafür sorgt das aus dem Rückgabewert von `curselection()` passende Argumente für die (Mehrzahl!) `os.remove()`-Aufrufe werden.

Sonstige Anmerkungen:

`Liste` hält sich in der Schreibweise nicht an die Namenskonvention (siehe Style Guide for Python Code).

Die `delete()`-Funktion sollte auf `Liste` nicht einfach so auf magische Weise zugreifen können, sonst ist das nicht wirklich eine Funktion. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert.  Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Auch wenn man mit Funktionen und Closures/`functools.partial()` noch ein gutes Stück weit kommen kann, braucht man für GUI-Programmierung letztlich objektorientierte Programmierung (OOP) für alles was nicht sehr trivial ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Philipp68 hat geschrieben: "os.remove(current_selection)" wäre natürlich sehr gut, funktioniert leider nicht.
Falls jemand einen Tipp hat, wäre ich sehr dankbar!
Liebe Grüße
Probiere zuerst einmal aus, was curselection ist:

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        # widget definitions ===================================
        self.listbox = tk.Listbox(self,selectmode='extended')
        self.listbox.delete(0,'end')
        for e in 'sel 0\nsel 1\nsel 2\nsel 3\nsel 4\nsel 5\nsel 6\nsel 7\nsel 8\nsel 9\n'.split('\n'):
            self.listbox.insert('end',e)
        self.listbox.pack()

        self.listbox.bind("<Return>",self.output_selection)
        self.listbox.bind("<1>",self.output_selection)

    def print_after(self):
        print(self.listbox.curselection())

    # needs some time until curselection fits
    def output_selection(self,event=None):
        self.after(10,self.print_after)

if __name__ == '__main__':
    Application().mainloop()
PS: bevor man etwas aus einem directory löscht, sollte man zuerst einmal das programmiert haben, was die Listbox tut. Und das kann man sich jederzeit mit print ausgeben lassen.

Also: nicht gleich alles zusammen programmieren, sondern jeden Teil einzeln und durchtesten. Bzw. nicht einzeln, sondern auch aufbauend auf das was bis jetzt geht.
BlackJack

@Alfons Mittelmeyer: Ein ziemlich hässlicher Hack mit der Wartezeit, da will man doch lieber das '<<ListboxSelect>>'-Ereignis verwenden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Ein ziemlich hässlicher Hack mit der Wartezeit, da will man doch lieber das '<<ListboxSelect>>'-Ereignis verwenden.
Ja, das funktioniert auch viel besser, also nochmals richtig:

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        # widget definitions ===================================
        self.listbox = tk.Listbox(self,selectmode='extended')
        self.listbox.delete(0,'end')
        for e in 'sel 0\nsel 1\nsel 2\nsel 3\nsel 4\nsel 5\nsel 6\nsel 7\nsel 8\nsel 9\n'.split('\n'):
            self.listbox.insert('end',e)
        self.listbox.pack()

        self.listbox.bind("<<ListboxSelect>>",self.output_selection)

    def output_selection(self,event=None):
        print(self.listbox.curselection())

if __name__ == '__main__':
    Application().mainloop()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Philipp68 hat geschrieben: @Alfons: Ich habe mir die Ausgabe angesehen, es wird der Index angegeben und kein String. Vielen Dank für dein Beispiel. D.h für meinen Fall, dass ich den String aus meiner listbox erhalten, den mit meinen Pfad zusammensetzen muss, damit genau diese Datei gelöscht wird?
Es wird eine Liste von Indizes ausgegeben. Frage: wählt man bei Dir nur einen Eintrag aus, oder auch mehrere gleichzeitig.
Wenn es nur einer ist durch Return oder Mausklick, kannst Du es auch anders machen. Im GuiDesigner habe ich ein Mausklick Event und ein Return Event dran hängen und lese es so aus:

Code: Alles auswählen

def do_lbox_click(event,lbox,entry,isMouse):
    if isMouse: text = lbox.get(lbox.nearest(event.y))
    else: text = lbox.get(tk.ACTIVE)
Den Parameter entry dabei bitte vergessen, das ist der Entry in den ich dann den Listbox Eintrag übernehme.

Du kannst es natürlich auch mit dem oder mehreren Indizes aus curselection auslesen. Die Methode heißt get().
BlackJack

@Philipp68: Zu Problemen mit globalen Variablen kommt es spätestens wenn man den Überblick verliert wann was von welcher ”Funktion” verändert wird. Und vorher halt schon wenn man die Funktionen nicht separat betrachten kann, sondern immer auch den ganzen Rest des Programms im Kopf haben muss, und das dadurch schwieriger ist als nötig.

Das menschliche Gehirn scheint so zwischen 4 und 7 Dinge gleichzeitig im Kopf jonglieren zu können, das ist also der Rahmen der üblicherweise für die maximale Anzahl von Variablen empfohlen wird. Da die meisten Programme natürlich mehr als 7 Variablen haben/brauchen, bricht man Probleme auf kleinere Teilprobleme herunter, die man isoliert betrachten kann und in separaten Funktionen oder Methoden mit wenigen Zeilen Code lösen kann. Dort kann man sich dann auch üblicherweise auf diese kleinere Anzahl von lokalen Variablen und/oder Attributen beschränken.

Globale Variablen machen auch die Fehlersuche schwieriger, weil man nicht einzelne Funktionen isoliert mit Testwerten ausführen kann, sondern immer auch die Variablen ausserhalb der Funktion berücksichtigen muss, die von dieser Funktion verwendet oder verändert werden, oder von Funktionen die von der zu testenden Funktion aufgerufen werden. Theoretisch kann ja jede davon irgend etwas globales ändern und damit Einfluss auf das Ergebnis und den Fehler haben. Um das zu wissen, muss man wieder *alle* Funktionen kennen, und nicht nur die, die man gerade testet.

Programm auf Modulebene macht ebenfalls das testen schwerer. Man kann nicht mal eben das Modul in einer Python-Shell importieren um einzelne Funktionen zu testen, ohne dass das Hauptprogramm losläuft. Automatisierte Tests gehen so auch nicht. Und auch andere Werkzeuge wie Sphinx, zur Erstellung von Dokumentation kann man mit solchen Modulen nicht verwenden. Und es gibt Module wie `multiprocessing` und `concurrent.futures` die erwarten das man das als Programm ausgeführte Modul zumindest unter Windows importieren kann, ohne das dabei gleich wieder das Programm als ganzes startet.

Komplett ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
import os
import Tkinter as tk
from glob import iglob

PROGRAM_PATH = os.path.dirname(__file__)
CONFIG_PATH = os.path.join(PROGRAM_PATH, 'Config')


def iter_filenames(path, extension):
    return (
        os.path.basename(p) for p in iglob(os.path.join(path, '*' + extension))
    )


def _on_error(_filename, exception):
    raise exception


def delete_files(filenames, on_error=_on_error):
    for filename in filenames:
        try:
            os.remove(filename)
        except Exception as error:
            on_error(filename, error)


class Application(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.filenames_listbox = tk.Listbox(self)
        self.filenames_listbox.pack()
        tk.Button(self, text='Load', command=self.on_load).pack()
        tk.Button(self, text='Delete', command=self.on_delete).pack()

    def on_load(self):
        filenames = sorted(iter_filenames(CONFIG_PATH, '.csv'))
        self.filenames_listbox.delete(0, tk.END)
        self.filenames_listbox.insert(0, *filenames)

    def on_delete(self):
        indices = self.filenames_listbox.curselection()
        filenames = [
            os.path.join(CONFIG_PATH, self.filenames_listbox.get(i))
            for i in indices
        ]
        # 
        # TODO Instead of ignoring exceptions while deleting files, maybe
        #       collect errors and display them after deleting.
        # 
        delete_files(filenames, lambda filename, exception: None)
        # 
        # Need to delete in reverse order so not yet deleted entries
        # are not moved to a different index.
        # 
        for index in sorted(indices, reverse=True):
            self.filenames_listbox.delete(index)


def main():
    Application().mainloop()

 
if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 18294
Registriert: Sonntag 21. Oktober 2012, 17:20

@Philipp68: bei dem vielen Kopieren wären aber zwei Stunden, die Du zum Lernen von Schleifen investierst, gut angelegte Zeit.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Wäre es nicht besser 'extended' zu nehmen?

Code: Alles auswählen

self.listbox = tk.Listbox(self,selectmode='extended')
Da kann man dann auch mehrere Einträge auf einmal auswählen und das auch per Tastatur.

Außerdem sollte man Checkbuttons nicht Radio nennen, weil man dann denkt, dass das Radiobuttons wären.

Verwirrend ist auch das:

Code: Alles auswählen

def Radio7():
    if v7.get():
            Radio7.config(bg='green')
    else:
            Radio7.config(bg='red')
Da gibt es eine Funktion Radio7 und einen Checkbutton der genauso heißt. Und v1 bis v24 sowie die Callbacks Radio1 bis Radio24 kannst Du Dir sparen, weil die alles dasselbe tun.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Phillip68: und so kannst Du Dir den ganzen Code für die Callbacks Radio1 bis Radio 24 sparen:

Code: Alles auswählen

from functools import partial

def radio_v1_v24_command(checkbutton):
    checkbutton['bg'] = 'green' if checkbutton.variable.get() else 'red'
        

variable = tk.IntVar()
Radio1 = Checkbutton(f3,bg='red',height=1, width=1,variable=variable,onvalue=1,offvalue=0) #3Uhr
Radio1.place(x=590+160.0, y=280+0.0)
Radio1.variable = variable
Radio1['command'] = partial(radio_v1_v24_command,Radio1)


# Dann wäre noch ein Dictionary zu empfehlen, damit man dann auch mehrere behandeln kann und auch über Pins Werte setzen kann
# besser wäre auch mit 0 anzufangen, weil man dann bei range den Startwert nicht mit angeben muss, aber das ist Nebensache
Checkbuttons[1] = Radio1
Wenn man die Koordinaten auch berechnen kann, kannst Du die Checkbuttons 1 bis 24 auch in einer Schleife erzeugen und die Namen Radio1 bis Radio24 braucht man da auch nicht, denn sie sind ja im Dictionary eingetragen.

Wenn alle Checkbuttons im Dictionary eingetragen sind, schrumpft resetsmdled() ganz gehörig zusammen, denn das machst Du dann mit einer Schleife.
Antworten