Easy Learn - Vokabeltrainer

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Hallo Forum,
Ich entwickle grade an meinem neuen Vokabeltrainer, Easy Learn soll er heißen und eine zwischen-Version, die bereits über die Möglichkeit verfügt, Vokabellisten zu erstellen und zu bearbeiten, ist hier verfügbar. Ich habe aber immernochnicht das Gefühl alles richtig zu machen und denke, dass es doch recht unübersichtlich wird. Sollte ich vielleicht für jedes Toplevel eine eigene Klasse machen? Oder ist das soweit Ok?
Gruß
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ganz kurz nur auf die schnelle: Ich vermisse die zentralen Datenstrukturen... ich sehe da nur GUI und keine grundsätzliche, von der GUI unabhängige Logik. Das dürfte schon ein "Fehler" im Vorgehen sein.

Schreibe doch erst einmal alles notwendige ohne GUI! Überlege Dir, wie Du Vokabeln, das Lernen usw. gestaltest. Das kann man alles ohne GUI machen - nicht falsch verstehen, Du sollst kein CLI bauen, sondern nur die Datenstrukturen und die darauf operierenden Objekte. Diese kannst Du dann auch leicht testen - ohne Benutzerinteraktion.

Wenn Du das hast, dann kannst Du das in eine GUI integrieren, in der Du nach dem "Druck auf einen Button" einfach nur noch die passende, bereits existierende Funktion aufrufst.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Ja, das Problem ist nur, das die Buttons höchstens die Funktionen haben etwas zu löschen, oder die GUI zu beeinflußen, soll ich diese Mini-Funktionen also auch außerbalb der Klasse schreiben? Und bei der GUI, alles in eine Klasse oder eine Klasse pro Fenster/Toplevel?
Danke,
Gruß
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich würde das noch mehr verallgemeinern: Brauchst Du denn für alles und jedes *Klassen*? Klassen sind ein Mittel in Python, etwas zu modellieren, aber nicht das einzige ;-)

Z.B. das Laden / Speichern von Vokabeln lässt sich bestimmt in Funktionen erledigen.

Und wie repräsentierst Du eine Vokabel? Wie organisierst Du ggf. "Lektionen"? Wie "merkst" Du Dir Lernerfolge? Wie wählst Du Vokabeln zum Lernen aus? Wie ermittelst Du die korrekte Antwort? Das sind alles Fragen, die mit dem UI nichts zu tun haben.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Scriptinggamer: Eigentlich verwendest Du noch gar keine Klassen. Die `App`-Klasse ist nur syntaktisch eine Klasse, von der Semantik her hast Du einfach ein Programm, das auf globalen Daten operiert, in eine Klasse gesteckt. Wodurch überhaupt nichts gewonnen ist — es ist nur ein wenig komplexer geworden.

Klassen sind dazu da um zusammegehörige Daten und Funktionen in Objekten zu kapseln um ein grösseres Problem in kleinere Teilprobleme zu zerlegen und dann Teillösungen dafür zu entwickeln. Das ganze Programm in einer Klasse ist keine sinnvolle Zerlegung.

Daten sollte man nicht literal im Quelltext ständig wiederholen. Vokabular heisst auf englisch „vocabulary”. Wenn man den Namen des Unterverzeichnis dahingehend ändern möchte, muss man den gesamten Quelltext danach absuchen und bei jeder Fundstelle schauen ob das Wort geändert werden muss oder nicht. Wenn man am Anfang einmal eine Konstante definiert und die konsequent verwendet, dann muss man den Namen nur an *einer* Stelle ändern.

Auch Quelltextwiederholungen sollte man vermeiden. Das betrifft zum Beispiel die „list comprehension” (LC), welche die Dateinamen im Vokabel-Ordner ausliest, die in vielen Fällen gleich danach in eine Listbox eingetragen werden. Wenn man an dem Code etwas ändern möchte, zum Beispiel das `glob`-Modul für das Erstellen der Liste verwenden möchte, muss man auch hier wieder durch das gesamte Programm gehen und alle Vorkommen finden. Was etwas schwerer sein kann als literale Werte zu finden, weil man Quelltext jedesmal subtil anders schreiben kann und/oder die Namen anders wählen kann und sich deshalb nicht immer alles mit einer einfachen Textsuche finden lässt.

Hyperion hat das Thema Datenstrukturen ja schon angesprochen: Eine Liste mit zwei Elementen die sehr verschiedene Bedeutungen haben und im Programm immer mit nichtssagenden Indexen angesprochen werden, ist keine gute Datenstruktur.

Pfadnamen sollte man mit `os.path.join()` zusammensetzen. Im Moment funktioniert das Programm nicht unter Linux oder MacOS was bei einem Vokabeltrainer etwas unverständlich ist.

Die `cut_text()`-Funktion ist unnötig. Die macht genau das selbe wie ``text[:length]``.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Naja, meien Idee war ja jetzt das jedes Fenster zum objekt wird, also in eine klasse, die __init__ um das Fenster aufzubauen und dann eben methoden die nur die GUI betreffen, also:

Code: Alles auswählen

