Forum: PC-Programmierung Assertion 'GTK_IS_WIDGET (widget)' failed


von A. D. (egsler)


Angehängte Dateien:

Lesenswert?

Ich versuche mich gerade in GTK+ einzuarbeiten, da ich für ein Projekt 
eine GUI benötige. Erfahrung habe ich mit Programmieren nicht viel, 
daher scheitert es leider immer wieder an irgendwelchen Kleinigkeiten, 
die ich aufgrund fehlender Grundlagen nicht korrigiert bekomme. Aber man 
lernt ja beim Arbeiten bekanntlich am besten...

Also momentan bekomme ich beim Ausführen meines Programms immer folgende 
Fehlermeldung:
"(GTK:2822): Gtk-CRITICAL **: gtk_widget_set_sensitive: assertion 
'GTK_IS_WIDGET (widget)' failed"

Diese Fehlermeldung stammt aus meiner Funktion "aktiv_18bit", mit der 
ich einige Buttons ausgrauen möchte. Wenn ich den Befehl exakt 
set_sensitive Befehl exakt so wie er da steht in die main() kopiere, 
dann funktioniert der Befehl. Daher wird es wohl irgendwie an meiner 
Übergabe von "button[0]" liegen? Ich hab leider auch noch nicht ganz 
verstanden, was ich da als Argumente so alles der Funktion übergeben 
muss...


Ein zweites Problem habe ich noch in der Funktion "data_acquisition".
Dort habe ich die If-Abfrage, mit der ich die Beschriftung des großen 
Buttons unten auf der GUI wechseln möchte.
Das klappt beim ersten Klick super, der Schriftzug wechselt zu 
"Aufzeichnung stoppen". Aber wenn ich dann noch ein zweites Mal (oder 
auch häufiger) darauf klicke, dann ändert sich gar nichts mehr! Und auch 
der printf-Befehl, den ich mir hilfsweise mal hineingeschrieben habe, 
wird scheinbar erst ausgeführt, wenn das Programm beednet wird! Zumindet 
erscheint erst dann in der Konsole der Text (so häufig wie ich gedrückt 
habe).
1
#include <gtk/gtk.h>
2
3
static void data_acquisition (GtkButton *trigger, gpointer AufzStrt)
4
{
5
  if (gtk_toggle_button_get_active) 
6
  {
7
    gtk_button_set_label(AufzStrt, "Aufzeichnung stoppen");
8
  }
9
  else
10
  {
11
    gtk_button_set_label(AufzStrt, "Aufzeichnung starten");
12
  }
13
  printf("gedrückt!");
14
    
15
}
16
17
static void aktiv_18bit (GtkSwitch *schalter1, gpointer button[0])
18
{
19
  if (gtk_switch_get_active (GTK_SWITCH (schalter1)))
20
    gtk_widget_set_sensitive (button[0], TRUE);
21
  else
22
    gtk_widget_set_sensitive (button[0], FALSE);
23
}
24
25
26
int main(int argc, char **argv)
27
{
28
  GtkWidget *window;
29
  GtkWidget *grid;
30
  GtkWidget *button[16];
31
  GtkWidget *AufzStrt;
32
  GtkWidget *schalter1;
33
  GtkWidget *schalter2;
34
  
35
  char label [10] = "Channel  ";
36
  
37
  gtk_init (&argc, &argv);
38
  
39
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
40
  gtk_window_set_title (GTK_WINDOW (window), "Messdatenerfassung");
41
  g_signal_connect (window, "destroy", G_CALLBACK(gtk_main_quit),NULL);
42
  gtk_container_set_border_width (GTK_CONTAINER(window), 10);
43
  gtk_widget_set_size_request (window, 600, 250);
44
  
45
  grid = gtk_grid_new ();
46
  gtk_grid_set_column_homogeneous (GTK_GRID(grid), TRUE);
47
  gtk_grid_set_row_homogeneous (GTK_GRID(grid), TRUE);
48
  gtk_container_add (GTK_CONTAINER (window), grid);
49
  
50
51
  gtk_grid_attach (GTK_GRID(grid), gtk_label_new ("AD1-18bit"), 0,0,2,1);
52
  gtk_grid_attach (GTK_GRID(grid), gtk_label_new ("AD2-14bit"), 4,0,2,1);
53
  
54
  schalter1 = gtk_switch_new();
55
  gtk_switch_set_active (GTK_SWITCH (schalter1), TRUE);
56
  gtk_grid_attach (GTK_GRID(grid), schalter1, 0,1,2,1);
57
  g_signal_connect (GTK_SWITCH (schalter1), "notify::active", G_CALLBACK (aktiv_18bit), button[0]);
58
  
59
  schalter2 = gtk_switch_new();
60
  gtk_grid_attach(GTK_GRID(grid), schalter2, 4,1,2,1);
61
  
62
  
63
  int i;
64
  //Channel-Label erstellen
65
  for (i=1; i<9; i++)
66
  {
67
    label[8] = '0'+i;                          //Schreibt in das Array "Label" an die 8. Stelle die Zahl i
68
    gtk_grid_attach (GTK_GRID(grid), gtk_label_new(label), 0,i+2,1,1);
69
    gtk_grid_attach (GTK_GRID(grid), gtk_label_new(label), 4,i+2,1,1);
70
  }
71
  
72
  //Spannung-Strom-Wahl-Button erstellen
73
  for (i=0; i<8; i++)
74
  {
75
    button[i] = gtk_button_new_with_label ("0...10V");
76
    gtk_grid_attach (GTK_GRID(grid), button[i], 1, i+3, 1, 1);
77
    //g_signal_connect(button[i], "clicked"
78
      
79
    button[i+8] = gtk_button_new_with_label ("0...10V");
80
    gtk_grid_attach (GTK_GRID(grid), button[i+8], 5, i+3, 1, 1);
81
  }
82
  
83
  AufzStrt = gtk_toggle_button_new_with_label("Aufzeichnung starten");
84
  gtk_grid_attach (GTK_GRID(grid), AufzStrt, 1, 12, 4, 1);
85
  g_signal_connect(AufzStrt, "toggled", G_CALLBACK(data_acquisition), AufzStrt);  
86
87
  //Aufteilung des Fensters
88
  gtk_grid_attach (GTK_GRID(grid), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL), 1, 11, 4, 1);
