Fotos von Kamera laden und sortieren

Du hast eine Idee für ein Projekt?
BlackJack

@iScream: Vergiss dass es ``global`` gibt und arbeite das Tutorial in der Dokumentation durch.

Die Abfrage von `__name__` kommt für gewöhnlich ganz ans Ende und der ganze Quelltext auf Modulebene sollte in einer Funktion verschwinden. *So* bringt das überhaupt nichts, sondern dient zusammen mit den beiden "Funktionen" und dem ``global`` nur der Verwirrung des Lesers.

`os.chdir()` sollte man IMHO auch gleich wieder vergessen. Das Arbeitsverzeichnis zu ändern ist keine gute Idee, weil man da schnell Probleme bekommt, wenn man das an mehr als einer Stelle machen wollte. Dann weiss man nie so recht wo man gerade ist.

Statt der ersten Schleife könnte man das `glob`-Modul mal anschauen.

Das Du den Text von "verschoben" nach "kopiert" verändert hast ist Blödsinn. Egal was faktisch passiert -- es ist am Ausgangsort hinterher nicht mehr da, dass ist also nicht das was man erwarten würde, wenn das Programm nur meldet, dass kopiert wurde.

Es werden keine Fehler behandelt und der Test vor dem Anlegen eines Verzeichnisses ist überflüssig. Da sollte man einfach versuchen das Verzeichnis anzulegen und eventuelle Fehler dabei ordentlich behandeln. Es können da so nämlich mindestens zwei Dinge passieren: 1. Der Test sagt alles ist okay, und kurz bevor das Verzeichnis angelegt werden soll, macht das ein anderer Prozess, oder 2. es gibt kein Verzeichnis mit dem Namen, aber eine *Datei*. In beiden Fällen haut's Dir eine Ausnahme um die Ohren.
iScream
User
Beiträge: 11
Registriert: Samstag 25. Juli 2009, 11:15

Die Abfrage von `__name__` kommt für gewöhnlich ganz ans Ende und der ganze Quelltext auf Modulebene sollte in einer Funktion verschwinden. *So* bringt das überhaupt nichts, sondern dient zusammen mit den beiden "Funktionen" und dem ``global`` nur der Verwirrung des Lesers.
Habe das verschwinden lassen ;) Als ich was anderes hier gepostet hatte, war die erste Änderung durch einen anderen dieses "if __name__ ==..." daher ging ich davon aus, damit spart man sich genauso Kritik wie mit konsequentem Englisch. Jetzt kann das "global" doch bleiben, oder? Ich weiß nämlich sonst nicht, wie ich den Wert der Funktion in eine Variable außerhalb verpackt kriege.
`os.chdir()` sollte man IMHO auch gleich wieder vergessen. Das Arbeitsverzeichnis zu ändern ist keine gute Idee, weil man da schnell Probleme bekommt, wenn man das an mehr als einer Stelle machen wollte. Dann weiss man nie so recht wo man gerade ist.
Wie sollte ich es wohl vermeiden? Zurzeit sehe ich keinen anderen Weg, an die Bilder zu gelangen :?: (Bitte Vorschlag)
Statt der ersten Schleife könnte man das `glob`-Modul mal anschauen.
Werde ich mir anschauen. Kenne aber bisher kaum Module, daher helfe ich mir mit "Bordmitteln" (obwohl Module in gewissermaßen auch "Bordmittel" sind)
Das Du den Text von "verschoben" nach "kopiert" verändert hast ist Blödsinn. Egal was faktisch passiert -- es ist am Ausgangsort hinterher nicht mehr da, dass ist also nicht das was man erwarten würde, wenn das Programm nur meldet, dass kopiert wurde.
:lol: Dickes Sorry! Ich hatte den funktionierenden Code noch "verschönert" vor dem posten. Daher war das Löschen nicht auskommentiert - jetzt aber
Es werden keine Fehler behandelt und der Test vor dem Anlegen eines Verzeichnisses ist überflüssig. Da sollte man einfach versuchen das Verzeichnis anzulegen und eventuelle Fehler dabei ordentlich behandeln. Es können da so nämlich mindestens zwei Dinge passieren: 1. Der Test sagt alles ist okay, und kurz bevor das Verzeichnis angelegt werden soll, macht das ein anderer Prozess, oder 2. es gibt kein Verzeichnis mit dem Namen, aber eine *Datei*. In beiden Fällen haut's Dir eine Ausnahme um die Ohren.
Habe nun ein "try...except" eingebaut. Ich hoffe das ist so OK ;)


