Hallo,
hoffe ihr findet den Fehler über den ich nun schon ein paar Stunden brüte. Haben in der Schule ein paar Pythonübungen gemacht und wollten daraus ein kleines Programm schreiben das alles zusammenfasst.
Was wir wollten:
Jeder Frame soll sich an der Instanz des Anwendungsfensters ``app.view`` ueber deren Methode ``set_actual_frame(frame, menubutton)`` zur Ansicht *anmelden*. Dort wird dann der vorherige Frame (gehalten in ``frm_aktuell``) mittels ``forget`` abgeschalten. Zudem wird auch der Menübutton, der den Aufruf ausgelöst hat, deaktiviert und in dem Attribut ``mnu_aktuell`` gespeichert.
Das Problem:
Im Menü Module befinden sich die Untermenüs für Trigonometrie und Brüche, mit jeweils einem Eintrag. Klickt man auf diesen, so öffnet sich entsprechend der Frame im Fenster. Nur beim Testframe ``TestViews`` der durch "Sonstiges>Tests>Test 1" gestartet wird, funktioniert es nicht. Die Art wie dieser implementiert wurde ist aber genau gleich zu den Frames für Brüche und Trigonometrie!
Um den Fehler zu finden habe ich den ``TestViews`` - Frame mal als ersten sichtbaren Frame gesetzt, wenn die Anwendung hochfährt. Er startet auch problemlos, scheint also prinzipiell lauffähig zu sein.
Hier findet ihr das Gesamtpaket Mathe-mitPython (mwp.zip)
Danke im Voraus!
Frame wird nicht angezeigt
Hi sedi
Habe dein Skript 'test.py' wie folgt modifiziert. Kannst du es einmal ausprobieren?:
Gruss wuf
Habe dein Skript 'test.py' wie folgt modifiziert. Kannst du es einmal ausprobieren?:
Code: Alles auswählen
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Modul test
==========
Diseses Modul ist fuer Tests von GUI-Elementen verwendbar.
"""
import tkinter as tk
from .helfer import HexEntry
import os.path
print(os.path.abspath(__file__))
class TestBaseView(tk.Frame):
"""
Jeder Test muss von dieser Klasse abgeleitet sein.
"""
def __init__(self, master, **kw):
tk.Frame.__init__(self, master=master, **kw)
self.mnu = None
self.lbl_header = tk.Label(self, bg="grey", fg="white")
self.lbl_header.pack(fill=tk.BOTH, expand=True)
def set_header(self, headline):
self.lbl_header.config(text=headline)
self.update()
def do_run(self):
raise NotImplementedError(
"Muss in abgeleiteter Klasse implementiert werden")
class HexEntryTestView(TestBaseView):
def __init__(self, master, **kw):
TestBaseView.__init__(self, master=master, **kw)
self.set_header("Teste das HexEntry aus dem Modul helfer")
# -> wuf
#self.mnu = self.master.menu.add_command(
#label="Test 1",
#command=self.do_run)
self._frm_testobjekt = HexEntry(self, width=15)
self._frm_testobjekt.pack(expand=True)
# -> wuf
#def do_run(self):
#print("{} [{}]".format(self.__class__.__name__,
#self.lbl_header.cget("text")))
#self.master.app.view.set_actual_frame(self, self.mnu)
class TestViews(tk.Frame):
"""Startet alle hier im Modul befindlichen Tests
"""
def __init__(self, app, master, menu, **kw):
tk.Frame.__init__(self, master=master, **kw)
self.app = app
self.menu = menu
self.actual_test = None
self.tests = []
# -> wuf
test_num = "Test-{}".format(len(self.tests))
self.tests.append(HexEntryTestView(self))
# -> wuf
self.menu.btn = self.menu.add_command(
label=test_num,
command=self.do_menu_click)
for test in self.tests:
test.pack(fill=tk.BOTH, expand=True)
# -> wuf
def do_menu_click(self):
self.app.view.set_actual_frame(frame=self, menubutton=self.menu.btn)
Take it easy Mates!
Genial @wuf - FUNZT!!!
Ich habe gestern wirklich STUNDEN damit zugebracht, diesen vermaledeiten Fehler zu finden.
Jetzt, da ich Deine Lösung sehe, scheint mir das Problem klar zu sein: Der Frame der sich beim Anwendungsfenster anmelden wollte liegt in ZWEITER Eben unter dem Anwendungsfensterframe - dazwishen liegt der Frame ``TestViews``. Hier mal ein Versuch das zu verdeutlichen:
Es genügt daher bereits im Modul ``test.py`` die 63. Zeile anzupassen - es muss nur der Elternframe übergeben werden.
Also anstatt:
muss der der Methode ``set_actual_frame`` als erstes Argument der Verweis auf den uebergeordneten Frame, also ``self.master`` mitgeliefert werden - und schon läufts:
Super - Danke
Ich habe gestern wirklich STUNDEN damit zugebracht, diesen vermaledeiten Fehler zu finden.
Jetzt, da ich Deine Lösung sehe, scheint mir das Problem klar zu sein: Der Frame der sich beim Anwendungsfenster anmelden wollte liegt in ZWEITER Eben unter dem Anwendungsfensterframe - dazwishen liegt der Frame ``TestViews``. Hier mal ein Versuch das zu verdeutlichen:
Code: Alles auswählen
+-----------------------+
| App-Fenster |
| ===================== |
| frm_aktuell |
| mnu_aktuell |
| ===================== |
| set_actual_frame() |<--------------+
+-----------------------+ |
|#>benachrichtigung der Fenster-Instanz:
+-----------------------+ | Diese sorgt für:
+---->| Modul-Fenster | | # (1) frm_aktuell.forget()
| | ===================== | | # (2) disable mnu_aktuell
| | mnu | | # (3) Neuen Frame anzeigen (!!!)
| +--<| views = [] | |
| | | ===================== | |
| | | do_menu_click() |>------+
| | +-----------------------+
| |
| |#>alle Views in die Liste
| |
| | +-----------------------+
| +------>| View (der Bereiche) |
| | ===================== |
+--------<| master |
| ===================== |
+-----------------------+
Also anstatt:
Code: Alles auswählen
[63] self.master.app.view.set_actual_frame(self, self.mnu)
Code: Alles auswählen
self.master.app.view.set_actual_frame(self.master, self.mnu)
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
``self.master.app.view.set_actual_frame(self.master, self.mnu)`` sieht so gar nicht gut aus. Über so viele Ebenen sollte ein Objekt keine Kenntnisse haben müssen. Dann kann man sich das ”entkoppeln” auch gleich sparen.
`actual` soll übrigens sehr wahrscheinlich `current` heissen. Denn „actual“ ist auf Deutsch *nicht* „aktuell“ sondern „tatsächlich“.
`actual` soll übrigens sehr wahrscheinlich `current` heissen. Denn „actual“ ist auf Deutsch *nicht* „aktuell“ sondern „tatsächlich“.
@BlackJack: Das ist aber doch Geschmackssache, ob das gut aussieht. Solange klar geregelt ist, welche Objekte Zugriffe erlauben und diese aus Sicht des momentan aktiven Objekts sichtbar sind, spricht doch nichts gegen einen solchen *Fernzugriff*. Lange Objektketten sind auch aus anderen Programmiersprachen bekannt - oder gibt es da bei Python irgend eine besondere Regel?
Das mit ``actual`` werde ich ändern - die Fremdsprachenkenntnisse sind somit wohl auch geklärt .
Das mit ``actual`` werde ich ändern - die Fremdsprachenkenntnisse sind somit wohl auch geklärt .
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
@sedi: Das ist keine Geschmackssache. Es gibt in Python keine besondere Regel: Das ist auch in anderen Programmiersprachen keine gute Idee weil das ganz grundsätzlich ein schlechter OOP-Entwurf ist wenn Objekte zu viel voneinander wissen müssen. Das macht Programme schwer nachvollziehbar und letztendlich ist damit am Ende alles mit allem auf eine Weise verbunden das man nichts ändern kann ohne das über mehrere Verbindungen weiter irgendwas kaputt gehen kann was davon eigentlich nicht betroffen sein sollte. Stichwort „Law of Demeter“. Da kann man natürlich auch gegen verstossen, aber das ist doch schon ein ziemlich krasser Fall.
Alleine das der erste Schritt über das Elternobjekt in der Widgethierarchie geht ist schon nicht gut. Abhängigkeiten sollten nicht in diese Richtung bestehen weil man dann Widgets nicht mehr frei einsetzen kann, sondern beide Widgets sich gegenseitig kennen müssen und damit zum Beispiel nicht mehr möglich ist das ”untere” Widget irgend wo anders einzusetzen, oder beispielsweise einen `Frame` oder etwas ähnliches dazwischen zu setzen um die Anordnung der Anzeigeelemente zu beeinflussen.
Alleine das der erste Schritt über das Elternobjekt in der Widgethierarchie geht ist schon nicht gut. Abhängigkeiten sollten nicht in diese Richtung bestehen weil man dann Widgets nicht mehr frei einsetzen kann, sondern beide Widgets sich gegenseitig kennen müssen und damit zum Beispiel nicht mehr möglich ist das ”untere” Widget irgend wo anders einzusetzen, oder beispielsweise einen `Frame` oder etwas ähnliches dazwischen zu setzen um die Anordnung der Anzeigeelemente zu beeinflussen.
@BlackJack: "Law of Demeter" das habe ich erst nachsehen müssen Wir hatten das mal als *shy code* oder *Kommunikation nur mit nächsten Nachbarn* kennengelernt. Geringe Abhängigkeiten => gut zu testen und zu pflegen: d'accord. Aber für eine Kleinstgruppe von Hobbyprogrammierern, die nur mal schnell eine GUI fuer gesammelten Code basteln wollen? Da wäre ne ganze Menge Wrapper nötig - ist das für dieses Szenario wirklich so ein großer Vorteil?
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
@sedi: Das hat nichts mit der Gruppengrösse zu tun. Das gilt für Code allgemein. Auch der Code von einem einzelnen Programmierer sollte leicht nachvollziebar und testbar sein, denn auch dieser einzelne Programmierer wird irgendwann mal auf Fehlersuche gehen oder den Code nach einem Jahr mal wieder verstehen müssen.
Wie gesagt ist ganz unabhängig davon alleine schon das Wissen über das `parent`-Objekt keine gute Idee und damit stellt sich auch die Frage ob da tatsächlich viele Wrapper nötig sind wenn man das grundsätzlich anders strukturieren würde.
Wie gesagt ist ganz unabhängig davon alleine schon das Wissen über das `parent`-Objekt keine gute Idee und damit stellt sich auch die Frage ob da tatsächlich viele Wrapper nötig sind wenn man das grundsätzlich anders strukturieren würde.