Weg mit den Leerzeichen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
BlackJack

@Sophus: Du hast da nicht die Widgets in die Liste gepackt, es werden immer noch Zeichenketten, halt von einem anderen Typ, in die Liste gepackt und nicht die Widgets von denen sie abgefragt werden. Und wenn Du Zeichenketten darauf prüfen möchtest ob sie mehr als nur Whitespace-Zeichen enthalten, dann musst Du das halt auch entsprechend überprüfen. Nur halt komplett in der Schleife über Widget-Objekte, also *dort* den Text abfragen, gegebenenfalls in einen anderen Zeichenkettentyp umwandeln und dann zum Beispiel alle Whitespace-Zeichen am Anfang und am Ende entfernen und schauen ob's dann noch Zeichen gibt, was dem Wahrheitswert von Zeichenketten entspricht, und zwar allen drei Typen die wir hier bisher gesehen haben. Die einfachere Schreibweise für zwei Ausdrücke die den gleichen Effekt haben ist in der Regel die bessere. Nur ist es eigentlich recht offensichtlich dass das hier nicht das macht, nicht machen *kann*, wie der Code bei dem man die Whitespace-Zeichen entfernt bevor man den Wahrheitswert der Zeichenkette als Entscheidungskriterium verwendet.

Wenn man (tatsächlich) über die Widget-Objekte iteriert hat man allerdings das Problem das die nicht die gleiche API haben um den Text abzufragen. Und um das sauber objektorientiert zu lösen braucht es IMHO ein bisschen mehr Erfahrung mit den Grundlagen als Du momentan hast. Gefühlt rätsts Du Dich eher durch den Code, die Datentypen, und die API, als das Du tatsächlich verstehst was die einzelnen Teilausdrücke für Werte, Typen, und damit Operationen haben.

Bei der Schleife bin ich zum Beispiel gespannt auf die Antwort was das ``break`` Deiner Meinung nach macht.

Ja, „anti pattern” sind Sachen die man nicht macht. Der Nachteil ist das es komplizierter ist als der Umweg über einen Index, und unflexibler weil der Zugriff über den Index einen Sequenztyp voraussetzt, während direktes Iterieren mit jedem iterierbaren Objekt funktioniert, also auch mit solchen die keinen Indexzugriff bieten. Das `i` ist hier ja auch eigentlich nicht der Index, sondern hauptsächlich hinzugekommen um eine Ausgabe zu erzeugen. Und Benutzer wollen in der Regel keinen Index der bei 0 anfängt, den ”normale” Menschen zählen Dinge ab 1 ab. Was mit `enumerate()` auch einfach möglich ist.

Zeichensatzkodierung ist unter jedem System ein Thema. Da führt kein Weg dran vorbei, das muss man heutzutage als Programmierer verstehen das Thema. Die Zeiten das man sich mit einer Ein-Byte-Pro-Zeichen-Kodierung durchmogeln konnte, also das Thema weitgehend ignorieren weil ”alle” die gleiche Kodierung benutzen, sind schon länger vorbei.

Der u-Präfix ist für literale Unicode-Zeichenketten im Quelltext. GUI-Toolkits machen da in der Regel schon das richtige und können mit Unicode umgehen. *Du* provozierst den Fehler in dem Du versuchst aus Zeichen wieder Bytes zu machen. Ohne eine Kodierung anzugeben. Was jetzt nicht heissen soll das Du eine Kodierung angeben solltest — lass die Zeichen einfach Zeichen bleiben.

*Weder* `QTextEdit`- *noch* `QLineEdit`-Objekte haben eine `trimmed()`-Methode…
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Das mit den Widgets. Das heißt, man muss da eine Etage höher, und abstrakter arbeiten, damit am Ende auch alle Widgets entsprechend in einer For-Schleife behandelt werden können? Das heißt dann für mich, diese Idee erst einmal beiseite zu legen, und mich andere Gebiete innerhalb des ToolKits anschauen?

Zu deiner gespannten Frage: Also, wie ich break interpretieren würde? Ich sehe das eher wie ein GoTo unter VB6. In meinem Fall wollte ich einfach so vorgehen, dass nach dem ersten Fehler, der gefunden wurde, die For-Schleife unterbrochen wird und nicht bis zum Ende durch gezogen wird. Sonst bekommt der Anwender ja zig Msg-Box-Fenster und klickt sich die Finger wund oder die Maus kaputt :-) Sobald also ein Element in der Liste gefunden wurde, welches leer ist, soll die For-Schleife mit einem break abgebrochen werden. Aber wie ich mir denke, habe ich total falsch verstanden? :-)
BlackJack