89
  gtk_grid_attach (GTK_GRID(grid), gtk_separator_new (GTK_ORIENTATION_VERTICAL), 3,1,1,10);
90
  
91
  gtk_widget_show_all(window);
92
  
93
  gtk_main();
94
    
95
  return 0;
96
}

: Verschoben durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

Ich schätze mal, das sollte so lauten.
1
static void aktiv_18bit (GtkSwitch *schalter1, gpointer button)
2
{
3
  if (gtk_switch_get_active (GTK_SWITCH (schalter1)))
4
    gtk_widget_set_sensitive (button, TRUE);
5
  else
6
    gtk_widget_set_sensitive (button, FALSE);
7
}

So macht das Sinn. deine Version macht keinen Sinn.

: Bearbeitet durch User
von Lukas K. (carrotindustries)


Lesenswert?

Schön, dass sich noch andere Leute GTK als Toolkit aussuchen :)

In deinem Code hat's mehrere Fehler:
1
g_signal_connect (GTK_SWITCH (schalter1), "notify::active", G_CALLBACK (aktiv_18bit), button[0]);
Du Übergibst dem button[0], was du erst später setzen wirst. Du willst 
einen Pointer auf den Anfang des Arrays button übergeben.(unglücklich 
gewählter Name, buttons wär, passender...). Also
1
g_signal_connect (GTK_SWITCH (schalter1), "notify::active", G_CALLBACK (aktiv_18bit), button);

Dann ist noch der Prototyp deiner aktiv_18bit Callback-Funktion falsch. 
Bei notify::active bekommt die Callback-Funktion drei Argumente. Leider 
keine exakte Quelle dafür. * In Python ist's auch so, da gibt's 
wenigstens nen Fehler, wenn die Funktion zu wenig Argumente nimmt...

Deine Callback-Funktion könnte so aussehen:
1
static void aktiv_18bit (GtkSwitch *schalter1, GParamSpec *pspec, GtkWidget **buttons)
2
{
3
  if (gtk_switch_get_active (GTK_SWITCH (schalter1)))
4
    gtk_widget_set_sensitive (buttons[0], TRUE);
5
  else
6
    gtk_widget_set_sensitive (buttons[0], FALSE);
7
}

