GTK, Callback auf Klassenmethode



  • Hallo ich hab ein kleines Problem, das mir ganz schön Kopfzerbrechen bereitet.

    Ich hab eine Klasse Uebungsplan.
    Diese Klasse bringt eine eigene show() Methode mit, mit der die ganze Oberfläche
    erstellt wird und ein GtkWidget zurückgegeben wird.

    Soweit alles kein Problem. Aber jetzt hab ich auf der Oberfläche einen Button,
    und möchte von diesem Button aus, eine Callback-Funktion aufrufen.
    Tja und wie kann ich das ganze jetzt zum Laufen bringen.
    Übergeben ich die Funktion als Klassenmethode, bekomme ich einen Compilerfehler.
    Deklariere ich die Callback-Funktion als friend, dann habe ich keinen Zugriff auf die aufrufende Klasse Uebungsplan.
    Und mehr Ideen hab ich noch nicht gehabt.

    Würd mich freuen, wenn mir jemand helfen könnte.

    Hier mal noch ein paar Codeschnipsel:

    Die main.cpp

    #include <gtk/gtk.h>
    #include "uebungsplan.h"
    
    static void destroy( GtkWidget *widget, gpointer   data );
    
    int main( int   argc, char *argv[] )
    {
        GtkWidget *window;
        Uebungsplan u;
    
        gtk_init (&argc, &argv);
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window),"UebungsTimer V0.2");
        gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
        //gtk_window_set_default_size(GTK_WINDOW(window),500,500);
    
        g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
    
        GtkWidget *UebungsplanBox = u.show();
        gtk_container_add (GTK_CONTAINER (window), UebungsplanBox);
        gtk_widget_show  (UebungsplanBox);
        gtk_widget_show  (window);
        gtk_main ();
        return 0;
    }
    
    static void destroy( GtkWidget *widget, gpointer   data )
    {
        gtk_main_quit ();
    }
    

    Die Uebungsplan.h

    #ifndef UEBUNGSPLAN_H
    #define UEBUNGSPLAN_H
    #include "lesson.h"
    #include "timer.h"
    #include <gtk/gtk.h>
    
    class Uebungsplan
    {
        public:
            Uebungsplan();
            ~Uebungsplan();
            GtkWidget * show();
            friend void addCallback(GtkWidget *widget,gpointer *data);
            void addLesson();
        protected:
        private:
            Timer time;
            int iLessons;
            int iCurrentLesson;
            Lesson * lessons;
            // private Functions
            GtkWidget * showControlPanel();
            GtkWidget * showUebungsplan();
            GtkWidget * showAddLessonPanel();
    
    };
    
    #endif // UEBUNGSPLAN_H
    

    und der Teil mit dem Button:

    GtkWidget * Uebungsplan::show()
    {
        GtkWidget *vbox;
        GtkWidget *frame;
        GtkWidget *MyControlPanel;
        GtkWidget *MyUebungsplan;
        GtkWidget *MyAddLessonPanel;
    
        vbox = gtk_vbox_new (TRUE, 0);
        gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
    
        .........
    
        // the Add Lesson area
        frame = gtk_frame_new ("Add Lesson");
        MyAddLessonPanel = showAddLessonPanel();
        gtk_container_add (GTK_CONTAINER (frame), MyAddLessonPanel);
        gtk_widget_show (MyAddLessonPanel);
        gtk_container_add (GTK_CONTAINER (vbox), frame);
        gtk_widget_show (frame);
    
        .....
    
        return vbox;
    }
    
    GtkWidget * Uebungsplan::showAddLessonPanel()
    {
        GtkWidget *table;
        GtkWidget *label;
        GtkWidget *entry;
        GtkWidget *button;
        GtkWidget *spinner;
        GtkAdjustment *adj;
    
        gint tmp_pos;
    
        table = gtk_table_new (2, 4, FALSE);
    ........
    

    Hier ist das Problem in der g_signal_connect Funktion,
    was muss bei G_CALLBACK() übergeben werden.

    // der Add Button
        button = gtk_button_new_with_label ("Add");
        gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
        //g_signal_connect(GTK_OBJECT(button),"clicked", G_CALLBACK(addCallback),NULL);
        gtk_widget_show (button);
        return table;
    }
    
    void Uebungsplan::addLesson()
    {
    
        Lesson * lessonsTemp;
        lessonsTemp = new Lesson[iLessons+1];
        for(int i=0; i < iLessons; i++)
            lessonsTemp[i] = lessons[i];
        iLessons++;
        lessonsTemp[iLessons].setLesson("Neu", 5*60);
        delete [] lessons;
        lessons = new Lesson[iLessons];
        for(int i=0; i < iLessons; i++)
            lessons[i] = lessonsTemp[i];
        delete [] lessonsTemp;
    }
    


  • Das einzige was mir einfallen würde ist den this zeiger zu übergeben:

    g_signal_connect(GTK_OBJECT(button),"clicked", G_CALLBACK(addCallback),this);
    

    Und dann über diesen Zeiger auf Klassen Elemente zugreifen:

    void addCallback(GtkWidget *widget, gpointer *data)
    {
        Uebungsplan *p = (Uebungsplan*)data;
        p->iLessons++;
        g_print("Callback %d\n",p->iLessons);
    }
    

    Weil addCallback friend ist, funktioniert das. Leider ist es nicht besonders elegant.

    Und wenn ich das richtig verstehe soll addCallback ein Callback sein, und nicht einen Callback hinzufügen. Richtig ?
    Der Name addCallback ist etwas verwirrend für mich.



  • Ja richtig, die addCallback Funktion ist ein Callback und zwar ein Callback,
    der dann die Methode Uebungsplan::addLesson() aufruft.
    Stimmt, daran hab ich gar nicht gedacht, das das etwas Missverständlich ist.

    Hab auch schon daran, gedacht das irgendwie mit dem *data mitzuschleppen,
    das ist dann aber an meinen Kentnissen bezüglich dieses Pointers gescheitert.
    Ich habs dummerweise mit *this versucht 😉

    Oki danke erstmal für die Lösung, das schein zu funtionieren.

    Noch ne weitere Frage hätt ich.
    So siehts jetzt aus, nicht schön aber sehr selten. 🙂
    Wenn ich jetzt die Lesson hinzugefügt hab muss ich ja alles "neuzeichnen".
    Gibt es da ne redraw()-Funktion oder wie muss ich das machen??

    void Uebungsplan::addLesson()
    {
    
        Lesson * lessonsTemp;
        lessonsTemp = new Lesson[iLessons+1];
        for(int i=0; i < iLessons; i++)
            lessonsTemp[i] = lessons[i];
        iLessons++;
        lessonsTemp[iLessons].setLesson("Neu", 5*60);
        delete [] lessons;
        lessons = new Lesson[iLessons];
        for(int i=0; i < iLessons; i++)
            lessons[i] = lessonsTemp[i];
        delete [] lessonsTemp;
    }
    
    void addCallback(GtkWidget *widget,gpointer *data)
    {
        Uebungsplan *p = (Uebungsplan*)data;
        p->addLesson();
    }
    


  • Vielleicht dazu noch der Link: http://www.newty.de/fpt/index.html



  • Wenn ich jetzt die Lesson hinzugefügt hab muss ich ja alles "neuzeichnen".
    Gibt es da ne redraw()-Funktion oder wie muss ich das machen??

    Falls Lesson ein GtkWidget ist dann nicht. Du muss es nur mit gtk_widget_show() anzeigen lassen.

    Und übrigens wenn du schon in C++ programierts dann könntest du gtkmm nehmen (falls sich noch lohnt, man müsste einiges neu schreiben).
    Mit gtk geht auch, aber es wäre bequemer.



  • Nee Lessons ist leider kein GtkWidget 😞

    Ich glaub ich hab da ein grundsätzliches Problem.

    Ich hab das ganze so strukturiert, das mein Klasse Uebungsplan
    immer ein GtkWidget in der entsprechenden show() Funktion zurückgibt.
    Das ganz wird dann durchgereicht und landet irgendwann in main(),
    wo es dann zum Window hinzugefügt wird und angezeigt wird.

    Also muss ich jetzt theoretisch wieder der Funktion
    void Callback_addLesson(GtkWidget *widget,gpointer *data) mitteilen, dass es da in main() etwas gibt, was sie gefälligst neu zeichnen soll...

    Nicht schön oder???

    Vielleicht sollt ich mir wirklich mal das gtkmm anschauen.
    Hab damit noch gar nix gemacht.

    Hab auch mal überlegt über wxWidgets auf das Gtk loszugehen.
    Da muss ich dann warscheinlich auch alles neu machen,
    aber so viel ists noch nicht.

    Welches ist denn da empfehlenswert? wxWidgets oder gtkmm???
    Danke



  • Ich hab das ganze so strukturiert, das mein Klasse Uebungsplan
    immer ein GtkWidget in der entsprechenden show() Funktion zurückgibt.
    Das ganz wird dann durchgereicht und landet irgendwann in main(),
    wo es dann zum Window hinzugefügt wird und angezeigt wird.

    Bis dahin alles klar.

    Also muss ich jetzt theoretisch wieder der Funktion
    void Callback_addLesson(GtkWidget *widget,gpointer *data) mitteilen, dass es da in main() etwas gibt, was sie gefälligst neu zeichnen soll...

    Ja, nein, ok. Neu zeichnen? In Gtk braucht man meistens nichts neu zu zeichnen, darum kümmert sich gtk selbst(solange keine Gdk-Sachen im Spiel sind). Also tut mir leid aber ich verstehe nicht was da neu gezeichnet werden soll/muss.

    wxWidgets oder gtkmm???

    Also ich habe mit wxWidgets noch nichts gemacht soll aber sehr gut sein.
    Mit gtkmm soll es Probleme unter Windows geben (habe keine eigenen Erfahrungen dies bezüglich), unter Linux funktioniert sehr gut.



  • Sorry wegen der Doppeldeutigkeit.
    Ich meine mit neuzeichen, die Darstellung der Gtk-Oberfläche, nachdem ich darauf was geändert hab. Also einfach ein erneutes ausführen von gtk_widget_show().

    Denn ich ändere die Widgets auf der Oberfläche zur Laufzeit des Programms.
    Drückt der User auf den Button Add wird in der Tabelle wo die Lessons drinnstehen,
    eine neue Zeile aus mit vier Labels angelegt.
    Weil sich die Tabelle geändert hat, muss jetzt ja die komplette Oberfläche,
    neu erstellt werden. Also alles nochmal durch gtk_widget_show().
    Ok ist vielleicht etwas seltsam, keine Ahnung.

    Mal sehen was noch dabei rauskommt, gerade versuch ich mal wxWidgets zum "Hello Worlden" zu bekommmen.
    Vielleicht komm ich damit weiter..

    Danke



  • Also alles nochmal durch gtk_widget_show().

    Ja, wenn du zu laufzeit ein neues Gtkwidget erstellst und zu einem anderen hinzufügst hast, dann reicht es einmal gtk_widget_show(GtkWidget*) sichtbar zu machen.
    Jegliche änderungen auf sichtbaren Widgets, etwa geänderter Text auf einem button oder geändertes Bild, werden unmittelbar auf das Widget übertragen ohne das man dass man es irgendwie neu zeichnen muss.
    Wenn der Widget der hinzugefügt wurde , selbst GtkWidgets beinhaltet dann ruf man gtk_widget_show_all(GtkWidget*) auf,um alles anzuzeigen.

    Noch etwas zu gtk mm oder nicht:
    Wenn du damit wirklich ein Programm programieren willst dass mehr als 2 buttons und ein Fenster hat, dann mach es mit glade, natürlich sollte man auch ohne glade das können, aber gtk ist ohne glade ein Krampf.



  • Hm danke für den Tipp mit Glade, das sieht ja wirklich interessant aus.
    Hab mir jetzt mal Linux draufgemacht, dann ist es glaub einfacher zu
    experimentieren.....


Anmelden zum Antworten