@Sophus: Das ``break`` in Deiner letzten Schleife wird bedingungslos am Ende des Schleifenkörpers aufgerufen. Damit läuft diese Schleife nur einmal für das erste Element und niemals für das zweite.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Das heißt nun was? Der Break muss also innerhalb des Schleifenkörpers in den else-Block? Etwa so?

Code: Alles auswählen

            if field.trimmed():
                print "No error"
            else:
                self.show_msgbox_error()
                break
So liegt der Break nicht in der For-Schleife, sondern im else-Block. Damit wird nur dann abgebrochen, wenn ein Fehler vorliegt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

So Leute, ich hole mir mal von euch den Gnadenschuss ab :-)

Hier erst einmal der Code.

Code: Alles auswählen

    def check_inputs(self):
        editFields = [self.ui_pp_feedback.textEditFeedback]

       lineFields = [self.ui_pp_feedback.lineEditSubject]

        print editFields, lineFields # [<PyQt4.QtGui.QTextEdit object at 0x0292D300>] [<PyQt4.QtGui.QLineEdit object at 0x0292D390>]

        for editField in editFields:
            self.var_editfield = editField.toPlainText()
            print self.var_editfield

        for lineField in lineFields:
            self.var_linefield = lineField.text()
            print self.var_linefield

        if not self.var_linefield.trimmed():
            self.show_msgbox_error()
        else:
            if not self.var_editfield.trimmed():
                self.show_msgbox_error()
            else:
                print "Do Something"
Was habe ich getan bzw was hatte ich vor?
Also, in Zeile 2 und 4 habe ich die beiden unterschiedlichen Widgets in unterschiedlichen Listen hinzugefügt. Denn die beiden haben unterschiedliche APIs bzw. unterschiedliche Methoden. Damit ich mir sicher sein kann, dass es sich hierbei tatsächlich um Widgets handelt und nicht etwa um Zeichenketten, die da in den Listen sind. Anhand der Ausgabe sehe ich also, dass es sich hierbei um Widgets handelt. Alles super. Nächster Schritt. Von Zeile 8 bis 14 habe ich zwei For-Schleifen gebaut - einmal für QTextEdit und einmal für QLineEdit. In diesen beiden Schleifen lasse ich einmal in Zeile 9 und 19 die Zeichenketten aus den Widgets geben und speichere sie in den Attributen (self.var_editfield und self.var_linefield). Warum ich hier self() verwende soll in Zeile 16 geklärt sein. Ich will hier auf Attribute zugreifen, die eigentlich in den beiden For-Schleifen sind. In Zeile 16 bis 22 baue ich eine If-Abfrage und benutze hier die trimmed-Methode. Und hier werde ich mir meinen Gnadenschuss holen. Ich weiss, dass in der QT-Dokumenttation folgendes steht:
Returns a string that has whitespace removed from the start and the end.
Jedoch gehe ich erst einmal davon aus, dass es funktioniert :-) Das dies natürlich keine gute, nicht mal annähernd eine zufriedenstellende Lösung ist, kann ich mir fast denken. Daher bin ich gespannt auf eure Reaktion :-)
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: ich glaube, Du hast das eigentlichen Sinn dieser ganzen Diskussion noch nicht verinnerlicht. Du hast Text- und Edit-Felder und möchtest Tests damit machen. Ein Programmierer überlegt sich, was denn die eigentliche Aufgabe ist, die man mit jedem dieser Felder machen muß und schreibt sich dafür eine Funktion, um diese Arbeit nur einmal machen zu müssen. Dann eine Schleife, um den eigentlichen Funktionsaufruf nur einmal machen zu müssen und erkennt dann, dass es am Sinnvollsten ist, alle Editfelder schon von Anfang an in einer Liste zu speichern um nicht in jeder Funktion, die mit Editfeldern arbeitet, diese wieder ändern zu müssen, falls ein Feld hinzukommt.

In Deinem Fall zum Input-Checken brauchen die Editfelder und die Text-Felder noch einen Wrapper der für die Funktionen "gib-mir-gestrippten-Unicode-String" und "setze die Hintergrundfarbe" eine einheitliche API bietet.

