wxPython-Menu refactored - ein Beispiel

Code-Stücke können hier veröffentlicht werden.
Antworten
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

Wirklich mal nur ein Code-Schnipsel: Ich habe eine Stück Code ein wenig re-factored und zeige einfach mal, was dabei rausgekommen ist, bzgl. einer MenuBar. Meine Motivation ist es einfach mal etwas Ordnung in meinen Code zu bekommen und - ohne jemanden zu nahe treten zu wollen - ich denke anderen wx-Nutzern hier könnten vielleicht auch Interesse haben. ;-)

Code: Alles auswählen

def build_one_menu(parent,itemlist):
    """
        builds one menu
    """
    menu = wx.Menu()
    for item in itemlist:
        #print item
        #item == empty list?
        if len(item) == 0:
            menu.AppendSeparator()
        #item[0] == menu itself? => Append submenu!
        elif type(item[0]) == type(wx.Menu()):
            menu.AppendMenu(-1,item[1],item[0])
        else:
            #itemlist = [name,status bar text,method to bind,CheckItem,CheckFlag,EnableFlag]
            #ckecked?
            if type(item[3]) == type(wx.ITEM_CHECK):
                x = menu.Append(-1,item[0],item[1],item[3])
                x.Check(item[4])
            else:
                x = menu.Append(-1,item[0],item[1])
            #enable?
            x.Enable(item[5])
            parent.Bind(wx.EVT_MENU,item[2],x)
    return menu
    
def PopulateMenuBar(parent):
    """
            Popluates SPlot's MenuBar
            expects parent class
    """
    <snip>
    #menulist = (name,status bar text,method to bind,CheckItem,CheckFlag,EnableFlag)
    ############## build menu 4 ##############
    submenu4list = (
    ("Show &Grid","Turn Grid in plot on / off",parent.OnGridCheck,wx.ITEM_CHECK,True,True),
    ("Increase Text Size\tCtrl++","Increase Text Size in all Plots",parent.OnChangeSize,'','',True),
    ("Decrease Text Size\tCtrl+-","Decrease Text Size in all Plots",parent.OnChangeSize,'','',True),
    (),
    ("Color Plot Option","Plot with colors or black & white",parent.OnColorPlot,wx.ITEM_CHECK,False,True),
    ("&Plot Error Bars","Plot with an error bar estimate",parent.OnErrorBarPlot,wx.ITEM_CHECK,False,True)
    )
    submenu4 = build_one_menu(parent,submenu4list)
    menu4list = (
    ("&Save Plot to Image","Save current plot",parent.OnSavePlot,'','',True),
    ("&Refresh\tCtrl+R","Redraw Plotting Window",parent.OnRefresh,'','',True),
    (submenu4,"&View Options"),
    (),
    ("&Log Intensity\tCtrl+L","Show the logarithm of the intensity",parent.OnSetLog,wx.ITEM_CHECK,True,False),
    (),
    ("Show Energy","Show Energy Distribution Plot",parent.OnPlotEnergy ,wx.ITEM_CHECK,False,False),
    ("Log Energy","Show the logarithm of the energy distribution plot",parent.OnSetLogEnergy ,wx.ITEM_CHECK,False,False)
    )
    menu4 = build_one_menu(parent,menu4list)
    ############## menu 4 ready ##############
    <snip>
    menuBar = wx.MenuBar()
    menuBar.Append(menu1,"&File")
    menuBar.Append(menu2,"&Evaluate")
    menuBar.Append(menu3,"&Desmearing")
    menuBar.Append(menu4,"&View")
    menuBar.Append(menu5,"&Help")
    parent.SetMenuBar(menuBar)
Ergibt recht übersichtliche Codestrukturen (werde wahrscheinlich gleich gesagt
bekommen, daß es noch übersichtlicher geht ... ;-) ), inkl. Untermenüs,
CHECK_ITEMs und teilw. ausgegrauten Menüoptionen.

Später im Mainframe kommt man beispielsweise so an die ehemals "disabelten" Menüpunkte:

Code: Alles auswählen

