Signal funktioniert nicht richtig

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Pysa
User
Beiträge: 2
Registriert: Donnerstag 28. September 2023, 06:58

Hallo zusammen,
ich dachte mir, ich gucke mal in Python und GTK3 rein und teste,, ob dass eine gute Alternative für mich ist - stehe daher noch am Anfang.
Um mir das Herumgehühner mit bsp. einer scrollbaren GtkListbox zu ersparen (box'en, Row's und darin Labels erstellen), habe ich mir eine Klasse erstellt, mit der es einfacher läuft eine simple Listbox zu erstellen und zu verwlten.
Aufgabe ist, dass die einfache Listbox- Klasse eine Liste in einem GtkScrolledWindow erstellt und man leicht Row's hinzufügen, entfernen, bearbeiten kann...
Das funktioniert auch.

Problem ist, wenn ich ein Signal an einen im Hauptfenster befindlichen Butten erstelle und über dessen 'clicked'- Signal bsp. ein Row hinzufügen möchte.
Die entsprechende callback wird ausgeführt und die AddItem- Funktion der Listen- Klasse wird auch ausgeführt, Row wird dort erstellt und auch der Liste hinzugefügt - aber nicht angezeigt.

In den letzten beiden Zeilen der Fensterklasse habe ich die AddItem- Funktion einmal direkt ausgeführt und einmal die callback manuell angesprochen - beides funktioniert.

Hier der Code:

Code: Alles auswählen


import gi
gi.require_version('Gtk','3.0')
from gi.repository import Gtk, GLib
from gi.repository import Gdk

class MyList(Gtk.ScrolledWindow):
    def __init__(self) -> None:
        super().__init__()
        self.lst = Gtk.ListBox()
        self.add(self.lst)
    def AddItem(self, Text:str):
        Row = Gtk.ListBoxRow()
        lbl = Gtk.Label(label = Text, halign = Gtk.Align.START)
        Row.add(lbl)
        self.lst.add(Row)
        print(self.lst)
        print(Text)
        ItemCount = 0
        for i in self.lst:
            ItemCount += 1
        print('Neuer Count: ', ItemCount)
        
        
class MyWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title = "Mein Fenster")
        self.vBox = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
        self.Bef1 = Gtk.Button(label = 'Test')
        self.lstKlienten = MyList()
        self.vBox.pack_start(self.lstKlienten, True, True, 0)
        self.vBox.pack_start(self.Bef1, True, True,0)
        self.Bef1.connect('clicked', self.bef1_onClick)
        self.add(self.vBox)
        self.lstKlienten.AddItem("Sascha")
        self.bef1_onClick(0)

    def bef1_onClick(self, gadget):
        self.lstKlienten.AddItem('Thomas')


FrmMain = MyWindow()
FrmMain.show_all()
Gtk.main()
Vermutlich fehlt mir irgend eine Kleinigkeit - eine Rückgabe oder ein Abschluss des Signals - oder so...

Kann mir da jemand einen Hinweis geben, wie ich das Problem lösen kann?

Vielen Dank
Pysa
Benutzeravatar
__blackjack__
User
Beiträge: 13457
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da fehlt ein `show_all()`-Aufruf.

`GLib` und `Gdk` werden importiert, aber nirgends verwendet.

Die Namen sind nicht gut und weit von den Konventionen entfernt. Dabei ist Gtk ein GUI-Rahmenwerk dessen Namenskonventionen ganz gut zu denen von Python passen, darum verstehe ich die Abweichungen nicht so wirklich. Es sieht so ein bisschen so aus als würde dort in einer anderen Programmiersprache als Python programmiert, mit den kryptischen Abkürzungen/Typpräfixen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen.

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.

Der Präfix `my` macht keinen Sinn wenn es nicht auch ein `our` oder `their` gibt, was eine Unterscheidung nötig machen würde.

Statt dem Click-Handler beim manuellen Aufruf irgendetwas unsinniges mitzugeben, würde man das Argument besser optional machen.

Was da noch fehlt ist das verlassen der Gtk-Hauptschleife wenn das Fenster geschlossen wird. Sonst läuft der Prozess einfach ohne GUI weiter.

Code: Alles auswählen

#!/usr/bin/env python3
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class ScrolledListBox(Gtk.ScrolledWindow):
    def __init__(self):
        super().__init__()
        self.list_box = Gtk.ListBox()
        self.add(self.list_box)

    def add_item(self, text):
        row = Gtk.ListBoxRow()
        row.add(Gtk.Label(label=text, halign=Gtk.Align.START))
        self.list_box.add(row)
        self.list_box.show_all()
        print("Neuer Count: ", sum(1 for _ in self.list_box))