Und dann hat's da noch was:
1
 if (gtk_toggle_button_get_active)
 evaluiert immer zu True. Nen paar Zeilen drunter hast du's richtig 
gemacht. Mit -Wall sagt der gcc einem auch, dass da was nicht stimmt.

Tipp: Seh dir das in C mit Pointern und Arrays nochmal an. Willst du dir 
GTK mit C wirklich antun? Mit Python ist's wesentlich entspannter und 
man bekommt auch Rückmeldung, wenn mal Datentypen nicht stimmen. Da in 
GTK recht viel erstmal auf void gecastet wird, bleiben Fehler wie der 
falsche Funktionsprototyp bei dir nebulös.

---
* Den Prototyp hab ich aus den Quellen des gnome-control-centers.

von Stefan Salewski (Gast)


Lesenswert?

Lukas K. schrieb:
> Schön, dass sich noch andere Leute GTK als Toolkit aussuchen :)

Schön dass Du es auch noch nutzt.

Ich mache gerade die GTK3 Wrapper für Nim...
Das mit den Callbacks wird wohl die größte Hürde sein -- hoffentlich 
auch die Einzige.

von Stefan Salewski (Gast)


Lesenswert?

Stefan Salewski schrieb:
> Das mit den Callbacks wird wohl die größte Hürde sein

Nun ist mir doch noch eingefallen, was mir etwas schwer im Magen 
liegt...

Bei diesem ganzen Callback Zeugs habe ich bisher einfach im Wesentlichen 
die Beispiele kopiert und gebenenfalls etwas angepasst -- sowohl in C 
als auch in Ruby. In dem Buch von Andrew Krause stand zum Thema ja auch 
nicht so viel.

http://zetcode.com/gfx/cairo/basicdrawing/
1
g_signal_connect(G_OBJECT(darea), "draw", 
2
    G_CALLBACK(on_draw_event), NULL); 
3
g_signal_connect(window, "destroy",
4
    G_CALLBACK(gtk_main_quit), NULL);  
5
    
6
g_signal_connect(window, "button-press-event", 
7
    G_CALLBACK(clicked), NULL);

on_draw_event(), gtk_main_quit() und clicked() sind hier ja völlig 
verschiedene Funktionen mit unterschiedlichen Parameterlisten. Wie kann 
das funktionieren, dass die Funktionen jeweils mit den richtigen 
Parameten versorgt werden?

von Lukas K. (carrotindustries)


Lesenswert?

Stefan Salewski schrieb:
> on_draw_event(), gtk_main_quit() und clicked() sind hier ja völlig
> verschiedene Funktionen mit unterschiedlichen Parameterlisten. Wie kann
> das funktionieren, dass die Funktionen jeweils mit den richtigen
> Parameten versorgt werden?

G_CALLBACK castet die Funktion als void(void). In den Tiefen von 
Gtk/GObject wird sie vorm Aufruf dann wieder umgecastet. (so mal meine 
naive Vermutung).

Ich hab mal nen backtrace aus nem callback gemacht:
1
#1  0xb73572ac in g_cclosure_marshal_VOID__PARAM () from /usr/lib/libgobject-2.0.so.0
2
#2  0xb735485b in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
Wenn's dich genauer interessiert, wirst du dir wohl die GObject closures 
näher ansehen müssen.

von Stefan Salewski (Gast)


Lesenswert?

Lukas K. schrieb:
> Wenn's dich genauer interessiert, wirst du dir wohl die GObject closures
> näher ansehen müssen.

Gut, dann ist das wohl in der Tat nicht so ganz trivial...
Wie gesagt, ich arbeite gerade an den GTK3 Bindings für Nim -- ich werde 
mich wohl ein klein wenig damit beschäftigen müssen.

von Lukas K. (carrotindustries)


Lesenswert?

Stefan Salewski schrieb:
> Wie gesagt, ich arbeite gerade an den GTK3 Bindings für Nim -- ich werde
> mich wohl ein klein wenig damit beschäftigen müssen.

Ich hab mir deine GTK3-Bindings für Nimrod mal angesehen. Auf den ersten 
blick scheint es mir, als erzeugst du diese aus den Headern von GTK. 
Weshalb setzt du nicht auf der GObject Introspection-Infrastruktur auf? 
Dann hätte man automatisch gleich Bindings für die ganze Gnome-welt.