id = self.GetMenuBar().FindMenuItem("View","Log Intensity")
self.GetMenuBar().Enable(id,True)
Na, vielleicht findet es ja jemand hier nützlich ...
Ich würde mich auch freuen, wenn mir jemand einen Tipp gibt es noch besser zu ordnen :-).

Gruß,
Christian

edit: Einige für mich sinnlose &s herausgenommen.
Zuletzt geändert von CM am Montag 14. August 2006, 15:34, insgesamt 1-mal geändert.
Francesco
User
Beiträge: 824
Registriert: Mittwoch 1. Dezember 2004, 12:35
Wohnort: Upper Austria

Hallo Christian,

so etwas ähnliches ist auch im wxPython Buch im Kapitel 5:
Ohne es mir genau angesehen zu haben, glaube ich, dass deine
Version noch flexibler ist.

Kennst du auch Metamenus von E. A. Tacão?
http://j.domaindlx.com/elements28/wxpyt ... menus.html
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi Franz,

nein, das Beispiel kannte ich nicht. Danke. Aber ich frage mich - ohne mir jetzt viel Zeit genommen zu haben mir das mal anzuschauen -, ob es alle meine Anforderungen erfüllt.
Was ich wollte ist all diese Features nutzen können: Submenu, wx.ITEM_CHECK und Enable. Und da ich häufiger mal kleine Menüs schreibe (geht anderen sicher auch so) und jetzt eine Anwendung größer wurde, aber noch nicht fertig ist, wurde es jetzt Zeit zum Refactoring.
In Robins Buch gibt es ein schönes Beispiel zum Refactoring an Hand von Buttons. Ich finde es sehr lehrreich, aber andererseits gefallen mir manche Stileigenheiten nicht - Geschmackssache. Vor allem aber läßt es sich nicht 1:1 für Menüs umsetzen. Und daher meine Variation auf dieses Thema (, wobei manches noch ein rechter "dirty Hack" ist).

Gruß,
Christian
Francesco
User
Beiträge: 824
Registriert: Mittwoch 1. Dezember 2004, 12:35
Wohnort: Upper Austria

CM hat geschrieben:Hoi Franz,

nein, das Beispiel kannte ich nicht. Danke. Aber ich frage mich - ohne mir jetzt viel Zeit genommen zu haben mir das mal anzuschauen -, ob es alle meine Anforderungen erfüllt.
Was ich wollte ist all diese Features nutzen können: Submenu, wx.ITEM_CHECK und Enable. Und da ich häufiger mal kleine Menüs schreibe (geht anderen sicher auch so) und jetzt eine Anwendung größer wurde, aber noch nicht fertig ist, wurde es jetzt Zeit zum Refactoring.
In Robins Buch gibt es ein schönes Beispiel zum Refactoring an Hand von Buttons. Ich finde es sehr lehrreich, aber andererseits gefallen mir manche Stileigenheiten nicht - Geschmackssache. Vor allem aber läßt es sich nicht 1:1 für Menüs umsetzen. Und daher meine Variation auf dieses Thema (, wobei manches noch ein rechter "dirty Hack" ist).

Gruß,
Christian
Hi Christian,

wahrscheinlich wird es nicht alle Anforderungen entsprechen, die Du braucht, denke ich.
Ich finde Deinen Code sehr nützlich. Vielleicht wenn ich wieder mal ins Programmierfieber falle, es vielleicht mit Deiner Erlaubnis verwenden.

Irgendwo habe ich gelesen type sei deprecated und isinstance sollte man vorziehen.

Da man item[3] eigentlich immer anhängen kann, glaube ich dass das der Check überflüssig ist. Falls es ein "normales" Item ist, kann man wx.ITEM_NORMAL anhängen. Ausserdem kann man noch auf wx.ITEM_RADIO abfragen. Also einfach 0 bei item[3] übergeben, dann braucht man das menu.Append nur einmal. Und das Check funktioniert auch für Radioitem (bin mir aber da nicht ganz sicher)?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi Franz,

vielen Dank für Deine Tipps - auf ein bißchen was bin ich gestern Abend selber gekommen, da ich nämlich in der Tat jetzt (in einem anderen Menu) auch noch radio-Funktionalität einbinden will. Ggf. werde ich demnächst einmal ein Update posten und Deinen Rat miteinbinden.

