Indice
Hello world in gtk4 e linguaggio D usando Gid
Autore: Fabio Di Matteo
Ultima revisione: 27/08/2025 23:05
GObject introspection D binding package repository
giD Gtk4 Examples
dejan/gid-examples
Senza Builder
Creaiamo lo scheletro del nostro hello world con il comando:
dub init progetto gid:gtk4
Incolliamo in progetto/source/app.d:
app.d
import gio.types : ApplicationFlags; import gio.application; import glib.types : OptionArg, OptionFlags; import glib.variant; import gobject.object; import gobject.param_spec; import gtk.application; import gtk.application_window; import gtk.builder; import std.stdio; class finestraPrincipale : ApplicationWindow { this(gtk.application.Application app) //costruttore { super(app); setTitle("Main Window"); setDefaultSize(600, 800); } } class applicazione : gtk.application.Application { finestraPrincipale MainWindow; this() //costruttore { super("org.example.App", ApplicationFlags.DefaultFlags); connectStartup(&onStartup); connectActivate(&onActivate); } void onStartup() { writeln("on startup..."); } void onActivate() { writeln("on activate..."); MainWindow = new finestraPrincipale(this); MainWindow.present(); } } void main(string[] args) { writeln("Apposto..."); auto myapp = new applicazione(); myapp.run(args); }
dub.json
{
"authors": [
"fabio"
],
"copyright": "Copyright © 2025, fabio",
"dependencies": {
"gid:gtk4": "~>0.9.7"
},
"description": "A minimal D application.",
"license": "MIT",
"name": "gtk4"
}
Compilare l'applicazione con :
dub
Con Builder
Creo da codice un'application window e poi carico dall'xml soltanto il container(grid0) e lo aggiungo come figlio principale dell'application window.
import gio.types : ApplicationFlags; import gio.application; import glib.types : OptionArg, OptionFlags; import glib.variant; import gobject.object; import gobject.param_spec; import gtk.application; import gtk.window; import gtk.grid; import gtk.application_window; import gtk.builder; import std.stdio; class win :ApplicationWindow { private Grid grid0; this(gtk.application.Application app) { super(app); auto builder = new Builder(); builder.addObjectsFromFile("gui.ui", ["grid0"]); grid0=cast(Grid)builder.getObject("grid0"); setTitle("Finestra principale"); setChild(grid0); setDefaultSize(600, 800); } } class applicazione : gtk.application.Application { win MainWindow; this() //costruttore { super("org.example.App", ApplicationFlags.DefaultFlags); connectStartup(&onStartup); connectActivate(&onActivate); } void onStartup() { writeln("on startup..."); } void onActivate() { writeln("on activate..."); MainWindow = new win(this); MainWindow.present(); } } void main(string[] args) { auto myapp = new applicazione(); myapp.run(args); }
Usare le risorse di Glib per includere il file dell'interfaccia grafica dentro l'eseguibile
Grazie al comando glib-compile-resources creeremo un sorgente C (resources.c) che poi compileremo in codice oggetto con gcc. Il file prodotto, resources.o verra passato al linker tramite dub package manager. Questi comandi li ho raggruppati in un pratico makefile che viene avviato da dub mediante la direttiva “preBuildCommands” .
Il mio progetto
Ecco l'alberatura delle cartelle:
.
├── build
│ └── sveglia
├── dub.json
├── dub.selections.json
└── source
├── app.d
├── gui.glade
├── gui.glade~
├── gui.ui
├── makefile
├── resources.c
├── resources.o
└── resources.xml
makefile
all: rm -f source/resources.o source/resources.c gtk4-builder-tool simplify --3to4 source/gui.glade > source/gui.ui glib-compile-resources --sourcedir=source/ --generate-source source/resources.xml cd source && gcc -c resources.c `pkg-config --cflags --libs glib-2.0`
Inoltre come si puo' vedere dalla prima riga converto il file creato con glade 3.x a file per gtk4 builder.
resources.xml
<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/org/sveglia/risorse"> <file>gui.ui</file> </gresource> </gresources>
glib-compile-resources –sourcedir=source/ –generate-source source/resources.xml ha come parametro –sourcedir=source/, il che indica a glib-compile-resources in quale cartella si trovano i file. In questo caso in source/
dub.json
{
"authors": [
"fabio"
],
"copyright": "Copyright © 2025, fabio",
"dependencies": {
"gid:gtk4": "~>0.9.7",
},
"description": "A minimal D application.",
"license": "GPL-3.0-only",
"name": "sveglia",
"targetType":"executable",
"targetPath": "./build",
"libs":["gio-2.0", "glib-2.0"],
"preBuildCommands": ["make -f source/makefile"],
"sourceFiles": ["source/app.d", "source/resources.o"],
"lflags-windows-x86_64":["-subsystem:windows"]
}
E' importante notare la direttiva “libs”:[“gio-2.0”, “glib-2.0”], che passa al compilatore e al linker i giusti parametri per le librerie C che si occupano di includere le risorse dentro l'eseguibile. Da notare che tali parametri sono presi tramite pkg-config in maniera trasparente. Altra cosa importante da notare è la direttiva “sourceFiles”: [“source/app.d”, “source/resources.o”] che aggiunge oltre al file sorgente principale anche il file delle risorse generato dal makefile.
“lflags-windows-x86_64” invece compila l'applicazione ,solo su windows, in modalità grafica. Vedere:Crosscompilare applicazione Gtk4 per windows
Il sorgente da compilare
app.d
import gio.types : ApplicationFlags; import gio.application; import gio.resource; import glib.types : OptionArg, OptionFlags; import glib.variant; import gobject.object; import gobject.param_spec; import gtk.application; import gtk.window; import gtk.grid; import gtk.application_window; import gtk.builder; import std.stdio; import std.file; import std.path; class win :ApplicationWindow { private Grid grid0; private string gui; this(gtk.application.Application app) { super(app); auto builder = new Builder(); auto guiPath = dirName(thisExePath())~"/gui.ui"; //builder.addObjectsFromFile(guiPath, ["grid0"]); builder.addObjectsFromResource("/org/sveglia/risorse/gui.ui", ["grid0"]); grid0=cast(Grid)builder.getObject("grid0"); setTitle("Finestra principale"); setChild(grid0); setDefaultSize(600, 800); } } class applicazione : gtk.application.Application { win MainWindow; this() //costruttore { super("org.example.App", ApplicationFlags.DefaultFlags); connectStartup(&onStartup); connectActivate(&onActivate); } void onStartup() { writeln("on startup..."); } void onActivate() { writeln("on activate..."); MainWindow = new win(this); MainWindow.present(); } } void main(string[] args) { auto myapp = new applicazione(); myapp.run(args); }
Linkare staticamente libgphobos
Se si vuole includere libgphobos (sto usando gdc) è possibile farlo seguendo le indicazioni a questa pagina: Creare eseguibili statici con dub package manager .
Oppure aggiungere a dub.json la direttiva:
"dflags":["-static-libphobos"]
In questa maniera il nostro eseguibile si porta dientro tutte le dipendenze legate al linguaggio D e non ha bisogno di altro se non altro le stesse dipendenze che ha una normale applicazione gtk4 in C. Che già così sono moltissime.
Usare g_idle_add()
Per interagire sui widget da un thread diverso dal principale possiamo usare,come in C, idleAdd. Ecco come:
import glib.global; ... bool test() { this.setTitle("Test idle add"); writeln("Test idle add"); return false; } ... idleAdd(1, &test);
Usare il file fileDialog di gtk4
import gtk.file_dialog; void on_file_choose(ObjectWrap ob, AsyncResult res) { string fn=fdialog.openFinish(res).getPath(); writeln("File: ",fn); } void onFileDialogOpen() { fdialog.setModal(true); fdialog.open(this,null,&on_file_choose); } ... auto fdialog = new FileDialog();
Semplice messagebox ricavata da una window
void MessageBox(Window p, string title, string label) { auto msgBox = new Window(); msgBox.title = title; msgBox.setModal(true); msgBox.setTransientFor(p); auto vbox = new Box(Orientation.Vertical, 4); vbox.marginTop = 4; vbox.marginBottom = 4; vbox.marginStart = 4; vbox.marginEnd = 4; msgBox.setChild(vbox); vbox.append(new Label(label)); auto btOk= new Button(); btOk.setLabel("Ok"); btOk.connectClicked((){ msgBox.destroy(); }); vbox.append(btOk); msgBox.present; }
Dove p è finestra genitore, title e label il testo della finestra e quello interno alla messagebox.
Creare una menubar
Menubar e action vengono create nella definizione della nostro oggetto gtk.application.Application non nella classe delle finestra principale.
import gio.types : ApplicationFlags; import gio.application; import glib.types : OptionArg, OptionFlags; import glib.variant; import gobject.object; import gobject.param_spec; import gtk.application; import gtk.application_window; import gtk.builder; import gio.menu; import gio.simple_action; import std.stdio; class finestraPrincipale : ApplicationWindow { this(gtk.application.Application app) { super(app); setTitle("Main Window"); setDefaultSize(600, 600); setShowMenubar = true; } } class applicazione : gtk.application.Application { finestraPrincipale MainWindow; enum MENU_XML = `<?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menubar"> <submenu> <attribute name="label" translatable="yes">File</attribute> <section> <item> <attribute name="action">app.myAction</attribute> <attribute name="label" translatable="yes">_About</attribute> </item> </section> </submenu> </menu> </interface> `; this() { super("org.example.App", ApplicationFlags.DefaultFlags); connectStartup(&onStartup); connectActivate(&onActivate); } void on_menu_item_activated(glib.variant.Variant v, SimpleAction simpleAction) { writeln("Hello world!"); MainWindow.setTitle("Hello world! "); } void onStartup() { writeln("on startup..."); auto myAction = new SimpleAction("myAction", null); myAction.connectActivate(&on_menu_item_activated); addAction(myAction); auto builder = Builder.newFromString(MENU_XML); setMenubar(cast(Menu)builder.getObject("menubar")); } void onActivate() { writeln("on activate..."); MainWindow = new finestraPrincipale(this); MainWindow.present(); } } void main(string[] args) { auto myapp = new applicazione(); myapp.run(args); }
Non far comparire la finestra di console su Widows quando lanci la tua applicazione
Devo dire a windows che uso il subsystem per le applicazioni grafiche attraverso l'entrypoint del WinMain. Dunque riformulare la main() come segue:
version(Windows) { import core.runtime; import core.sys.windows.windows; } ... version(Posix){ void main(string[] args) { auto myapp = new app(); myapp.run(args); } } version(Windows){ extern(Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int result; try { Runtime.initialize(); result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); Runtime.terminate(); } catch (Throwable e) { //MessageBoxA(null, e.toString().toStringz(), null,MB_ICONEXCLAMATION); result = 0; // failed } return result; } int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { auto myapp = new app(); return myapp.run(null); } }
Aggiungere in dub.json la seguente voce:
... "lflags-windows-x86_64": ["-subsystem:windows"] ...