Probleme bei deinem letzten Code: Du benutzt zwar for-Schleifen, überschreibst aber die Variablen var_editfeld und var_linefield immer wieder, so dass nur der Inhalt des jeweils letzten Feldes übrig bleibt. Die if-Kaskade danach wollten wir dir schon ganz am Anfang ausreden. Auch würde es mich überraschen, wenn eine Funktion, die check_inputs heißt, Attribute setzt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Es war ja auch nur ein Versuch. Mit diesem Code will ich niemanden beeindrucken :-) Anscheinend bin ich noch von dieser Thematik weit entfernt. Es ist nur so, dass in VB6 das Überprüfen der Textfelder ein "leichtes" wär, und hier sich wie eine monströse Aufgabe für mich herausstellt. Hättest du denn ein Pseudo-Code, damit ich in meinem Denkanstoß weiterkomme? Ich will keine lauffähige Codes, zumindest nicht in diesem Fall.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: In VB6 hast Du schonmal ein Problem, alle Textfelder in eine Collection zu packen. Hast Du nicht nur Textboxen sondern auch noch andere Elemente, läufst Du wieder in die selbe Situation, dass Du für jeden Typ einen eigenen Check brauchst. In Python kannst Du das wenigstens so schön kapseln, dass Du beim Einfügen eines weiteren Widgets nicht Code an 20 Stellen ändern mußt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

So wirklich rausrücken will hier nicht so niemand :-) Eher redet bzw. schreibt man viel drum herum, aber zum Punkt komme ich immer noch nicht. Ich habe immer noch das Gefühl im Kreis zu drehen. Oder die Antworten sind mit Absicht so dermaßen abstrakt gehalten.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sophus hat geschrieben:Jedoch verhält er sich nicht so wie gewünscht. Wenn ich die Textfelder als mit Leerzeichen fülle und dann auf den Button klicke, dann kommt eher die print-Meldung, dass "no error" vorliegen. Wie es aussieht, ist die einfache Schreibweise nicht immer die beste :-)
Einfach mal einen Schritt weiter denken ;-) Es geht nicht um die strip-Methode, sondern um den Test auf einen leeren String.
Sophus hat geschrieben:So wirklich rausrücken will hier nicht so niemand :-) Eher redet bzw. schreibt man viel drum herum, aber zum Punkt komme ich immer noch nicht. Ich habe immer noch das Gefühl im Kreis zu drehen. Oder die Antworten sind mit Absicht so dermaßen abstrakt gehalten.
Du hast du schon jede Menge deutliche Hinweise bekommen:
EyDu hat geschrieben:Indem du nicht die Strings in eine Liste packst, sondern die Widgets. Dann kannst du auch direkt auf die zugreifen. Eventuell musst du die Schnittstelle zu den Widgets noch ein wenig abstrahieren, damit du für verschiedenen Widgets dieselbe Schnittstelle hast.
BlackJack hat geschrieben:Wenn man (tatsächlich) über die Widget-Objekte iteriert hat man allerdings das Problem das die nicht die gleiche API haben um den Text abzufragen. Und um das sauber objektorientiert zu lösen braucht es IMHO ein bisschen mehr Erfahrung mit den Grundlagen als Du momentan hast. Gefühlt rätsts Du Dich eher durch den Code, die Datentypen, und die API, als das Du tatsächlich verstehst was die einzelnen Teilausdrücke für Werte, Typen, und damit Operationen haben.
Sirius3 hat geschrieben:In Deinem Fall zum Input-Checken brauchen die Editfelder und die Text-Felder noch einen Wrapper der für die Funktionen "gib-mir-gestrippten-Unicode-String" und "setze die Hintergrundfarbe" eine einheitliche API bietet.
Wenn du die Hinweise nicht verstehst, solltest du vielleicht wirklich erstmal die Finger von GUIs lassen und dich mit OOP-Grundlagen beschäftigen.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Wenn du die Hinweise nicht verstehst, solltest du vielleicht wirklich erstmal die Finger von GUIs lassen und dich mit OOP-Grundlagen beschäftigen.
Da musste ich doch schmunzeln :-) Also setze ich mich wieder mit dem Python Shell hin, und schreibe wieder redundante Funktionen, Klassen... alles für die Fingerübungen, die ich bereits getan habe :-) Ich will ja nicht sagen, dass ich alles in Python beherrsche, oder sie annähernd professionell beherrsche, aber ich denke man muss nicht ewig mit der Python Shell arbeiten. Man sucht sich eben ein Gebiet (GUI) und versucht sich daran :-) Ich finde diese Rückweisung eher demütigend. Als würde ein Mathe-Professor sagen "Geh in die erste Klasse zurück", nur weil ich Probleme mit den Vektorrechnungen habe. :-)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sophus hat geschrieben:Man sucht sich eben ein Gebiet (GUI) und versucht sich daran :-)
Und genau das ist das Problem: GUIs sind OOP-lastig. Sind sind geradezu *das* Standard-OOP-Szenario. Wenn du mit GUIs arbeitest, dann musst du erst OOP lernen. Anschließend noch ereignisorientierte Programmierung, aber das ist ein anderes Thema. Und erst dann kannst du vernünftig mit Oberflächen umgehen.

