Problem beim Erstellen eines Menüs

Plattformunabhängige GUIs mit wxWidgets.
Antworten
sebastian06
User
Beiträge: 19
Registriert: Mittwoch 16. Dezember 2009, 11:05

Hallo,

ich versuche gerade meinen Code zur Generierung des GUI etwas "übersichtlicher zu gestallten (die Anregung dazu habe ich aus WxPython in Action), aber bekomme immer folgenden Fehler:

"Unhandled Value Error - too many values to unpack"

Das Problem tritt in Zeile 58 auf, aber ich komme nicht dahinter, was falsch ist. Hier ein Teil des Programms:

Code: Alles auswählen

def menuData(self):
        return (("File",
                    ("Save As...", "", self.OnSaveAs, wx.ITEM_NORMAL),
                    ("", "", ""),
                    ("Quit", "", self.OnQuit, wx.ITEM_NORMAL)),
                ("Dyes", 
                    ("Load Dye Absorption", "", self.OnLoadAbs, wx.ITEM_NORMAL),
                    ("Load Dye Emission", "", self.OnLoadFlu, wx.ITEM_NORMAL),
                    ("", "", ""),
                    ("Delete last Absorption", "", self.OnDelAbs, wx.ITEM_NORMAL),
                    ("Delete last Emission", "", self.OnDelAbs, wx.ITEM_NORMAL)),
                ("Filter", 
                    ("Load Excitation Filter", "", self.OnLoadEx, wx.ITEM_NORMAL),
                    ("Load Dichroic Mirror", "", self.OnLoadDi, wx.ITEM_NORMAL),
                    ("Load Emission Filter", "", self.OnLoadEm, wx.ITEM_NORMAL),
                    ("", "", ""),
                    ("Delete last Excitation Filter", "", self.OnDelEx, wx.ITEM_NORMAL),
                    ("Delete last Dichroic Mirror", "", self.OnDelDi, wx.ITEM_NORMAL),
                    ("Delete Last Emission Filter", "", self.OnDelEm, wx.ITEM_NORMAL)),
                ("Light Source", 
                    ("Load Light Source", "", self.OnLoadLs, wx.ITEM_NORMAL),
                    ("", "", ""),
                    ("Delete last Light Source", "", self.OnDelLs, wx.ITEM_NORMAL)),
                ("Laser", 
                    ("405nm", "", self.OnShow405, wx.ITEM_CHECK),
                    ("445nm", "", self.OnShow445, wx.ITEM_CHECK),
                    ("473nm", "", self.OnShow473, wx.ITEM_CHECK),
                    ("488nm", "", self.OnShow488, wx.ITEM_CHECK),
                    ("491nm", "", self.OnShow491, wx.ITEM_CHECK),
                    ("515nm", "", self.OnShow515, wx.ITEM_CHECK),
                    ("532nm", "", self.OnShow532, wx.ITEM_CHECK),
                    ("561nm", "", self.OnShow561, wx.ITEM_CHECK),
                    ("594nm", "", self.OnShow594, wx.ITEM_CHECK),
                    ("640nm", "", self.OnShow640, wx.ITEM_CHECK),
                    ("", "", ""),
                    ("H2O Raman Lines 405nm", "", self.OnShowRaman405, wx.ITEM_CHECK),
                    ("H2O Raman Lines 445nm", "", self.OnShowRaman445, wx.ITEM_CHECK),
                    ("H2O Raman Lines 473nm", "", self.OnShowRaman473, wx.ITEM_CHECK),
                    ("H2O Raman Lines 488nm", "", self.OnShowRaman488, wx.ITEM_CHECK),
                    ("H2O Raman Lines 491nm", "", self.OnShowRaman491, wx.ITEM_CHECK),
                    ("H2O Raman Lines 515nm", "", self.OnShowRaman515, wx.ITEM_CHECK),
                    ("H2O Raman Lines 532nm", "", self.OnShowRaman532, wx.ITEM_CHECK),
                    ("H2O Raman Lines 561nm", "", self.OnShowRaman561, wx.ITEM_CHECK),
                    ("H2O Raman Lines 594nm", "", self.OnShowRaman594, wx.ITEM_CHECK),
                    ("H2O Raman Lines 640nm", "", self.OnShowRaman640, wx.ITEM_CHECK)))

    def createMenuBar(self):
        menuBar = wx.MenuBar()
        for eachMenuData in self.menuData():
            menuLabel = eachMenuData[0]
            menuItems = eachMenuData[1]
            menuBar.Append(self.createMenu(menuItems), menuLabel)
        self.SetMenuBar(menuBar)

    def createMenu(self, menuData):
        menu = wx.Menu()
        for eachLabel, eachStatus, eachHandler,  eachKind in menuData:
            if not eachLabel:
                menu.AppendSeparator()
                continue
            menuItem = menu.Append(-1, eachLabel, eachStatus,  eachKind)
            self.Bind(wx.EVT_MENU, eachHandler, menuItem)
        return menu
Danke für Eure Hilfe,

Sebi
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Hallo auch

schonmal in Zeile 5 (sowie: 10, 17, 23, ... ) geschaut? Dort sind Deine Tupel lediglich 3 Elemente groß, anstelle der erwarteten 4.

Noch ein Tipp: benutze einen Debugger!!!!! Hilft manchmal ungemein und mann sehr leicht Fehler dieser Natur selber aufdecken und mit einem Erfolgsgefühl verzeichnen.

€dit:
und als weiteren Tipp: Um Separatoren zu beschreiben, würde ich einfach das Tupel leer lassen und in der Schleife nicht schon die einzelnen Komponenten zuweisen, sondern erst auf die Länge prüfen ;). Ist die Länge eines Tupels == 0 hast Du einen Separator.