Vielen Dank, dass du dir die Zeit genommen hast, ich lerne hierdurch ne ganze Menge :wink:

Ich habe noch eine Änderung des Ordnernamens vollzogen: Aus 2009-08-12 wird 2009_08_12. Das habe ich gemacht, da die Ordner nahtlos zu den anderen Ordnern passen sollen, die ich habe. Des Weiteren ist nun noch eine Variable Englisch.

Der neue Code:

Code: Alles auswählen

import shutil
import os
import datetime
from tkFileDialog import askdirectory

#Quelle waehlen und als "source" speichern
def choosesource():
    global source    
    source = askdirectory()
#Ziel waehlen und als "destination" speichern
def choosedestination():
    global destination    
    destination = askdirectory()
#Startet die Abfragen beim Aufruf
choosesource()
choosedestination()    

os.chdir(source)
bilder = []
for dat in os.listdir(os.getcwd()):
     if dat.endswith('.jpg') or dat.endswith('.JPG'):
          bilder.append(dat)


#ordner erstellen und kopieren
for bild in bilder:
     cap = str(datetime.date.fromtimestamp(os.path.getatime(bild)))
     capturedate = cap[0:4] + '_' + cap[5:7] + '_' + cap[8:10]
     os.chdir(destination)
     try:
        os.mkdir(capturedate)
     except:
        pass
     os.chdir(source)
     shutil.copy(bild, os.path.join(destination, capturedate))
#     os.remove(bild)
     print bild, 'wurde nach ', capturedate, 'kopiert'
#Jetzt wurde kopiert :P 'os.remove' erstmal als Kommentar behalten, falls ich mir es anders ueberlege


print 'Vorgang abgeschlossen'
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Jetzt kann das "global" doch bleiben, oder? Ich weiß nämlich sonst nicht, wie ich den Wert der Funktion in eine Variable außerhalb verpackt kriege.
so?

Code: Alles auswählen

def choosesource():
    return askdirectory()
source=choosesource()
for file in os.listdir(source): pass
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Wenn du die '-' lieber mit '_' ersetzen willst, benutze cap.replace('-', '_')

@ BlackJack
Was ist an os.chdir() so schlimm?
iScream hat geschrieben:

Code: Alles auswählen

from tkFileDialog import askdirectory

#Quelle waehlen und als "source" speichern
def choosesource():
    global source    
    source = askdirectory()
#Ziel waehlen und als "destination" speichern
def choosedestination():
    global destination    
    destination = askdirectory()
#Startet die Abfragen beim Aufruf

Meiner Meinung nach .... schwachsinn.. :roll:

Die Funktionen machen beide das gleiche!
ein "return" wäre da sinnvoller.

Und da "asdirectory()" auch nichts anderes mach als den Pfad zurückzuliefern, kann man gleich schreiben

print 'Start wählen'
START=asdirectory()
print 'Ziel wählen'
ZIEL=askdirectory()
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Pascal hat geschrieben:Und da "asdirectory()" auch nichts anderes mach als den Pfad zurückzuliefern, kann man gleich schreiben

print 'Start wählen'
START=asdirectory()
print 'Ziel wählen'
ZIEL=askdirectory()
Neu gelerntes sofort umgesetzt :-) Du solltest aber noch einen Blick in PEP8 werfen.