BlackJack hat es weiter oben schon ganz gut auf den Punkt gebracht: Du rätst dich eher durch den Code, als dass du systematisch vorgehen würdest. Es ist mehr als offensichtlich, dass du nicht richtig verstehst was du da machst. Hättest du die einfachsten OOP-Grundlagen drauf, dann müsstest du gar nicht nachfragen wie man eine gemeinsame Schnittstelle für Objekte erstellt. Das ist simpelstes Basiswissen: Quasi 1. OOP-Vorlesung nach 30 Minuten. Na gut, vielleicht auch 60.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

@Sophus: Dieser Thread driftet stark in die gleiche Richtung wie im Juni letzten Jahres, vielleicht erinnerst du dich. Da versuchten bereits mehrere Leute, die wissen wovon sie reden, dir den essentiellen Charakter von OOP darzulegen, letzten Endes recht erfolglos.

Wieso ist Code, ohne GUI in der Shell geschrieben, automatisch redundant? Das sind doch keine bloßen Fingerübungen, du kannst deine ganze Anwendung auch auf Kommandozeile funktionieren lassen, musst also nicht zwingend eine GUI verwenden um OOP zu verinnerlichen. Ratsam wäre das sowieso nicht, aber das haben schlauere Leute als ich bereits mehrfach illustriert.

Ignoriere doch mal alle Frustration über GUI und OOP und überlege dir, wie lange du dich mit diesen Problemchen herumschlagen willst. OOP ist die Grundvoraussetzung für moderne GUI-Entwicklung, da führt kein Weg dran vorbei! Wenn du ersteres besser beherrschst, erübrigen sich viele deiner Fragen hier quasi von selbst. Die dadurch freigewordene Zeit lässt sich ganz bestimmt besser investieren. ;-)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Sophus: Ich weiß gar nicht recht, wie in anfangen soll, dir zu helfen. Vielleicht so:

Wenn Begriffe auftauchen wie zB. List Comprehension, dann haben diese idR. eine genaue und fest begrenzte Bedeutung. Das hier ist eine List Comprehension:

Code: Alles auswählen

>>> [x * 3 for x in range(5)]
[0, 3, 6, 9, 12]
Das hier nicht:

Code: Alles auswählen

>>> ['hallo', 'welt']
['hallo', 'welt']
Im ersten Fall wird die Operation x + 3 auf jedes Element x einer Reihe von Werten angewandt, im zweiten Fall stehe Werte explizit im Source Code hintereinander und keine Operation wird darauf angewendet. Damit etwas eine List Comprehension ist, muss es nicht nur in eckigen Klammern stehen, sondern es muss zudem auch eine spezifische grammatische Struktur besitzen, nämlich diese: ... for ... in ..., mit einem optional folgenden if ... . An den ... Stellen darf nicht beliebiges stehen. Hier ein paar ungültige Varianten:

Code: Alles auswählen

[123 + 2 for 123 in range(10)]
Zwischen for und in muss ein sog. L-Value stehen. Letzteres ist ein in vielen Sprachen auftauchendes Konzept, es bezeichnet solche sprachlichen Konstrukte, die links von einem Zuweisungszeichen stehen dürfen. 123 ist kein L-Value wie man hier sehen kann:

Code: Alles auswählen

>>> 123 = 456
  File "<stdin>", line 1
SyntaxError: can't assign to literal
Auch das hier ist keine gültige List Comprehension:

Code: Alles auswählen

[x *3 for x in 123]
Weil man über 123 nicht iterieren kann.

