Frame wird nicht angezeigt

Fragen zu Tkinter.
Antworten
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

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! :twisted:
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!
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi sedi

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)
Gruss wuf :wink:
Take it easy Mates!
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

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:

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                |
            | ===================== | 
            +-----------------------+
Es genügt daher bereits im Modul ``test.py`` die 63. Zeile anzupassen - es muss nur der Elternframe übergeben werden.

Also anstatt:

Code: Alles auswählen

[63] self.master.app.view.set_actual_frame(self, self.mnu)
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:

Code: Alles auswählen

self.master.app.view.set_actual_frame(self.master, self.mnu)
Super - Danke
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
BlackJack

``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“.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

@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 :? .
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
BlackJack

@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.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

@BlackJack: "Law of Demeter" das habe ich erst nachsehen müssen :oops: 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
BlackJack

@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.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

ok - das leuchtet schon ein @BlackJack - dann wird das Ganze mal **Redesigned** ...
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
Antworten