Zum chdir: Damit wechselst du das Arbeitsverzeichnis. Normalerweise sollte man/ein Außenstehender davon ausgehen können, dass das Arbeitsverzeichnis gleich bleibt und sich dort befindet, wo man es angegeben hat/das Script gestartet hat. Außerdem arbeitet man beim Wechsel des Verzeichnis auf globalen Variablen. Rufst du verschachtelte Funktionen auf, die alle was auf Verzeichnissen machen und alle irgendwie wechseln, dann baust du dir mit Sicherheit einen Fehler ein.

@iScream: der Code ist ja noch schlimmer geworden :shock:

Das "if __name__ ..." sollte nicht verschwinden sondern ganz ans Ende des Codes. Was verschwinden soll ist der Code auf Modulebene. Das bedeutet bei dir konkret: Teile dein Programm in sinnvolle Funktionen auf.

"global" ist niemals eine Lösung. Verwende es einfach nicht. Wenn du es einsetzt, dann machst du etwas falsch. Die Lösung dieses Problems sind Rückgabewerte und Parameter. Es gilt: eine Funktion sollte keine Seiteneffekte haben, natürlich abgesehen von IO und Konsorten. Maximal sollten sich die Parameter verändern.

Dein try-except fängt ALLE Fehler ab, dass kann manchmal zu seltsamen Fehlern führen, die nicht nachvollziehbar sind. Gebe also immer die Fehler an, die abgefangen werden sollen. Eine Fehlerbehandlung mit einem "pass" ist in den Meisten fällen auch Unfug - so wie bei dir. Es kann nämlich durchaus passieren, dass das Verzeichnis nicht erstellt werden konnte. Dann läuft dein Programm weiter, liefert dann aber sicher in Zeile 35 einen Fehler.
Das Leben ist wie ein Tennisball.
BlackJack

@iScream: Du hast das Falsche verschwinden lassen. Der Code sollte in eine Funktion, die dann in der ``if __name__``-Überprüfung aufgerufen wird.

``global`` sollte man grundsätzlich meiden. Wenn Du nicht weisst wie man mit Funktionen arbeitet, solltest Du wirklich mal das Tutorial in der Python-Dokumentation durcharbeiten.

Wieso weisst Du nicht, wie Du ohne das Arbeitsverzeichnis zu wechseln an die Bilder kommst? Du *tust* das doch sogar schon für das Zielverteichnis, weil man ja immer nur in einem Arbeitsverzeichnis gleichzeitig sein kann.

Für das `capturedate` würde ich nicht die `str()`-Repräsentation von dem `date`-Objekt nehmen, sondern dessen `strftime()`-Methode. Dann kannst Du auch gleich Unterstriche einsetzen, und musst die nicht hinterher ersetzen.

Ein ``except`` ohne eine konkrete Ausnahme in dem dann auch noch einfach gar nichts gemacht wird, ist keine gute Idee. Damit kann man Fehlersituationen "maskieren", nach denen man sich im Ernstfall dumm und dusselig sucht, weil man wichtige Informationen einfach nicht bekommt.

@Pascal: Bei `os.chdir()` muss man *programmweit* immer aufpassen, dass man an jeder Stelle weiss, welches Verzeichnis zur Laufzeit das Aktuelle an dieser Stelle ist. Spätestens wenn man das auch in Bibliotheken macht, oder mehrere Threads benutzt, wird so ein Programm unwartbar. Schon bei bedingten Verzweigungen kann es unübersichtlich werden, weil man für jeden möglichen Auführungspfad sicherstellen muss, das die Verzeichniswechselei richtig läuft und nicht irgendwo mal "übergangen" wird.
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