Desweiteren fehlen die Klammern in self.ui_pp_feedback.textEditFeedback.toPlainText, damit die Funktion ausgeführt wird. So, wie der Ausdruck dasteht, benennt er einfach nur ein Ding, welches hier eine Funktion ist. Um das zu verdeutlichen:

Code: Alles auswählen

>>> def foo():
...     return 123
...
>>> foo
<function foo at 0xb6eda6a4>
>>> foo()
123
Ich habe dir recht früh schon geschrieben, dass die beste Variante, Strings auf "Leerheit" zu testen diese hier ist:

Code: Alles auswählen

if some_string:
    irgendwas()
Du dagegen ersetzt die sehr komplizierte Variante durch eine weniger, aber immer noch zu komplizierte Variante. Ich hatte sogar Yehova gesagt (d.i., ich hab mich auf PEP8 berufen). Wenn du die Ratschläge nicht annimmst, warum fragst du dann überhaupt? Und wenn du jetzt antworten willst "Weil die Vorschläge nicht funktioniert haben", dann solltest du dich ernsthaft fragen, was wahrscheinlicher ist, dass einige Berufsprogrammierer mit zT. mehr als zwanzig Jahren Berufserfahrung dir etwas falsches sagen, oder dass du etwas nicht richtig verstanden hast?

Auch bringst du immer so seltsame Dinge an, die keiner mehr ernsthaft machen würde, wie zB. Ungarische Notation. Du schriebst, du würdest das wegen der Leserlichkeit tun. Ich kann nichts leserliches an ui_pp_feedback finden. Was soll denn ein pp sein? ui hat vermutlich irgendetwas mit dem User Interface zu tun, aber nachdem hier in QT-Programm geschrieben werden soll, muss man mir das irgendwie nicht noch zusätzlich sagen. Oder findest du, ein Auto wird dadurch besser bedienbar, wenn "Auto" draufsteht? Ich empfehle wirklich, den Aufsatz von Charles Simonyi zu lesen. Dann wirst du sehen, dass du - wie die meisten, die Ungarische Notation zu verwenden glauben -, etwas fundamental falsch verstanden hast. Und auch den von mir verlinkten Spott-Artikel solltest du lesen, darin werden die Probleme der UN deutlich gemacht.

Wenn wir hier von Idiomen, Pattern und Anti-Pattern schreiben, dann meinen wir nicht: "Du musst diese Dinge tun/bleiben lassen, weil du sonst einen fremden Stallgeruch hast und wir dich dann nicht mehr mögen". Was wir meinen ist: "Es haben sich im Laufe der letzten gut zwanzig Jahre seit es Python gibt bestimmte Konstruktionen als in irgendeiner Fom nützlich erwiesen. Wenn du dir dein Leben einfach machen möchtest, verwende diese Konstruktionen." Ich wittere hier ja das Problem des soziologischen Standpunkts. Dieser ist, dass man glaubt, alles aus dem Gruppenverhalten erklären zu können und es folglich daneben keine andere Erklärung geben kann. Etwas ähliches findet man bei Kaufleuten. Wenn man versucht, denen zu erklären, das das, was sie gebaut haben möchten, nicht programmniert werden kann, da der Algorithmus selbst dann länger laufen würde, als das Universum noch leben wird, wenn man alle auf der Welt existierenden Computer gleichzeitig verwenden könnte. Sie hören dann nur "Dieser Programmierer bringt als Verhandlungsmasse ein Techno-Geblubber, damit er irgendwelche Vorteile (mehr Teammitglieder, mehr Zeit, ...) rausschlagen kann." Der kaufmännische Standpunkt ist dabei, dass alles Verhandlungssache ist, weil das Ziel des Kaufmanns der Abschluss eines Vertrages ist, und den muss man eben aushandeln. Programmieren geht aber so nicht.

Übrigens gibt es ähnliches auch bei vielen Programmierern. Programmieren ist die Anwendung der Wissenschaft der Prozesse im allgemeinsten, abstraktesten Sinn von "Prozess". Viele Programmierer können alles nur in diesem Bezugsrahmen interpretieren. Wenn sie vor ein soziales Problem gestellt werden, suchen sie zuerst mal eine technische Lösung dafür. Was natürlich fast nie gelingt. git oder mercurial werden zB. nicht helfen, wenn alle Dateien permanent von allen gleichzeitig bearbeiten, weil dann das Mergen mehr Zeit beansprucht, als das VCS einem einspart.

