Code-Beispiel
GTK-Fenster mit GLib-Thread kombinieren
Lizenz: | Erster Autor: | Letzte Bearbeitung: |
GPLv3 | TJF | 24.05.2012 |
Bildschirmausgabe g_thread Beispiel
Diese kleine Beispiel betrifft die Anwendung von Threads in Kombination mit einem Ausgabefenster. Ein Thread ist ein Unterprogramm, welches selbstständig (quasi) zeitgleich mit dem Hauptprogramm ausgeführt wird.
Im Beispiel werden in zwei Threads Zähler in unterschiedlicher Geschwindigkeit heraufgezählt und das Ergebnis wird in einem GTK Fenster angezeigt. Gleichzeitig wird im Hauptprogramm die Betätigung der Buttons bearbeitet.
Threads können die Ausgabe in Fenstern des Hauptprogrammes stören, was durch Kapselung mit gdk_thread_enter() und gdk_thread_leave() verhindert wird. Diese Kapselung ist unter X11 (LINUX) nicht notwendig. Leider gibt es große Unterschiede zwischen den verschiedenen windows Versionen. Das Beispiel ist für XP und höher einzusetzen. Die GLib Dokumentation weist jedoch darauf hin, dass bei manchen XP Versionen Probleme mit dem Mutex-Lock auftreten können (bitte ggf. rückmelden). Alternativ kann auch die Verwendung von gdk_threads (timeout / idle) erwogen werden.
Das GUI wird mit dem Designer Glade3 erstellt und als GUI-XML-Datei gespeichert. Aus dieser Datei werden mit GladeToBac Vorlagen für den FB Quelltext erstellt und diese durch individuelle Funktionalität ergänzt. GladeToBac erzeugt dabei Quelltext, der in mehrere Dateien aufgeteilt ist, damit eine nachträgliche Ergänzung bzw. Veränderung ohne manuelle Nacharbeiten möglich wird. In diesem Beispiel wird der "tobac"-Ordner verwendet. Voraussetzung für das erfolgreiche Kompilieren ist die Verwendung von aktualisierten GLib / GTK+ Headern und folgende Dateistruktur:
Projekt -+- tobac -+- wwt_tobac.bas
| +- on_ButL_clicked.bas
| +- on_ButR_clicked.bas
|
+- wwt.bas
+- wwt.ui
tobac Ordner
wwt_tobac.bas (GladeToBac Steuerdatei. Wird automatisch erzeugt bzw. verändert.)
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< _tobac.bas modul generated by utility GladeToBac V3.0.3 >
'< Modul _tobac.bas erzeugt von >
'< Generated at / Generierung am 2012-05-23, 08:29 >
' -----------------------------------------------------------------------------
'< Main/Haupt Program name: wwt.bas >
'< Email: Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net >
' -----------------------------------------------------------------------------
'< declare names, signal handlers, load GUI-XML do not edit! >
'< deklariert Namen, Signale, laedt GUI-XML nicht veraendern! >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
TYPE GUIData
AS GtkBuilder PTR XML
AS GObject PTR _
window1, LabOL, LabOR, LabUL, LabUR
END TYPE
DIM SHARED AS GUIData GUI
GUI.XML = gtk_builder_new()
SCOPE
DIM AS GError PTR meld
IF 0 = gtk_builder_add_from_file(GUI.XML, "wwt.ui", @meld) THEN
WITH *meld
?"Fehler/Error (GTK-Builder):"
?*.message
END WITH
g_error_free(meld)
END 2
END IF
END SCOPE
WITH GUI
.window1 = gtk_builder_get_object(.XML, "window1")
.LabOL = gtk_builder_get_object(.XML, "LabOL")
.LabOR = gtk_builder_get_object(.XML, "LabOR")
.LabUL = gtk_builder_get_object(.XML, "LabUL")
.LabUR = gtk_builder_get_object(.XML, "LabUR")
END WITH
#INCLUDE "on_ButL_clicked.bas"
#INCLUDE "on_ButR_clicked.bas"
on_ButL_clicked.bas (Signal Handler für linken Button. Vorlage automatisch erzeugt, Quelltext individuell)
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< Signal handler generated by utility GladeToBac V3.0.3 >
'< Signal-Modul erzeugt von >
'< Generated at / Generierung am 2012-05-22, 19:37 >
' -----------------------------------------------------------------------------
'< Main/Haupt Program name: wwt.bas >
'< Email: Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net >
' -----------------------------------------------------------------------------
'< callback SUB/FUNCTION insert code! >
'< Ereignis Unterprogramm/Funktion Quelltext einfuegen! >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
SUB on_ButL_clicked CDECL ALIAS "on_ButL_clicked" ( _
BYVAL button AS GtkButton PTR, _
BYVAL user_data AS gpointer) EXPORT
STATIC AS BYTE flag = 0
STATIC AS INTEGER zaehler = 0
zaehler += 1
IF flag THEN
gtk_label_set_text(GTK_LABEL(GUI.LabUL), *__("Button 1 jetzt ") & zaehler & *__(" mal geklickt"))
flag = 0
ELSE
gtk_label_set_text(GTK_LABEL(GUI.LabUL), *__("Button 1 nun ") & zaehler & *__(" mal geklickt"))
flag = 1
END IF
END SUB
on_ButR_clicked.bas (Signal Handler für rechten Button)
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< Signal handler generated by utility GladeToBac V3.0.3 >
'< Signal-Modul erzeugt von >
'< Generated at / Generierung am 2012-05-22, 19:37 >
' -----------------------------------------------------------------------------
'< Main/Haupt Program name: wwt.bas >
'< Email: Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net >
' -----------------------------------------------------------------------------
'< callback SUB/FUNCTION insert code! >
'< Ereignis Unterprogramm/Funktion Quelltext einfuegen! >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
SUB on_ButR_clicked CDECL ALIAS "on_ButR_clicked" ( _
BYVAL button AS GtkButton PTR, _
BYVAL user_data AS gpointer) EXPORT
STATIC AS BYTE flag = 0
STATIC AS INTEGER zaehler = 0
zaehler += 1
IF flag THEN
gtk_label_set_text(GTK_LABEL(GUI.LabUR), *__("Button 2 jetzt ") & zaehler & *__(" mal geklickt"))
flag = 0
ELSE
gtk_label_set_text(GTK_LABEL(GUI.LabUR), *__("Button 2 nun ") & zaehler & *__(" mal geklickt"))
flag = 1
END IF
END SUB
Projekt Ordner
wwt.bas (Hauptdatei, diese wird kompiliert. Vorlage automatisch erzeugt, Quelltext individuell.)
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< main program generated by utility GladeToBac V3.0.3 >
'< Hauptprogramm erzeugt von >
'< Generated at / Generierung am 2012-05-22, 19:37 >
' -----------------------------------------------------------------------------
'< Program info: >
CONST PROJ_NAME = "wwt" ' >
CONST PROJ_DESC = "g_threads" ' >
CONST PROJ_VERS = "0.0" ' >
CONST PROJ_YEAR = "2012" ' >
CONST PROJ_AUTH = "" ' >
CONST PROJ_MAIL = "Thomas[ dot ]Freiherr[ at ]gmx[ dot ]net" ' >
CONST PROJ_WEBS = "" ' >
CONST PROJ_LICE = "" ' >
'< >
'< Description / Beschreibung: >
'< >
'< Example for GTK and GLib thread / Beispiel GTK Fenster und GLib Thread >
'< >
'< License / Lizenz: >
'< >
'< GPLv3 >
'< >
' -----------------------------------------------------------------------------
'< Please prefer GNU GENERAL PUBLIC LICENSE to support open software. >
'< For more information please visit: http://www.fsf.org >
'< >
'< Bitte bevorzugen Sie die GNU GENERAL PUBLIC LICENSE und >
'< unterstuetzen Sie mit Ihrem Programm die freie Software >
'< Mehr Informationen finden Sie unter: http://www.fsf.org >
' -----------------------------------------------------------------------------
'< GladeToBac: general init / Allgemeine Initialisierungen >
' #DEFINE __USE_GTK_3__ ' for new versions / new GTK+ Version >= 3.0 >
#INCLUDE "gtk/gtk.bi" ' GTK+library / GTK+ Bibliothek >
gtk_init(@__FB_ARGC__, @__FB_ARGV__) ' start GKT / GTK starten >
#INCLUDE "libintl.bi" ' load lib / Lib laden >
bindtextdomain(PROJ_NAME, EXEPATH & "/locale") ' path / Pfad >
bind_textdomain_codeset(PROJ_NAME, "UTF-8") ' set encoding / Zeichensatz >
textdomain(PROJ_NAME) ' Filename / Dateiname >
'< GladeToBac: end block / Blockende >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
g_thread_init(NULL)
gdk_threads_init()
#IFNDEF __FB_UNIX__
#LIBPATH "C:\opt\gtk\lib"
' under X11 we don't need this / Nicht benoetigt unter X11
gdk_threads_enter()
#ENDIF
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< GladeToBac: load GTK stuff / GTK Anbindung >
#INCLUDE "tobac/wwt_tobac.bas" ' Signale & GUI-XML >
'< GladeToBac: end block / Blockende >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
FUNCTION Timer1 CDECL(BYVAL Ud AS gpointer) AS gpointer
STATIC AS INTEGER ende = 200
FOR i AS INTEGER = 0 TO ende
gdk_threads_enter()
gtk_label_set_text(GTK_LABEL(GUI.LabOL), *__("Thread 1, Durchlauf ") & i & " (" & ende & ")")
gdk_threads_leave()
SLEEP 50
NEXT : RETURN NULL
END FUNCTION
FUNCTION Timer2 CDECL(BYVAL Ud AS gpointer) AS gpointer
STATIC AS INTEGER ende = 100
FOR i AS INTEGER = 0 TO ende
gdk_threads_enter()
gtk_label_set_text(GTK_LABEL(GUI.LabOR), *__("Thread 2, Durchlauf ") & i & " (" & ende & ")")
gdk_threads_leave()
SLEEP 70
NEXT : RETURN NULL
END FUNCTION
DIM AS GError PTR meld
VAR Thread1 = g_thread_create(@TIMER1, NULL, FALSE, @meld)
IF 0 = Thread1 THEN ? meld->message : g_error_free(meld)
VAR Thread2 = g_thread_create(@Timer2, NULL, FALSE, @meld)
IF 0 = Thread2 THEN ? meld->message : g_error_free(meld)
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'< GladeToBac: run GTK main, then end / GTK Hauptschleife, dann Ende >
gtk_builder_connect_signals(GUI.XML, 0) ' Signale anbinden >
gtk_widget_show_all(GTK_WIDGET(GUI.window1)) ' Hauptfenster darstellen >
gtk_main() ' main loop / Hauptschleife >
g_object_unref(GUI.XML) ' dereference / Referenz abbauen >
'< GladeToBac: end block / Blockende >
' vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#IFNDEF __FB_UNIX__
' under X11 we don't need this / Nicht benoetigt unter X11
gdk_threads_leave()
#ENDIF
END 0 ' finish with return code 0 / Ende mit Returncode 0
wwt.ui (GUI-XML-Datei, erstellt mit Glade3.)
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.24"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<signal name="delete-event" handler="gtk_main_quit" swapped="no"/>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="spacing">5</property>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">10</property>
<property name="row_spacing">10</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="LabOL">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
</child>
<child>
<object class="GtkLabel" id="LabOR">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="left_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="LabUL">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Button 1 noch nicht geklickt</property>
</object>
<packing>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="LabUR">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Button 2 noch nicht geklickt</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">Button 1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_ButL_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label" translatable="yes">Button 2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="on_ButR_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
English
See english forum thread.
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|