von Stefan Salewski (Gast)


Lesenswert?

Lukas K. schrieb:
> Ich hab mir deine GTK3-Bindings für Nimrod mal angesehen. Auf den ersten
> blick scheint es mir, als erzeugst du diese aus den Headern von GTK.
> Weshalb setzt du nicht auf der GObject Introspection-Infrastruktur auf?
> Dann hätte man automatisch gleich Bindings für die ganze Gnome-welt.

Nun ja...

Nim ist mit Bedacht so gestaltet, dass  C-Bibliotheken gut aufgerufen 
werden können. Dazu gibt es das Tool c2nim, das C-Header verarbeitet und 
daraus einen Wrapper generiert. Das funktioniert auch ganz gut.

Im Frühsommer, als ich damit anfing, hatte ich im Nimrod-Forum gefragt, 
und mir war auch zu diesem Vorgehen geraten worden. Das GObject 
Introspection Zeugs habe ich bisher nicht wirklich verstanden. Ruby und 
Python benutzen es. C++ wohl aber noch nicht? Aber an C2nim ist 
eigentlich nicht viel auszusetzen. Man muss einige komplizierte Makros 
aus den Header Dateien entfernen, sonst kommt es damit nicht zurecht. 
Und später macht man dann aus gtk_widget_irgendwas(w: widget...) einfach 
irgendwas(w: widget...) usw. um ein objekt-orientiertes Feeling zu 
bekommen. Soweit recht einfach.

Object Introspection -- wie gesagt es erscheint mir doch recht 
kompliziert. Ich habe mich aber nicht sehr damit beschäftigt.

Übrigens, die neuesten Wrapper glib, gobject und Cairo sind jetzt auf 
GitHub, dass wollten die Nim-Leute gerne so haben: 
https://github.com/stefansalewski

Pango muss ich noch machen, und dann GTK3/GDK3 etwas überarbeiten...

Und später, viel später, kann man drüber nachdenken, wie man wirkliche 
High-Level Wrapper macht, die völlig kompatibel mit dem Nim GC sind. 
Aber das hat wirklich keine hohe Priorität, das Ref-Counting von GTK ist 
ja soweit ausreichend. Viele hätten ja eh lieber Qt als GUI Bibliothek, 
aber da hat sich aus dem Nim Umfeld noch keiner rangewagt. Und ich 
selbst bin kein so extremer Qt-Fan. Für Linux ist GTK nicht schlecht -- 
man muss es ja nicht von C aus benutzen.

von A. D. (egsler)


Lesenswert?

@ Karl Heinz & Lukas

Ahh, viielen Dank. Ja, so läuft das wunderbar =)
Ich dachte, ich müsste direkt ein Array-Element übergeben, aber so ist 
das ja viel praktischer, da kann ich direkt alle Buttons einer Spalte 
aktivieren/deaktivieren.

Ich würd schon gern bei C bleiben, da auch der Rest meiner Software 
schon in C steht. Da muss ich mich wohl durchkämpfen^^

von A. D. (egsler)


Lesenswert?

Achso, eine Frage habe ich ganz vergessen zu stellen.

Wenn ich untern den großen Knopf "Aufzeichnung starten" drücke soll, wie 
man sich wohl denken kann, mein Programmcode ausgeführt werden, mit dem 
meine Messdaten abgerufen werden. Darin möchte ich dann die Zustände der 
ganzen Buttons und Schalter abfragen können, wobei da eventuell noch 
welche hinzukommen.
Wie kann ich das denn realisieren, dass die dann aufgerufene Funktion 
Zugriff auf die ganzen Signale der GUI-Elemte hat? Oder muss ich die 
irgendwie beim Funktionsaufruf übergeben?

von Lukas K. (carrotindustries)


Lesenswert?

Julian S. schrieb:
> Ich würd schon gern bei C bleiben, da auch der Rest meiner Software
> schon in C steht. Da muss ich mich wohl durchkämpfen^^
Du kannst aus Python heraus recht bequem am vorhandenen C-Code 
anflanschen. ctypes oder die python c-api helfen dabei

Julian S. schrieb:
> Wie kann ich das denn realisieren, dass die dann aufgerufene Funktion
> Zugriff auf die ganzen Signale der GUI-Elemte hat? Oder muss ich die
> irgendwie beim Funktionsaufruf übergeben?