Im übrigen gilt für die Idiome Pythons, dass die wichtigen nicht nur in PEP8 stehen, sondern dort auch illustriert und begründet werden. Würdest du PEP8 mal intensiv durchgehen, würdest du von uns nicht erwarten, dir dieselben Erklärungen hier wieder hinzuschreiben, die dort schon stehen. Wievielen Python-Beginnern meinst du, sollte man das Tutorial, die Dokumentation oder PEP8 hier einzeln schriftlich vorlesen? Weil doch Sekundärliteratur immer besser ist, als das Original? Oder weil nur der ein geeigneter Helfer sein kann, der es dadurch beweist, dass er gut Texte abschreiben kann?

Ein break ist kein goto, sonst hieße er goto. Beim Programmieren muss man genau (ja, spießig) sein, denn Laxheit funktioniert nicht. Wenn ich dem Computer nur so ungefähr sage, was er tun soll, dann wird er auch nur so ungefähr tun, was ich will. Außerdem ist ein break immer in einer Schleife, alles andere wäre ein Syntaxfehler:
Sophus hat geschrieben:@BlackJack: Das heißt nun was? Der break muss also innerhalb des Schleifenkörpers in den else-Block? Etwa so?

Code: Alles auswählen

            if field.trimmed():
                print "No error"
            else:
                self.show_msgbox_error()
                break
So liegt der Break nicht in der For-Schleife, sondern im else-Block. Damit wird nur dann abgebrochen, wenn ein Fehler vorliegt.
Wenn du in einem Auto sitzt und dir, sagen wir eine Euromünze in die Hosentasche steckst, ist die Münze dann nicht mehr im Auto?
Sophus hat geschrieben:

Code: Alles auswählen

editFields = [self.ui_pp_feedback.textEditFeedback]
for editField in editFields:
    self.var_editfield = editField.toPlainText()
    print self.var_editfield
Das ist die kompliziertese Weise, die ich je gesehen habe, um das hier zu schreiben:

Code: Alles auswählen

self.var_editfield = self.ui_pp_feedback.textEditFeedback.toPlainText()
print self.var_editfield
Kannst du erklären, warum du eine ein-elementige Liste erzeugst, die du für nichts anderes verwendest, als dafür, das eine Element in einer for-Schleife (die genau einmal durchlaufen wird) auszupacken und dann auf dem vorher eingepackten Element zu operieren? Welchen Vorteil haben dabei die Liste und die Schleife?

Wie würde ich das Problem der Input-Validierung lösen? Vermutlich mit dem Model-View-Controller-Pattern, also zB. so: http://www.python-forum.de/pastebin.php?mode=view&s=421. Das bitte nur als Illustration eines Konzepts betrachten. GUI-Frameworks haben solche Mechanismen idR. bereits eingebaut, so dass man sie gleich verwenden kann. In tkinter gibt es zB. Variablen-Klassen wie StringVar, bei denen man Observer (also Views) mittels trace() registrieren kann. Ich habe es nur ausprogrammiert, damit man leichter sehen kann, was wie zusammenhängt.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

EyDu hat geschrieben:
Sophus hat geschrieben:

Code: Alles auswählen

for i in range(len(editFields)):
Warum hast du deinen Code jetzt so verunstaltet? Bei deinen vielen Beiträgen wurdest du bestimmt schon darauf hingewiesen, dass das ein absolutes Antipattern ist.
Ja, sogar schon durch Hinweis auf einen zuvor gegebenen Ratschlag :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Sophus hat geschrieben:So wirklich rausrücken will hier nicht so niemand :-) Eher redet bzw. schreibt man viel drum herum, aber zum Punkt komme ich immer noch nicht. Ich habe immer noch das Gefühl im Kreis zu drehen. Oder die Antworten sind mit Absicht so dermaßen abstrakt gehalten.
Ein ganz konkretes Stück Code, Version 2: http://www.python-forum.de/pastebin.php?mode=view&s=422

Models repräsentieren (und kapseln) Werte. Views beobachten Models. Wenn eine View ein Model beobachten soll, dann registriert man es beim dem Model, entweder mittels model.subscribe(view), oder indem man es der __init__() Methode des Models mitgibt. Bei jedem Model können sich beliebig viele Views regsitrieren.

