Widgets als Klassen in Grid

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

Ich versuche eine GUI mit PyGtk zu programmieren. In einem Hauptfenster sollen in einem Grid Widgets angezeigt werden.
Diese Widgets haben eine eigene Klasse. Dies aus dem Grund weil das Widget eher komplex sein wird und mehrfach vorkommt. Im Beispiel ist diese Klasse auf einen Button vereinfacht (class OtherWidget).
Es gelingt mir Instanzen der OtherWidget-Klasse anzuzeigen. Unabhängig welcher Button als zweiter angeklickt wird, findet diese Instanz das Attribut setting dieser Klasse nicht mehr (AttributeError: "OtherWidget" has no attribute "_OtherWidget__Setting"). Erwarten würde ich, dass beim Klick auf Button One "Button One Left" oder "Button One Right" erscheint. Entsprechend beim Button Two: "Button Two Left" oder "Button Two Right".

Anbei der Code:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from gi.repository import Gtk

class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        self.__grid = Gtk.Grid()
        self.add(self.__grid)
        self.__column = 0
        self.__row = 0
        self.__setting1 = "One"
        self.__setting2 = "Two"

        OtherWidget(self,self.__setting1) # widget 1
        self.__column += 1
        OtherWidget(self,self.__setting2) # widget 2

    def add_widget_to_grid(self,widget):
        """ add widget to grid, called from OtherWidget """
        self.__grid.attach(widget,self.__column,self.__row,1,1)

class OtherWidget():

    def __init__(self,window,setting):
        self.__window = window 
        self.__setting = setting 
        
        self.__main_button = Gtk.Button("Button "+self.__setting) 
        self.__main_button.connect("button-press-event", self.__button_pressed)
        self.__window.add_widget_to_grid(self.__main_button) 

    def __button_pressed(self,button,event):
        if event.button == 1:
            print("Left "+self.__setting)
    
        elif event.button == 3: 
            print("Right "+self.__setting)

def main():
    app = MainWindow()
    app.connect("delete-event", Gtk.main_quit)
    app.show_all()
    Gtk.main()

if __name__ == '__main__':
    main()
Bin um Hinweise dankbar, da ich in der Doku zu PyGtk noch nichts hilfreiches zu diesem Verhalten gefunden habe.

Danke im Voraus

Thomas
BlackJack

@Umsteiger73: Das hat nichts mit PyGtk zu tun sondern mit den doppelten führenden Unterstrichen. Implementierungsdetails kennzeichnet man mit *einem* führenden Unterstrich. Und selbst den sollte man weglassen wenn man von einer anderen Klasse aus auf das Attribut zugreift, denn dann ist es ja offensichtlich kein internes Detail sondern Teil der öffentlichen API.
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

Hallo BlackJack

Ist leider nicht die Lösung. Auch der Code ohne führende Unterstriche macht denselben Error.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from gi.repository import Gtk

class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        self.grid = Gtk.Grid()
        self.add(self.grid)
        self.column = 0
        self.row = 0
        self.setting1 = "One"
        self.setting2 = "Two"

        OtherWidget(self,self.setting1) # widget 1
        self.column += 1
        OtherWidget(self,self.setting2) # widget 2

    def add_widget_to_grid(self,widget):
        """ add widget to grid, called from OtherWidget """
        self.grid.attach(widget,self.column,self.row,1,1)

class OtherWidget():

    def __init__(self,window,setting):
        self.window = window 
        self.setting = setting 
        
        self.main_button = Gtk.Button("Button "+self.setting) 
        self.main_button.connect("button-press-event", self.button_pressed)
        self.window.add_widget_to_grid(self.main_button) 

    def button_pressed(self,button,event):
        if event.button == 1:
            print("Left "+self.setting)
    
        elif event.button == 3: 
            print("Right "+self.setting)

def main():
    app = MainWindow()
    app.connect("delete-event", Gtk.main_quit)
    app.show_all()
    Gtk.main()

if __name__ == '__main__':
    main()
Gruss

Thomas
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Umsteiger73: hängt das irgendwie damit zusammen, dass Du Dir keine Referenz auf Deine OtherWidget-Instanzen merkst? Kannst Du bitte den kompletten Traceback posten.
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