BlackJack hat geschrieben: @Pascal: Bei `os.chdir()` muss man *programmweit* immer aufpassen, dass man an jeder Stelle weiss, welches Verzeichnis zur Laufzeit das Aktuelle an dieser Stelle ist. Spätestens wenn man das auch in Bibliotheken macht, oder mehrere Threads benutzt, wird so ein Programm unwartbar. Schon bei bedingten Verzweigungen kann es unübersichtlich werden, weil man für jeden möglichen Auführungspfad sicherstellen muss, das die Verzeichniswechselei richtig läuft und nicht irgendwo mal "übergangen" wird.
Gut, das seh ich ein.
bei shutil.copy müsste man dann allerdings (START+'/pic.jpg' , ZIEL) angeben. Dann geht das auch. (Das war nämlich mein Problem, deswegen der Verzeichnis wechsel)

Und noch eine Frage hinterher:
Wäre shutil.copy2 nicht eigentlich besser geeignet?
Oder shutil.move ?
iScream
User
Beiträge: 11
Registriert: Samstag 25. Juli 2009, 11:15

Bitte erklärt das nochmal genauer, ich verstehe es leider noch nicht ganz.
Das Programm soll also mit "if __name__" aufgerufen werden?
Es sollte nur aus einer Funktion bestehen?!

Das except sollte zunächst auch nur zur Maskierung dienen. Arbeite ich noch dran ;)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

#deine Funktionen hier

def main():
    #hier dein Code, der auch die Funktionen auruft

if __name__ == "__main__":
    main()
Dann wird sichergestellt, dass die "main"-Funktion nur dann ausgeführt wird, wenn die Datei ausgeführt wird. Wird nur das Modul importiert, dann passiert nichts.

@Pascal:
Pascal hat geschrieben:bei shutil.copy müsste man dann allerdings (START+'/pic.jpg' , ZIEL) angeben
Nein, dann möchtest du "os.path.join" benutzen.
Das Leben ist wie ein Tennisball.
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

ich habe mal probiert das os.chdir() zu vermeiden
Das Programm ist jetzt allerdings noch ungetestet (Hatte keine passende Verzeichnisse)

Code: Alles auswählen

import shutil
import os
import datetime
from tkFileDialog import askdirectory 

print 'Startverzeichnis wählen:'
start=askdirectory()

print 'Zielverzeichnes wählen:'
ziel=askdirectory()

print 'Alle gefundenen Bilder in %s werden nach %s kopiert' %(start, ziel)

print 'Sollen die Bilder nach dem kopieren aus dem Startverzeichnis gelöscht werden? (Y oder N)'
bilder_loeschen=raw_input()
d={'Y':True, 'N': False}
try:
     bilder_loeschen=d[bilder_loeschen]
except KeyError:
     print 'Du Idiot! Nur Y oder N eingeben'
    #raise SystemExit('Selber Schuld! (Gib beim nächsten Mal Y oder N ein!)')


bilder=[]
for dat in os.listdir(start):
     if dat[:-4] in ['.jpg', '.JPG', '.gif', '.png', '.bmp']:
          bilder.append(dat)

#ordner erstellen und kopieren

for bild in bilder:
     aufnahme=str(datetime.date.fromtimestamp(os.path.getatime(bild)))
     
     if not os.path.isdir(os.path.join(ziel, aufnahme)):
          os.mkdir(os.path.join(ziel, aufnahme))

     shutil.copy(os.path.join(start, bild), os.path.join(ZIEL, aufnahme))
     
     if bilder_loeschen:
          os.remove(os.path.join(start, bild))
          print bild, 'wurde nach ', aufnahme, 'verschoben'
     else:
          print bild, 'wurde nach ', aufnahme, 'kopiert'
     

print 
print 'Vorgang abgeschlossen'
Ich hoffe ich hab nichts übersehen :roll:
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Code: Alles auswählen

import shutil
import os
import datetime
from tkFileDialog import askdirectory
from tkMessageBox import askyesno

