Ein Adressbuch mit Tkinter

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo !
Habe mein Adressbuch mal mit Tkinter - Oberfläche gemacht. Ich speichere die Label, Entry etc. in einer Liste, damit ich im Programmablauf auf diese zugreifen kann - geht es auch anders ?

http://www.ubuntuusers.de/paste/13200/

Textdatei anlegen:

Muster
Max
Maxstrasse 1
Maxhausen 1000
01234/12345
0123/12345
01234/23456
max@muster.de
Arbeit


gruss und dank frank
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ohje, Tkinter schon wieder per Stern-Import ins Modul gezogen, von PEP 8 konfimität gar nicht erst zu sprechen.

Ich würde Buttons die die Daten nicht anzeigen nicht mit Inzizes abspeichern sondern eher in einem Dict mit namen. Oder noch besser an die Instanz der Klasse binden (mir wollte grade das böse Wort Instanzvariablen entrutschen).

``Adressbuch.vor()`` und ``Adressbuch.zurueck()`` finde ich etwas seltsam. Was passiert wenn ein ``Adressbuch`` vor oder zurückgeht? Das scheint mir sinnloses Klassendesign zu sein, vor allem weil die Klasse auch stark mit der Anzeigelogik verdrahtet ist.

Ebenso finde ich ``self.zeahler`` deplaziert. Wozu soll der gut sein (ohne jetzt die genaue Funktionsweise des Programms entziffern zu wollen)? Kann man den Zählen wert nicht aus einer sinnvollen Datenstruktur in die die Einträge gespeichert sind heruasbekommen? Wenn man die Datenstruktur und den Zähler einzeln behandelt kommt es einfacher dazu, dass der Zähler und der Wahre wert untereinander nicht stimmen - man muss somit aufpassen dass wenn man an der Datenstruktur etwas ändert, dass dann auch der Zähler aktualisiert wird. Unnötig, wie ich finde.

Überhaupt hätte ich das ganz anders Implementiert. Der Parameter ``datei`` gehört wenn, dann schon eher an ``laden()`` und ``ende()`` (speichern?). Die Klassenstruktur wirkt so gezwungen - ein reales Objekt würde im richtigen Leben ganz sicher nicht so aussehen. Noch dazu fliegt das Programm auf die Nase, wenns kein Adressbuch gibt - dann sollte ein leeres erstellt werden, statt an einer Exception zu scheitern. ``os.path.exists()`` existiert ja, open() im Read-Mode wirft ja auch eine Exception auf die man reagieren kann. ``laden()`` gibt wiederrum Adressen zurück - als Methode einer Klasse würde man eher erwarten, dass die Aderssen direkt in die Klasse geladen werden.

Ich würde es eher so machen: EIn Adressbuch-Objekt, dass eine Anzahl von Kontakt-Objekten beinhalten kann. Die Kontaktobjekte beinhalten die Daten über die Kontakte, die Adressbuch-Klasse kümmert sich um das Speichern und Laden, ebenso wie um das Suchen.
Dazu noch eine Klasse die sich um die GUI-Dinge kümmert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke Leonidas !

Ich würde mal sagen, dass ich von objektorientierter Programmierung keine Ahnung habe und alles vermischt habe. Ich habe mal versucht es zu verstehen und habe jetzt einige Fragen.

Ein Objekt wäre eine Adresse in Form einer (bei mir) Liste mit den Adressdaten.
Attributname -> self.adresse
Attributwert -> ein Adressdatensatz