Ich bin ein Schritt weiter gekommen.

Wenn ich anstelle von "button-press-Event" den Button mit "clicked" verbinde funktioniert das ganze ohne Fehlermeldung.

Code: Alles auswählen

class OtherWidget():

    def __init__(self,window,setting):
        self.__window = window 
        self.__setting = setting 
        
        self.__main_button = Gtk.Button("Button "+self.__setting) 
        #self.__main_button.connect("button-press-event", self.__button_pressed)
        self.__main_button.connect("clicked",self.__button_clicked)
        self.__window.add_widget_to_grid(self.__main_button) 

    #def __button_pressed(self,button,event):
        #if event.button == 1:
            #print("Left "+self.__setting)
    
        #elif event.button == 3: 
            #print("Right "+self.__setting)
            
    def __button_clicked(self,button):
        print("Left "+self.__setting)
Kann mir das jemand erklären? Was habe ich beim "button-press-Event" nicht begriffen?

Danke und Gruss

Thomas
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

Hallo

Problem gelöst. Ein Erbe sollte man nie ausschlagen (Zeile 24 + 30) ;).

Dieses Script funktioniert:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from gi.repository import Gtk

class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        self.grid = Gtk.Grid()
        self.add(self.grid)
        self.column = 0
        self.row = 0
        
        for row in range(0,7):
            for column in range(0,3):
                widgetname = "self."+str(self.row)+str(self.column)
                widgetname = OtherWidget(self,str(row)+str(column))
                self.grid.attach(widgetname,self.column,self.row,1,1)
                self.column += 1
            self.column =0
            self.row +=1

class OtherWidget(Gtk.Button):

    def __init__(self,window,setting):
        self.window = window 
        self.setting = setting
        
        Gtk.Button.__init__(self,label="Button "+str(self.setting)) 
        
        self.connect("button-press-event", self.button_pressed)

    def button_pressed(self,button,event):
        if event.button == 1:
            print("Left "+self.setting)
    
        elif event.button == 3: 
            print("Right "+self.setting)

def main():
    app = MainWindow()
    app.connect("delete-event", Gtk.main_quit)
    app.show_all()
    Gtk.main()

if __name__ == '__main__':
    main()
Das Problem für dieses Forum zu formulieren, war schon die halbe Lösung.

Gruss Thomas
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Umsteiger73: damit speicherst Du nun explizit die Referenz auf Deine OtherWidget-Instanzen innerhalb von grid.attach. Bei GUI-Rahmenwerken muß man immer aufpassen, wie dort intern Referenzen gehandhabt werden, damit der GC von Python nicht etwas freigibt, was eigentlich noch gebraucht wird.
Was soll Zeile 17 bewirken?? Und für was ist self.column und self.row da?
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

@Sirius3
Danke für deinen Input.

Mit Zeile 17 sprichst du etwas an, für das ich gerne eine elegantere Lösung hätte. Zeile 17 setzt mir den Objektname (Bsp: self.00, self.10, self.20) zusammen, damit ich später auf das Widget zugreifen kann. Bessere Möglichkeiten sind gerne Willkommen.

self.column und self.row stehen für die Zellen des Grid, in die das Widget dann eingefügt wird. Da ich auf ein Box-Layout umsteige, werden diese Attribute nicht mehr nötig sein.

Gruss

Thomas
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Umsteiger73: "self.row" und "self.column" sind Zählvariablen, sind also keine Attribute Deines Objekts, haben folglich nichts in self zu suchen. Und huch, die selben Zählvariablen hast Du ja schon als "row" und "column" in Deinen for-Schleifen. Das Selberzählen ist also total unnötig. Genauso Zeile 17: widgetname wird nie verwendet, weil es in Zeile 18 sofort wieder überschrieben wird.
Umsteiger73
User
Beiträge: 12
Registriert: Mittwoch 29. April 2015, 18:12

@Sirius3

Herzlichen Dank. Schon wieder was gelernt.

Gruss

Thomas
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Umsteiger73: da war nichts zu lernen, Du mußt nur Deinen eigenen Code lesen.
Antworten