Du packst alle Gui-Elemente in eine struct-Variable und machst diese 
global oder übergibst sie der Callback-Funktion.

Jetzt ist allerdings der Zeitpunkt gekommen, wo man über Trennung von 
GUI/Anwendung nachdenken sollte. Statt dem obigen Vorschlag ist es 
besser, dass die Widgets durch ihre Callbacks ihren Wert in eine globale 
Struktur einspeichern. So musst du dich dann nicht mehr mit den 
Funktionen zum Auslesen der Widgets rumschlagen. So mal als Denkanstoß.

Auch hast du vielleicht gemerkt, dass es ein wenig mühselig ist, sich 
die GUI zusammemzuprogrammieren. Mit Glade hat GTK (imho) einen der 
besten GUI-Designer, da kannst du dir GUI zusammenklicken. Raus fällt 
eine XML, die du dann mit deinem Programmcode lädst. Ist in <5 Zeilen 
gemacht. Dinge, die mehrfach vorkommen, kannst du einmal in einem 
weiteren GtkWindow designen, und dann mit 
gtk_builder_add_objects_from_file aus den XML fischen. Wird z.B. in 
pavucontrol so gemacht (ist zwar C++, die Gtk-Methoden sind ja immer 
noch die selben).

von A. D. (egsler)


Lesenswert?

Lukas K. schrieb:
> Du kannst aus Python heraus recht bequem am vorhandenen C-Code
> anflanschen. ctypes oder die python c-api helfen dabei
Ist Python wirklich so viel einfacher?
Prinzipiell würd ich halt gerne bei C bleiben, allein schon da das auch 
in der Uni noch auf mich zu kommt...

: Bearbeitet durch User
von Salewski, Stefan (Gast)


Lesenswert?

Julian S. schrieb:
> Ist Python wirklich so viel einfacher?

Na Du kannst Dir ja mal (GTK) Tutorials in Python, Ruby und C ansehen. 
In C ist der Code deutlich länger, und wird dann schnell etwas 
unübersichtluch, insbesondere wenn man ihn oft umstellt. Aber C hat 
durchaus auch Vorteile -- kompakt, schnell, GTK Beispielcode findet man 
oft in C und kann ihn dann direkt übernehmen. Dass ist oft einfach auch 
Geschmacksache. Ich persönlich würde ein grösseres Programm nicht in C 
schreiben, einfach zu unübersichtlich und zu langwierig. Interpretierte 
Sprachen wie Python/Ruby sind natürlich eher langsam -- ich persönlich 
würde heute Nim oder Rust nehmen, dass scheinen mir die besten 
Kompromisse für den PC. (Für kleine uC ist C aber sicher eine gute 
Wahl.) Aber die Kids mögen heute ja eher Qt statt GTK.

von Lukas K. (carrotindustries)


Lesenswert?

Julian S. schrieb:
> Ist Python wirklich so viel einfacher?

Deutlich weniger boilerplate drum herum als bei C. Und Fehler wie der 
mit falschen Funktionsprototyp führen zu Laufzeitfehlern, fallen also 
auf. Wenn man dann z.b. mal ne hashtable braucht und drüber iterieren 
will:
C/GLib:
1
GHashTable *ht = g_hash_table_new(NULL, NULL);
2
//was einfügen...
3
GHashTableIter iter;
4
gpointer key, value;
5
6
g_hash_table_iter_init (&iter, ht);
7
while (g_hash_table_iter_next (&iter, &key, &value))
8
  {
9
    // do something with key and value
10
  }

Python:
1
ht={}
2
#was einfügen...
3
for key,value in ht.items() :
4
 #was mit key und value machen

Ich denke du siehst, worauf es hinausläuft.

Salewski, Stefan schrieb:
> GTK Beispielcode findet man
> oft in C und kann ihn dann direkt übernehmen.
Klar, Copy/paste geht nicht, aber spätestens seit GTK3 ist der 
unterschied zwischen C und python bei GTK fast nur noch kosmetischer 
Natur. Denn ob man nun
1
gtk_label_set_text(label, "Hallo welt")
oder
1
label.set_text("Hallo welt")
schreibt ist praktisch egal.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.