class Window(Gtk.Window):
    def __init__(self):
        super().__init__(title="Mein Fenster")
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.add(box)
        
        self.clients_list_box = ScrolledListBox()
        box.pack_start(self.clients_list_box, True, True, 0)
        
        button = Gtk.Button(label="Test")
        box.pack_start(button, True, True, 0)
        
        button.connect("clicked", self.on_button_click)
        self.clients_list_box.add_item("Sascha")
        self.on_button_click()

    def on_button_click(self, _widget=None):
        self.clients_list_box.add_item("Thomas")


def main():
    window = Window()
    window.connect("destroy", Gtk.main_quit)
    window.show_all()
    Gtk.main()


if __name__ == "__main__":
    main()

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
Pysa
User
Beiträge: 2
Registriert: Donnerstag 28. September 2023, 06:58

Hallo __blackjack__,
ich danke dir vielmals für deine Mühe.

Für diejenigen die den Code nicht vergleichen wollen und den substanziellen Fehler suchen:
In der Klasse MyList fehlte nach dem Erstellen des "Konstrukts" im Konstruktor
self.lst.shw_all()

Ich danke für die Hinweise zur Konvention der Schreibweisen.
Zum einen habe ich den Code nur geschwind zusammengeschustert und ich denke, dafür hat es gereicht.
Zum anderen halte ich mich an die Ungarische Notation (größtenteils). Ich komme aus dem C/C++/ASM Bereich der 90er Jahre...
Konventionen sind wichtig, ja, aber kein Zwang. Nach jahrzehnten entwickelt man seine Schreibweisen mit denen man gut zurecht kommt und die zu ändern ist unnötig.
Für dritte, die diesen Code irgend wann evtl. mal lesen und verstehen müssen mache ich intensieven Gebrauch von Kommentaren.
Und in meinen Codes heißen die Button natürlich nicht Bef1 :)

Aber, Respekt für deinen Einsatz . Warum die Rows ohne show_all hinzugrfügt werden wenn ich die Add. Funktion direkt aufrufe aber nicht, wenn ich über den Signal- Callback gehe muss ich aber erst nachlesen.

Beste Grüße
Pysa
Benutzeravatar
__blackjack__
User
Beiträge: 13457
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pysa: Wenn man Kommentare braucht um kryptische Namen zu erklären macht man etwas falsch und das zu ändern würde ich nicht als unnötig ansehen. Das Problem was damit gelöst werden sollte war das die tatsächlichen Datentypen nicht ausreichten um den semantischen Typ zu beschreiben. Zum Beispiel in Assembler wo ja alles letztlich nur eine Zahl ist, und man deshalb einen semantischen Typ in den Namen kodiert hat. Das trifft auch auf C noch zu wo sehr viele unterschiedliche Informationen in einfachen ganzen Zahlen ausgedrückt wurden und der Datentyp selbst deshalb nicht ausreichend war als Information was da kodiert wird.

Das ist in Python und vielen anderen modernen Programmiersprachen anders. Da muss man nicht hClientList schreiben um zu signalisieren, dass der ``int``-Wert ein Handle ist, das stellvertretend für einen ListBox steht, denn der Wert ist hier das Objekt selbst. Und das kennt seinen Typ, und den kann man auch abfragen und der taucht auch in Fehlermeldungen auf wenn man damit versucht was zu machen was nicht geht. Im Gegensatz zu den h-Zahlen mit denen man allerlei unsinnige Sachen machen konnte wenn man ignoriert hat wofür die tatsächlich stehen.

Warum heisst `Bef1` dann im Beispiel so komisch wenn in echtem Code sinnvollere Namen verwendet werden? Was ist daran naheliegender und verständlicher als `button`?

Den Unterschied zwischen Signal und direktem Aufruf gibt es nicht. Man muss immer mit einem entsprechenden Aufruf dafür sorgen, dass das auch tatsächlich angezeigt wird. Für die Daten die vor dem Start der Hauptschleife hinzugefügt werden, ist das der `show_all()`-Aufruf auf dem Fenster, der ja seinerseits dafür sorgt das rekursiv `show_all()` auf allen verschachtelten Elementen, also auch auf der `ListBox` aufgerufen wird.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
Antworten