Klassenattribute (für alle Objekte gültig) -> feldnamen = ('Name', 'Vorname'. etc....

Ich erstelle eine Klasse für die GUI. Diese Klasse ist nun meine 'Oberklasse', da doch diese dem Objekt auferlegt, dass es verschiedene Zustände/Verhaltensweisen annehmen soll ?!. Das Objekt soll sich mit einem neuen Datensatz befüllen oder soll ein noch nicht vorhandener Datensatz werden etc.

Die Klasse für laden(), speichern() und suche() ist eine Klasse der Klasse für die GUI ?

gruss und dank frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo !

Würde so die Adressbuch-Klasse aussehen ?

Code: Alles auswählen

class Adressbuch(object):
    
    def __init__(self):
        self.zaehler = 0
        self.adressen = self.laden()
        
    def laden(self):
        adressen = (('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678'),
                    ('Tuxi', 'Tux', 'Tuxstr. 21','2 Tuxhausen', '023456734'))
        return adressen

    
    def speichern(self):
        pass

    
    def getAdresse(self):
        adresse = self.adressen[self.zaehler]
        if self.zaehler == len(self.adressen) - 1:
            self.zaehler = 0
        else:
            self.zaehler += 1
        return adresse


    def sucheAdresse(self, such_begriff):
        suche = list()
        for adresse in self.adressen:
            for eintrag in adresse:
                if such_begriff.upper() in eintrag.upper():
                    suche.append(adresse)
                    break
        return suche
So kommt man an die Adressen :

Code: Alles auswählen

adresse = Adressbuch()

adresse.getAdresse()
('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678')

adresse.getAdresse()
('Tuxi', 'Tux', 'Tuxstr. 21', '2 Tuxhausen', '023456734')

adresse.sucheAdresse('Tux')
[('Tuxi', 'Tux', 'Tuxstr. 21', '2 Tuxhausen', '023456734')]
gruss und dank frank
BlackJack

Es hat einen kleinen Augenblick gedauert, bis ich `getAdresse()` verstanden hatte. Recht ungewöhnlich und falls man auch Adressen entfernen kann, dann ist die Überprüfung auf "Anzahl der Adressen - 1" fehlerhaft. Und bei einem leeren Adressbuch funktioniert die Methode auch nicht.

Um durch die Adressen zu gehen würde man eher `__iter__()` entsprechend implementieren, dann kann man so ein Adressbuch einfach in einer ``for``-Schleife verwenden. Oder um so eine Art Rolodex™ zu simulieren kann man dann `itertools.cycle()` benutzen.

Code: Alles auswählen

class Adressbuch(object):
    def __init__(self):
        self.adressen = self.laden()
    
    def __len__(self):
        return len(self.adressen)
    
    def __iter__(self):
        return iter(self.adressen)
    
    def laden(self):
        adressen = (('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678'),
                    ('Tuxi', 'Tux', 'Tuxstr. 21','2 Tuxhausen', '023456734'))
        return adressen
So kann man es dann verwenden:

Code: Alles auswählen

In [8]: adressen = Adressbuch()

In [9]: len(adressen)
Out[9]: 2

In [10]: for adresse in adressen:
   ....:     print adresse
   ....:
('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678')
('Tuxi', 'Tux', 'Tuxstr. 21', '2 Tuxhausen', '023456734')

In [11]: import itertools

In [12]: rolodex = itertools.cycle(adressen)

In [13]: rolodex.next()
Out[13]: ('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678')

In [14]: rolodex.next()
Out[14]: ('Tuxi', 'Tux', 'Tuxstr. 21', '2 Tuxhausen', '023456734')

In [15]: rolodex.next()
Out[15]: ('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678')

In [16]: rolodex.next()
Out[16]: ('Tuxi', 'Tux', 'Tuxstr. 21', '2 Tuxhausen', '023456734')
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

Mein Ansatz war ja es nach dem Tip von Leonidas zu machen. Eine Klasse für laden, speichern und suchen. Da ich ja schon unbewusst das ganze Adressbuch übergeben habe, ist das 'Nachladen' der einzelnen Adressen ja unötig - oder ?. Das Suchen in der Adressbuch - Klasse eigentich auch !?. Das mit itertools löst meine self.zaehler Probleme - was es so alles schönes gibt. Dafür muss aber die Methode 'def __iter__(self) ' vorhanden sein ?

gruss und dank frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Habe mich mal eine ein Version mit dem realen Laden und Speichern der Adressen gemacht.

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8
import pickle

class Adressbuch(object):
    feldnamen = ('Name:','Vorname:', 'Strasse:', 'Ort:',
                 'Festnetz:', 'Handy:','Arbeit:', 'email:',
                 'Bemerkung:')
    
    def __init__(self, adressbuch):
        self.adressen = self.laden(adressbuch)


    def __iter__(self):
        return iter(self.adressen)
    
        
    def laden(self, adressbuch):
        try:
            datei = file('adressbuch.dmp', 'r')
        except IOError:
            adressen = list()
            adresse = list()
            for name in self.feldnamen:
                adresse.append('')
            adressen.append(adresse)
            return adressen   
        adressen = pickle.load(datei)
        datei.close()
        return adressen
    
    
    def speichern(self, adressen):
        datei = file('adressbuch.dmp', 'w')
        pickle.dump(adressen, datei)
        datei.close()
gruss und dank frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Habe das Adressbuch mal umgebaut , doch die Funktion zurückblättern geht jetzt nicht mehr - oder gibt es das Gegenteil von next() (z.B.: back()) ?

http://www.ubuntuusers.de/paste/13524/

gruss frank
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Also ich würde ja bei ``Adressbuch.laden()`` nicht die Adressen zurückgeben, sondern den internen Zustand der Klasse ändern, so dass die Klasse nun die Liste der Adressen enthält. Dann kann man der Funktion auch noch einen Parameter ``filename`` mitgeben, der Definiert aus welcher Datei überhaupt geladen wird.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke Leonidas !!

Das würde man mit einer Methode übergeben ( z.B. wie die Methode zum itterieren --> def ___iter___(self)) Habe in meinen Buch und hier auf dem Board sowas nicht gefunden ( z. B.:" def___list___(self)"). Könntest du mir da helfen ?

gruss frank
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich hätts etwa so gemacht wie in Paste #2334. Man könnte auch ``Adressbuch.__init__`` einen Dateinamen-Parameter mitgeben, so dass es dort ``Adressbuch.laden()`` aufruft, aber an sich ist die Idee gar nicht mal so gut, denn man könnte ja verschiendene Laden-Funktionen machen und dann wäre das wohl nicht optimal. Solche Funktionen könnten sein ``laden_aus_pickle(self, dateiname)``, ``laden_aus_datenbank(self, tabellenname)`` etc.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke Leonidas !

Das was Du gepostet hast sieht schon schön aus - aber was es so macht ?

Ich habe einen kleine Fehler gefunden. Es fehlt ein Unterstrich in self.custom_attrs.append(key).

So wie ich OOP verstanden habe, wir dem Objekt Eigenschaften mit den definierten Methoden übergeben. Diese Eigenschaften sind doch in meinem Beispiel nur ein einziges mal relevant, da ich die self.adressen ja mit Adressbuch_Gui ständigen verändere und somit ich die Verhaltensweisen des Objektes immer wieder neu definieren muß oder ? Die Anzahl der Adressen ändert sich doch beim Einfügen einer Adresse und es ist kein .append oder len möglich. Deswegen habe ich in meinem Beispiel die self.ladressen (für .append etc.) und die self.iadressen (für .next()). Das man sie gleich als eine Liste übergibt, finde ich auch ok, doch das habe ich ja nicht hinbekommen und mache es über den Umweg mit Aufruf: self.ladressen = self.adress_buch.laden(). Das ganze Übergeben von Eigenschaften macht für mich irgendwie keinen Sinn ?!

Dein Beispiel macht dies evt. möglich doch wie o.b. muß ich es erst mal verstehen lernen - habe dieses Wochenende frei und viel Zeit :-).