Um den Wert eines Models zu setzen verwendet mann dessen set(value) Methode. Jedes Model besitzt eine - per default leere - Vailierungs-Methode. Diese wird in set(value) aufgerufen, bevor der Wert gespeichert wird. Ist die Validierung erfolgreich, wird der neue Wert gespeichert und alle registrierten Views werden mittels view.on_success(value) darüber benachrichtigt. Scheitert die Validierung, wird die Änderung verworfen und alle Views werden darüber mittels view.on_failure(error) benachrichtigt.

Im konkreten Fall gibt es mehrere Views: BGColorSetter setzen die die Hintergrundfarbe eines Entry-Widgets. Im Erfolgsfall wird der Hintergrund weiß gesetzt, Fehlerfall rot. ValidationState beobachtet den Erfolg der Validierungen aller Models. ValidationState ist nicht nur eine View (also ein Beobachter von Änderungen), sondern auch selbt ein Model. Es repräsentiert den Erfolg aller Validierungen.

Die Funktion save() wird aufgerufen, wenn der Save-Button derückt wird. Zunächst wird dort der ValidationState auf True gesetzt. Dann werden alle Models mit den Werten der Eingaben aktualisiert. Jede Aktualisierung hat entweder Erfolg oder scheitert, je nachdem, was die entsprechende Validierung ergibt. Nachdem alle Aktualisierungsversuche durchgeführt wurden, können wir testen, ob alle Validierungen erfolgreich waren, indem wir den Wert des ValidationStates abfragen. Ist dessen Wert immer noch True, können wir die Models speichern und das Fenster schließen. Ist der Wert jedoch False, informieren wir den Benutzer mit einer Message Box und schließen das Fenster nicht, sodass er seine Eingaben korrigieren kann. Die zu korrigierenden Textfelder sind dann bereits rot markiert, weil ja die BGColorSetter ebenfalls benachrichtigt wurden und die Hintergrundfarbe entsprechend gesetzt haben.

Die Vorteile einer solchen Mikroarchitektur sind vielleicht nicht gleich ersichtlich. Sie liegen in der Trennung der verschiedenen Programmteile. Die Benutzeroberfläche muss nichts über Validierungen oder konkrete Werte wissen. Alles was sie wissen muss, sind Benutzeroberflächendinge. Die Models brauchen nichts über die Benutzerüberfläche zu wissen. Die Validierung wird direkt auf einem Wert ausgeführt, ohne dass das Model wissen muss, dass er aus einem tkinter.Entry Feld kommt. Man könnte sogar mit wenig Aufwand tkinter durch Qt ersetzen, und viele Teile des Programms könnten bleiben, wie sie sind. Und die Teile, die geändert werden müssen, schreien einen geradezu an: "ich bin tkinter-spezifisch". Oder man könnte Tooltips/Balloons erzeugen, die aufpoppen, wenn sich der Mauszeiger über einem Entry-Feld mit feherhafter Eingabe befindet, und so die Ursache des Fehlers ausgeben. In einem MVC-System ist das trivial zu lösen, man braucht nur - analog zu BGColorSetter - eine neue View registrieren, die einen Balloon erzeugt. Dazu sind nur diese Änderungen nötig:

In tkinter gibt es keine Balloons, aber in tkinter.tix:

Code: Alles auswählen

import tkinter.tk as tk
das hier:

Code: Alles auswählen

import tkinter.tix as tk
Diese neue View-Klasse:

Code: Alles auswählen

class BalloonSetter(View):

    def __init__(self, root, entry):
        self.entry = entry
        self.balloon = tk.Balloon(root, initwait=500, state='balloon')

    def on_success(self, value):
        self.balloon.unbind_widget(self.entry)

    def on_failure(self, error):
        self.balloon.bind_widget(self.entry, msg=str(error))
Und statt:

Code: Alles auswählen

        name_entry: Name(BGColorSetter(name_entry), is_valid),
        age_entry: Age(BGColorSetter(age_entry), is_valid),
das hier:

Code: Alles auswählen

        name_entry: Name(BGColorSetter(name_entry), BalloonSetter(window, name_entry), is_valid),
        age_entry: Age(BGColorSetter(age_entry), BalloonSetter(window, age_entry), is_valid),
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@pillmuncher: Danke für dein Beispiel. Ich werde deinen Code als Lektüre heranziehen, und versuchen im einzelnen zu verstehen.
Antworten