class Mainwindow:
    def __init__(self, root, filelist):
        ...
    def fill_list(self, v_list, selected)
        ...
Und den Rest in Funktionen außerhalb der Klasse, das gleiche dann wieder für das Editor_Toplevel, bekommt dann eben die Funktion um Listen und Entrys zu füllen und den Rest erledigen wieder Funktionen...
Wenn es da jetzt erstmal nichts auszusetzen gibt, würde ich das dahingehend erstmal ändern und dann auch gleich die Konstanten und Funktionsunterteilung beachten.
Gruß
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Scriptinggamer hat geschrieben: Wenn es da jetzt erstmal nichts auszusetzen gibt, würde ich das dahingehend erstmal ändern und dann auch gleich die Konstanten und Funktionsunterteilung beachten.
Doch! Die gesamte Heransgehensweise... ;-) Aber dazu schrieb ich ja schon mal etwas. Ich würde die GUI erst einmal *komplett* weglassen. Implementiere erst alles, was Du an Datenstrukturen und Logik für den Trainer brauchst. *Anschließend* baue eine GUI, die die entsprechenden Funktionen / Daten nutzt. Ich denke Du wirst dann auch eher merken, welche Art von Funktionen Du brauchst, welche Parameter diese brauchen usw. Auf diese Art und Weise wird das API Deines "Core-Problems" besser, als wenn Du es sofort wieder mit GUI mischst. Denn im Moment ist bei Dir nicht erkennbar, dass Du weisst, welche Abläufe mit der GUI zusammen hängen und welche davon unabhängig sind.

Ein (funktionsloser) GUI-Prototyp kann natürlich auch dabei helfen, sich vorzustellen, welche Abläufe und Aktionen es überhaupt in solch einem Trainer geben kann. Den würde ich aber nicht per Hand coden, sondern mittels eines GUI-Designers (QtDesigner, Glade) erstellen und mit Dummy-Daten füllen. Solche Dummy-Fenster kann man dann mal als Screenshots speichern und ggf. mit einem Grafikprogramm Abläufe zwischen den Elementen einzeichnen oder wichtige Dinge anmakern usw. Eingabeelemente lassen oftmals auch Rückschlüsse über die Art von Datenstruktur zu; z.B. könnte man überlegen, ob man für eine Übersetzung wirklich nur eine 1:1 Beziehung hat, oder doch eine 1:n oder gar n:m. So etwas könnte man daran erkennen, dass man anstatt eines Textfeldes für die Bedeutung ein Listenelement benutzt.

Mit einer gewissen Übung kann man auch GUI und Core-API zusammen entwickeln - Du scheinst aber ja noch eher ungeübt zu sein, weswegen ich Dir wirklich raten würde, erst einmal ohne GUI den Kern zu implementieren.

Vielleicht sollte ich zu so einem Vorgehen mal ein Tutorial schreiben... :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
CasualCoding.org

Hyperion hat geschrieben:Vielleicht sollte ich zu so einem Vorgehen mal ein Tutorial schreiben... :-)
Oh ja, bitte. Das ist genau das, womit ich auch unter Java schon immer Probleme hatte!
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Also versuche ich das Ganze erstmal in der Konsole umzusetzen und ihr helft mir dann dabei GUI davor zuklatschen?
deets

Wenn Konsole heisst "mit text ein/ausgabe" - dann ist das nicht sinnvoll. Wirklich "nur" die Modellierung deines Datenmodells und der Funktionen, die das modifizieren. Dazu vielleicht sogar am besten gleich unittests schreiben, um das auszuprobieren. Die startet man natuerlich ueber die Konsole. Aber du solltest dich erstmal fern halten von dem Versuch, Nutzerinteraktion in das ganze reinzubringen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ob TUI oder GUI ist hier egal, wenn du direkt mit dem User Interface anfaengst, dann laeufst du Gefahr die Logik und die Darstellung zu vermischen wie du es schon getan hast.

Ueberlege dir die Operationen, finde entsprechende Datenstrukturen und _dann_ baue dein UI.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Ich werde es versuchen, aber viele Funktionen Interagieren doch mit dem UI, was soll ich den dann da hin schreiben?
Gruß
deets