Code: Alles auswählen

...
for element in menuData:
    if len(element) == 0:
       menu.AppendSeparator()
...
Dann könntest Du auch dort weiter anknüpfen und die Länge auf 4 prüfen:

Code: Alles auswählen

...
    else:
        assert len(element) == 4, "Invalid menu sub-element configuration: %s" % str(element)
        eachLabel, eachStatus, eachHandler,  eachKind = element
        # und hier dann ganz normal weiter im Programm
...
>>Masaru<<
sebastian06
User
Beiträge: 19
Registriert: Mittwoch 16. Dezember 2009, 11:05

Hallo Masaru,

danke für die Tipps. Das mit der Länge 3 stimmt, das habe ich übersehen. Was wohl daran liegt, das die Fehlermeldung schon vorher auftritt. Ich benutze übrigens einen Debugger (ERIC).

Code: Alles auswählen

...
def menuData(self):
        return (("File",
                    ("Save As...", "", self.OnSaveAs, wx.ITEM_NORMAL),
                    (), 
                    ("Quit", "", self.OnQuit, wx.ITEM_NORMAL)),
                ("Dyes", 
                    ("Load Dye Absorption", "", self.OnLoadAbs, wx.ITEM_NORMAL),
...
...

Code: Alles auswählen

def createMenu(self, menuData):
        menu = wx.Menu()
        for element in menuData:
            if len(element) == 0:
                menu.AppendSeparator()
            else:
                assert len(element) == 4, "Invalid menu sub-element configuration: %s" % str(element)
                eachLabel, eachStatus, eachHandler,  eachKind = element 
            menuItem = menu.Append(-1, eachLabel, eachStatus,  eachKind)
            self.Bind(wx.EVT_MENU, eachHandler, menuItem)
        return menu
...
Dann bekomme ich als Ausgabe:

unhandled AssertionError
"Invalid menu sub-element configuration: Save As..."

Aber was stimmt denn mit diesem Menü-Eintrag nicht? Die Länge ist doch gleich 4, oder übersehe ich wieder etwas.

Danke,

Sebi
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Benutz doch mal Deinen "Debugger" in ERIC und schau Dir an was sich genau hinter menuData verbirgt.

Eigentlich sagt es die Assertion-Fehlermeldung fast schon aus ... naja, vielleicht auf den ersten Blick nicht für jederman zu erkennen ... deswegen schau's Dir im Debugger an, dann solltest Du sehen wo der Fehler liegt.
Setz einfach mal nen Breakbpoint gleich zu beginn Deiner createMenu(self, menuData) Methode ...

>>Masaru<<
sebastian06
User
Beiträge: 19
Registriert: Mittwoch 16. Dezember 2009, 11:05

Servus Masaru,

ich habe Deinen Rat befolgt. Die Fehlermeldung wird ausgegeben, weil element in diesen Fall die Länge 10 hat.
Dann verstehe ich aber noch nicht ganz, was Du mit Deinem Code tun willst

Code: Alles auswählen

...
    def createMenu(self,  menuData):
        menu = wx.Menu()
        for element in menuData:
            if len(element) == 0:
                menu.AppendSeparator()
            else:
                assert len(element) == 4, "Invalid menu sub-element configuration: %s" % str(element)
                eachLabel, eachStatus, eachHandler,  eachKind = element 
            menuItem = menu.Append(-1, eachLabel, eachStatus,  eachKind)
            self.Bind(wx.EVT_MENU, eachHandler, menuItem)
        return menu
...
Ich dachte, der Sinn Deiner Zeilen ist es, zu prüfen, ob die Länge des Tuples = 4 ist, nicht die des Strings.
menuData hat genau 4 Einträge (String, String, InstanceMethod, Integer - laut Debugger).

Grüße,

Sebi
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Tjoa ... dann war woh mein Beispiel falsch. Anstelle auf die einzelnen Elemente des menuData die Längenprüfung anzuwenden, müsste es gleich zu beginn der Methode auf menuData selbst angewendet werden.

Sprich:
- hat menuData keine Elemente => dann ist es ein Separator
- hat menuData ungleich 4 Elemente => dann ist irgendwas falsch (z.B. wieder Prüfung/Zusicherung via Assertion)
- anaonsten kann menuData dazu verwendet werden ein Menu mit den 4 Elementen zu erzeugen

Code: Alles auswählen

... 

# aaaach, den Spass will ich Dir nicht nehmen es selber anzupassen
...
>>Masaru<<
sebastian06
User
Beiträge: 19
Registriert: Mittwoch 16. Dezember 2009, 11:05

Servus Masaru,

ich habe noch etwas geändert, aber jetzt passt es.

Code: Alles auswählen

...
def createMenu(self, menuData):
        menu = wx.Menu()
        for eachLabel, eachStatus, eachHandler,  eachKind in menuData:
            if not eachLabel:
                menu.AppendSeparator()
                continue
            menuItem = menu.Append(-1, eachLabel, eachStatus,  eachKind)
            self.Bind(wx.EVT_MENU, eachHandler, menuItem)
        return menu
...
Nochmal Danke für dier Tipps!

Sebi
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Namen wie eachIrgendwas sind extrem anstrengend zu lesen und sollten deshalb in Python vermieden werden. Siehe PEP8.
sebastian06
User
Beiträge: 19
Registriert: Mittwoch 16. Dezember 2009, 11:05

Danke für den Tipp. Da habe ich nicht gewusst.

Grüße,

Sebi
Antworten