gruss und dank frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Habe da mal etwa gefunden In A Byte of Python:

http://abop-german.berlios.de/read/repr-function.html

Code: Alles auswählen

>>> liste = []
>>> liste.append('element')
>>> `liste`
"['element']"
>>> repr(liste)
"['element']"
gruss frank
BlackJack

Aus Deiner Beschreibung was Du denkst was OOP ist, werde ich nicht wirklich schlau.

Und hier: http://www.ubuntuusers.de/paste/13524/ ist `Adressbuch` kein wirkliches Objekt. Also formal ist es schon eines, aber im OOP Sinn nicht. Wo sind denn da die Adressen? Von einem Adressbuch erwartet man, das es die Adressen und die notwendigen Methoden zum Umgang mit ihnen enthält. Stattdessen sind bei Dir die Adressen in der GUI gespeichert und dass dann auch noch zweimal unabhängig voneinander.

Und Dir fehlt es anscheinend auch immer noch an ganz grundlegendem Verständnis was Programmabläufe und Sichtbarkeitsbereiche von Namen angeht. Anders kann ich mir zum Beispiel `Adressbuch_GUI.vor()` nicht erklären.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke BlackJack !

Die Adressen bleiben im Adressbuch und mit Adressbuch_gui rufe ich immer nur eine ab. Diese hat über die definierten Methoden die richtige Form. Ich lade nicht alle Adressen in das Adressbuch_Gui, sondern nur immer die benötigte Adresse.

Adressbuch_Gui.vor() bedeutet nächste Adresse anzeigen !

gruss frank
BlackJack

Dann reden wir von einem anderem Quelltext. In dem, den ich verlinkt habe wird keine Adresse im Adressbuch gespeichert.

Was `Adressbuch_GUI.vor()` bedeuten *soll* ist klar, mir ist nicht klar warum Du denkst der Code der dort steht würde das tun. Wieso `anzahl`!? Was ist der Sinn der Schleife? Wo kommt `entry` her?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

kaytec hat geschrieben:Ich habe einen kleine Fehler gefunden. Es fehlt ein Unterstrich in self.custom_attrs.append(key).
Fehler sind nicht ausgeschlossen, denn ich habe den Code in paste-Fenster geschrieben ohne ihn zu testen - mich hätten auch IndentationErrors nicht gewundert...

Deine Beschreibung von OOP habe ich, ähnlich wie BlackJack, nicht verstanden :?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo BlackJack !!