def movefiles(from_, to, keepold=False):
	print 'Alle gefundenen Bilder in %s werden nach %s kopiert' %(start, ziel)
	for file in os.listdir(from_):
		if filter( file.lower().endswith, ['.jpg', '.gif', '.png', '.bmp'])
			#ist das so sinnvoll?
			aufnahme=str(datetime.date.fromtimestamp(os.path.getatime(file)))
			
			try:
				os.mkdir(os.path.join(to, aufnahme))
			except OSError, error:
				if error.errno !=17:
					raise
			if keepold:
				shutil.copy(os.path.join(from_, file), os.path.join(to, aufnahme))
				print bild, 'wurde nach ', aufnahme, 'kopiert'
			else:
				shutil.move(os.path.join(from_, file), os.path.join(to, aufnahme))
				print bild, 'wurde nach ', aufnahme, 'verschoben'



def main():
	#wieso nochmal schreiben wenn per gui gefragt wird?
	print 'Startverzeichnis wählen:'
	start=askdirectory()

	print 'Zielverzeichnes wählen:'
	ziel=askdirectory()
	
	movefiles(start, ziel, askyesno('Achtung', 'remove old?'))
	print 'Vorgang abgeschlossen' 

if __name__ == '__main__':
	main()
ich habe es mal etwas bereinigt, ist aber auch noch etwas verbesserungswürdig
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

- zum Beispiel nur eine Sprache verwenden
- file nicht zu überschreiben
- und

Code: Alles auswählen

if keepold:
    shutil.copy(os.path.join(from_, file), os.path.join(to, aufnahme))
else:
    shutil.move(os.path.join(from_, file), os.path.join(to, aufnahme))
als

Code: Alles auswählen

action = shutil.copy if keepod else shutil.move
action(os.path.join(from_, file), os.path.join(to, aufnahme))
Sonst sind es ja nur noch sehr kleine Kleinigkeiten.
Das Leben ist wie ein Tennisball.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

EyDu hat geschrieben:- file nicht zu überschreiben
:oops:

bei der sprache war ich einfach zu faul :D
BlackJack

Ich habe auch mal etwas (ungetestetes) gehackt:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import division, with_statement
import errno
import os
import shutil
from datetime import date as Date
from tkFileDialog import askdirectory


def is_jpg(filename):
    return filename.lower().endswith('.jpg')


def iter_jpg_names(path):
    return (os.path.join(path, f) for f in os.listdir(path) if is_jpg(f))


def copy_file(source_path, target_path):
    date = Date.fromtimestamp(os.path.getatime(source_path))
    full_target_path = os.path.join(target_path, date.strftime('%Y_%m_%d'))
    try:
        os.mkdir(full_target_path)
    except OSError, error:
        if error.errno != errno.EEXIST:
            raise
    shutil.copy(source_path, full_target_path)


def main():
    source_path = askdirectory()
    target_path = askdirectory()
    for jpg_filename in iter_jpg_names(source_path):
        copy_file(jpg_filename, target_path)
        print '%r wurde nach %r kopiert' % (jpg_filename, full_target_path)
    print 'Vorgang abgeschlossen.'


if __name__ == '__main__':
    main()
@jbs: So umgeht man die "magische" 17 für den Fall, dass der Pfad schon existiert.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

man lernt nie aus
iScream
User
Beiträge: 11
Registriert: Samstag 25. Juli 2009, 11:15

Hey ;)

Es gibt sicher noch Verbesserungmöglichkeiten am Code, doch er übertrifft jetzt schon meine Erwartungen an Komfort und Funktionalität bei weitem :wink:

Ich möchte euch allen danken, dass ihr euch soviel Zeit genommen habt. Ich habe viel Neues gelernt und einen gut funktionierendes Skript, was mich von einer weiteren Windows-Software erlöst und mich somit Linux näher bringt ;)


lg,
iScream
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Allerdings ist das immernochnicht das, was du wolltest.
Die Bilder werden nämlich nicht nach dem Aufnahmedatum einsortiert!
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

iScream: Hast du schon von digikam gehört? Ist eine komplette Fotoverwaltung mit Bewertung, Tagging, mächtigen Plugins etc. und läuft unter GNU/Linux. Ein echter Grund zum Wechseln. :)
Antworten