Bzgl. type vs. isinstance: Also ich gehöre zu den Leuten, die es wirklich schätzen, wenn wenigstens ein bis zwei Versionen von Python eine deprection warning erscheint, bevor eine Umstellung erfolgt. Im Allgemeinen ist das ja auch so. Also reagiere ich erst, wenn ich so etwas sehe ...

Und natürlich kann einjeder dieses Snippet verwenden - auch ohne Credits. Ist ja schließlich das Forum für kleine "zwischendurch" Snippets und nicht mehr. ;-)

Besten Gruß,
Christian
Francesco
User
Beiträge: 824
Registriert: Mittwoch 1. Dezember 2004, 12:35
Wohnort: Upper Austria

Hi Christian, habe dein Posting erst jetzt gesehen.

Naja, ich frage aus Höflichkeit halt. :)
hmueller
User
Beiträge: 39
Registriert: Montag 27. November 2006, 16:07
Wohnort: Linz, Oberösterreich

Ich hätte eine Frage zu diesem Teil hier:

Code: Alles auswählen

    ("Increase Text Size\tCtrl++","Increase Text Size in all Plots",parent.OnChangeSize,'','',True),
    ("Decrease Text Size\tCtrl+-","Decrease Text Size in all Plots",parent.OnChangeSize,'','',True), 
Wie weiss der Eventhandler in diesem Fall, ob er nun vergrößern oder verkleinenern soll?
Könnte mir da jemand ein Schnippselchen antworten, wie ich das unterscheiden kann, welcher Menüpunkt das aufgerufen hat?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Zur Information:

Oh, die Frage hatte ich völlig übersehen, bis hmueller mir eine PN schickte.

Meine Antwort: Ich hatte das zu dieser Zeit noch über die IDs gemacht. Das war aber, aufgrund der Umstellungen meines Programms zu unflexibel und zu buggy. Also habe ich darauf umgestellt auf zwei versch. Methoden zuzugreifen, auch weil mir keine sichere, einfache Möglichkeit bekannt ist es anders zu machen.

(Im Thread http://www.python-forum.de/topic-8025.html wurde dies nochmal von hmuller aufgegriffen und es findet sich eine gänzlich andere Lösung.)

Gruß,
Christian
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Wenn man das Codesnippet um eine FileHistory erweitern will, geht das so:

Code: Alles auswählen

# in build_one_menu
      elif item[0] == 'Filehistory':
            parent.recentfilesubmenu =  wx.FileHistory()
            tmp = wx.Menu()
            menu.AppendMenu(-1, 'FileHistory', tmp)
            parent.recentfilesubmenu.UseMenu(tmp)
            parent.Bind(wx.EVT_MENU_RANGE, parent.OnFileHistory, id = wx.ID_FILE1, id2 = wx.ID_FILE9)
 
aufgerufen explizit durch:

Code: Alles auswählen

menu1list = (
    ("About", "More Information about this program.", parent.OnAbout, None, None, True),
    ("History", "Version History of this program", parent.OnHistory, None, None, True),
    (),
    ("&Open\tCtrl+O", "Open SAXS Data Sets", parent.OnOpen, None, None, True),
    ('Filehistory', "Open recent files"),
    ("&Save\tCtrl+S", "Save Datasets as Internal File", parent.OnSave, None, None, True),
...
und im parent (wx.Frame == der Mainframe):

Code: Alles auswählen

    def OnFileHistory(self, evt):
        # get the file based on the menu ID
        fileNum = evt.GetId() - wx.ID_FILE1
        path = self.recentfilesubmenu.GetHistoryFile(fileNum)
        # add it back to the history so it will be moved up the list
        self.recentfilesubmenu.AddFileToHistory(path)
        # finally, open the selected path again:
        self.FileOpen([path])
was noch nicht ein Historyfile einbindet, wo diese Daten zwischen zwei Starts des Programms abgelegt werden können. Mehr dazu findet sich u. a. hier.

Gruß,
Christian
Antworten