Er tut mal das was ich möchte er löscht die Einträge aus den Entrys und fügt dort die neue Adresse ein. Ich erzeuge die Entrys mit einer Schleife, damit ich nicht alle ausformulieren muß. Damit ich die einzelnen Entrys ansprechen kann, übergebe ich sie in die Liste 'self.adressbuch_entry'. Aus dieser Liste hole ich sie mir wieder und lösche den Inhalt und übergebe einen neuen Inhalt, den der nächsten Adresse 'self.adresse = self.iadressen.next()'. Self.adresse ist nun eine Liste und aus der holen ich mir mit entry.insert(0, self.adresse[anzahl] diesen neuen Inhalt. Funktioniert eigentlich recht gut - hatte deswegen gedacht es ist so richtig.

gruss frank
BlackJack

Okay der Code tut doch das was er soll. Ich bin blöd. Oder kann Deinen Code nicht lesen. Der ist so "anders" das er mich verwirrt.

Schön ist das trotzdem nicht. `anzahl` ist keine Anzahl sondern ein Index. Da hätte ich eher `zip()` benutzt um die Elemente der Adresse mit den `Entry`\s zusammen zu bringen. Aber `Adressen` Objekte speichern keine Adressen. Oder das passiert auch wieder auf eine komische Art und ich versteh's nicht.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

@BlackJack !
Das Speicher geht auch. 'self.adress_buch = Adressbuch(self.feldnamen)' habe ich aus Objekorientierte Progammierung mit Python von Michael Wiegand. Der macht es so bei einem Vokabeltrainer. Bei ihm würde es so aussehen (galube ich mal ?) self.a = Adressbuch(self.feldnamen)'. Das finde ich auch nicht so schön und deswegen habe ich den Namen ausformuliert. Wie ich es verstanden habe macht er es ,damit er beim Laden oder Speichern nicht 'Adressbuch(self.feldnamen).laden()' schreiben müsste, sondern damit man schreiben kann: 'self.a.laden()'.

Habe mein Programm mal mit Deinem Ansatz ohne Tkinter gemacht, damit du mein Problem evt. verstehst.

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8 -*-
class Adressbuch(object):
    def __init__(self):
        self.adressen = self.laden()
   
    def __len__(self):
        return len(self.adressen)
   
    def __iter__(self):
        return iter(self.adressen)
   
    def laden(self):
        adressen = (('Max', 'Muster', 'Maxstr. 12', '1 Maxhausen', '012345678'),
                    ('Tuxi', 'Tux', 'Tuxstr. 21','2 Tuxhausen', '023456734'))
        return adressen

    def speichern(self, adressen):
        pass


def suchen(adressen):
    suchergebnis = list()
    suche = raw_input('Suche: ')
    for adresse in adressen:
        for eintrag in adresse:
            if suche.upper() in eintrag.upper():
                suchergebnis.append(adresse)
                break
    for adresse in suchergebnis:
        print adresse
                

def eintragen(adressen):
    #hier würde ich es mit adressen.append(neue_adresse) versuchen !
    #das geht nicht !
    pass


def ausgeben(adressen):
    for adresse in adressen:
        print adresse
    print 'Im Adressbuch befinden sich ' + str(len(adressen)) + ' Adressen'
        

def speichern(adressen):
    Adressbuch().speichern(adressen)


def main():
    adressen = Adressbuch()
    auswahl_zu_funktion = { 'S': suchen,
			    'N': eintragen,
			    'A': ausgeben,
			    'E': speichern }

    while True:
        print 
        print '____(S)uche mach Adresse_____________'
        print '____(N)eue Adresse eintragen_________'
        print '____(A)lle Adresse ausgeben__________'
        print '____(E)nde___________________________'
        print
		
        auswahl = raw_input('Auswahl:').upper()
		
        try:
            funktion = auswahl_zu_funktion[auswahl]
        except KeyError:
            continue
		
        funktion(adressen)
		
        if auswahl == 'E':
            break

if __name__ == '__main__':
	main()
Würde ich es ohne 'def__len' und 'def__iter' schreiben und einfach adressen = Adressbuch().laden() in die main() Funktion schreiben, dann hätte ich kein Probleme mit dem Eintragen von neuen Adressen. Deswegen sieht es für mich irgendwie unnötig aus, diese Methoden zu formulieren. Auch eine Suche in der Klasse Adressbuch ist doch 'blödsinnig', da ich beim Eintragen einer neuen Adresse nach dieser gar nicht suchen kann. Ich müsste die Adressen speichern und neu laden - oder ?

hast du es so gemeint ?

Code: Alles auswählen

def vor(self):
        self.adresse = self.iadressen.next()
        for entry, eintrag in zip(self.adressbuch_entry, self.adresse):
            entry.delete(0, END)
            entry.insert(0, eintrag)

gruss und dank frank
Antworten