Du schreibst TestCases die zb eine Vokabel hinzufuegen, den Karteikasten speichern, laden und dann pruefen, dass die neue Vokabel drin ist. Das kannst du als "richtige" unittest.TestCase Unterklassen machen, oder aber auch erstmal einfach als ein normales Skript, was du daneben legst, und das aus vokabletrainer importiert.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Ich hab jetzt einfach erstmal ein paar Funktiönchen geschrieben (Script), soll ich so erstmal weiter machen? Ich hab da aber ein Problem, woher sollen die einzelnen Funktionen die liste bekommen? Sollte ich z.B. eine Funktion schreiben mit den Argumenten: Vokabelliste, index, die dann den indexten Eintrag der Liste löscht und die neue liste returnt?
brrr, ich bin verwirrt :(
Gruß
deets

Uhhhhh. Bitte gaaanz schnell abgewoehnen, try/execpt ohne konkrete Exception zu verwenden. Und erst recht, um damit Fehler-Codes zu erzeugen, die du dann zurueckgibst.

Auch globale Variablen sollten nicht vorkommen. Wenn du fuer alle moeglichen Funktionen FILES_PATH vorraussetzt, dann ist es besser, eine Klasse zb VocabularyStore zu schreiben, die das als Argument bekommt.

Vielleicht erzaehlst du uns erstmal, was du eigentlich fuer Daten verwalten willst, und was fuer Operationen darauf laufen sollen.
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Dann muss doch der Vocabulary Store wieder global sein oder nicht?
was sollte dann alles in diese Klasse rein?
Ich verstehe auch nicht wie die Funktionen auf die Klasse zugreifen sollen.
Problem bei den Excepts ist, das Appleuser bestimmt keinen WindowsError bekommen oder? wie soll ich das machen?
Wie geht das überhaupt mit Funktionen die gleiche variablen und Klassen benutzen, wenn sie nicht Global sein dürfen?
Ich kann doch nicht jeder Funktion dann meine Instanz mit geben, nur weil irgendeine Funktion die von der aufgerufen wird sie braucht, das kanns ja nicht sein...
verwirrter Gruß
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Scriptinggamer hat geschrieben: Ich kann doch nicht jeder Funktion dann meine Instanz mit geben, nur weil irgendeine Funktion die von der aufgerufen wird sie braucht, das kanns ja nicht sein...
Ganz grundsätzlich: Ja klar! Wenn irgend eine Funktion auf einem Objekt arbeiten muss, dann muss man das dieser Funktion als Parameter übergeben.

Ich frage mich nur, wieso Du schon wieder über irgend eine Klasse philosophierst? Bisher hast Du die Datenstruktur noch nicht ansatzweise beschrieben... und das sollte doch das zentrale Element zu Beginn sein.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Excepts erstmal für Windows gefixt. Bearbeiten der Vokabellisten ohne UI fertig. Was muss ich noch ändern? Wie kommt jetzt GUI davor?
Scriptinggamer
User
Beiträge: 107
Registriert: Sonntag 24. Juni 2012, 16:38
Wohnort: Werder/Havel

Naja, ich philosophiere über klassen weil ich ja die FILES_PATH variable in eine Klasse tun soll, aber auf die sollen nun mehrere Funktionen Zugreifen. Und da hab ich grade keine Ahnung wie ich das anstellen soll...

Und nochwas, wenn ein Button aus einer Klasse eine Funktion aufruft die garkeine Argumente braucht aber auf die Klasse zugreifen muss, muss ich dann immer

Code: Alles auswählen

command = functools.partial(Meine_lieblings_Funktion, self)
benutzen?

Ich würde jetzt erstmal gerne die Funktionen zum Bearbeiten inklusieve GUI erstellen und dannach, wenn ich das verstanden habe die restlichen Funktionen
Gruß, Danke für eure Geduld :D
Zuletzt geändert von Scriptinggamer am Donnerstag 9. August 2012, 19:54, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Scriptinggamer hat geschrieben:Excepts erstmal für Windows gefixt. Bearbeiten der Vokabellisten ohne UI fertig.
Naja... im Grunde genommen ist das immer noch unschön. Du hast ja in dem Sinne gar keine Ausnahmebehandlung eingebaut. deets hatte Dich schon darauf hingewiesen, dass das Mappen von Exceptions auf Rückgabewerte ziemlicher Unsinn ist. Was versprichst Du Dir davon? Welchen Vorteil bringt diese Lösung jetzt für Funktionen, die diese Funktionen aufrufen?

Wie genau sieht Deine Datenstruktur denn aus? Genau wissen wir das immer noch nicht - auch wenn ich schlimmes ahne (da es auf das ursprüngliche Modell hinausläuft).

Beschreibe doch erst einmal das in Worten und mit ausgeschriebenen Daten; Python bietet ja ein Glück prima Literale für Listen, Dictionaries usw.

Dann überlege Dir, welche Art von Operationen Du auf diesen durchführen willst. Persistenz habe ich evtl. als Beispiel genannt (?), das ist aber nicht das einzige geschweige denn auch wichtigste.

Mir fallen da spontan solche Sachen ein:

- Abfragen der Bedeutungen bei "Kenntnis" eines Wortes
- Richtigkeit einer Übersetzung auswerten
- x zufällige Vokabeln aus dem Pool auslesen
- CRUD
- Persitenz (laden, speichern) natürlich auch

Dazu kämen dann noch Dinge, die weitere Datenstrukturen nach sich zögen:

- Richtigkeit einer Antwort für eine Lernsession merken
- "Lektionen" abbilden
- Zeitstempel für eine abgefragte Session / Vokabel hinterlegen
- "Ähnlichkeits"-analyse von Wörtern für unscharfe Fehlerbewertung ("fast" richtig statt nur "richtig" und "falsch")

Aber sicherlich ist das teilweise nicht grad trivial ;-)

Insgesamt ist das natürlich alles eine Frage der Anforderungen. Aber diese wirken sich eben *direkt* auf die Datenstrukturen aus!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten