---
id: 974f36e12dfcece861bfda1af9303980
author: Yunjin Lee
title: 'Implémentation d''une interface graphique Tcl/Tk en Go sans CGo (modernc.org/tk9.0) : gestion du placement des widgets et rendu d''images'
description: Mise en œuvre de la création de fenêtres, du placement de widgets de bouton et de barre de menus, ainsi que du rendu d'images SVG, PNG et ICO en Go, à l'aide de la bibliothèque multiplateforme modernc.org/tk9.0, sans dépendance CGo.
language: fr
date: 2024-10-22T11:51:27.614782351Z
path: /blog/posts/creating-a-gui-with-tk-in-go-z004dd008
category: gui
lang_canonical:
    ko: https://blog.naver.com/bugaku/223629101405
---

Python est livré avec des bibliothèques GUI intégrées par défaut, comme Tkinter.
Récemment, la bibliothèque [CGo-Free, Cross Platform Tk](https://pkg.go.dev/modernc.org/tk9.0) a été développée pour permettre d'utiliser Tcl/Tk depuis le langage Go. Aujourd'hui, nous allons examiner ses bases d'utilisation.

## Créer un Hello, Tk

Commençons par un exemple simple « Hello, TK! ».

```go
package main

import tk "modernc.org/tk9.0"

func main() {
    tk.Pack(
        tk.TButton(
            tk.Txt("Hello, TK!"),
            tk.Command(func() {
                tk.Destroy(tk.App)
            })),
        tk.Ipadx(10), tk.Ipady(5), tk.Padx(15), tk.Pady(10),
    )
    tk.App.Wait()
}
```

![résultat d'exécution de hello-tk](/assets/images/go-tk-helloworld-svg-etc/go-tk-hello.png)

---

Examinons en détail le code d'exemple ci-dessus et son résultat d'exécution.

Si vous avez déjà utilisé Tk avec Python, vous comprendrez la structure où les widgets sont placés (packed) dans une fenêtre ou directement sous la fenêtre. Selon le type de widget, des étiquettes (labels) et autres éléments y sont inclus.

_Ipadx et Ipady sont les abréviations de Internal padding (marge interne) ; ils ajustent les marges internes des widgets. Dans cet exemple, ce sont les marges du bouton qui sont ajustées._

Cette bibliothèque possède une structure Window, et la variable App gère la fenêtre de niveau supérieur. Celle-ci est prédéfinie à l'intérieur de la bibliothèque. Ainsi, la fonction tk.App.Destroy(), qui met fin à tk.App.Wait(), joue le rôle de fermer la fenêtre de niveau supérieur.

Examinons maintenant quelques exemples présents dans le dossier \_examples de GitLab.

## Traiter des fichiers SVG

Voici un exemple qui affiche un fichier SVG dans un widget Label.

```go
package main

import . "modernc.org/tk9.0"

// https://en.wikipedia.org/wiki/SVG
const svg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="391" height="391" viewBox="-70.5 -70.5 391 391" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect fill="#fff" stroke="#000" x="-70" y="-70" width="390" height="390"/>
<g opacity="0.8">
    <rect x="25" y="25" width="200" height="200" fill="lime" stroke-width="4" stroke="pink" />
    <circle cx="125" cy="125" r="75" fill="orange" />
    <polyline points="50,150 50,200 200,200 200,100" stroke="red" stroke-width="4" fill="none" />
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" />
</g>
</svg>`

func main() {
    Pack(Label(Image(NewPhoto(Data(svg)))),
        TExit(),
        Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
    App.Center().Wait()
}
```

![résultat d'exécution de go-tk-svg](/assets/images/go-tk-helloworld-svg-etc/go-tk-svg.png)

La manière dont cette bibliothèque traite le SVG est la suivante :

1. Lire le contenu du fichier SVG sous forme de chaîne de caractères (ou l'inclure directement, comme dans l'exemple ci-dessus).
2. Transmettre ce contenu à la fonction Data pour le convertir en chaîne de caractères contenant les options (option -data).
3. Les octets convertis sont transmis à la fonction NewPhoto, qui renvoie un pointeur vers la structure Img représentant une image Tcl/Tk.
4. En passant par la fonction Image, le pointeur de la structure Img est converti en chaîne de caractères à laquelle est ajoutée l'option -Image.
5. La conversion en chaîne contenant la valeur RAW de la structure a pour but la création du widget Label.

Les fichiers ICO et PNG sont traités de manière similaire.

## Traiter des fichiers PNG

```go
package main

import _ "embed"
import . "modernc.org/tk9.0"

//go:embed gopher.png
var gopher []byte

func main() {
    Pack(Label(Image(NewPhoto(Data(gopher)))),
        TExit(),
        Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
    App.Center().Wait()
}
```

![résultat d'exécution de go-tk-png](/assets/images/go-tk-helloworld-svg-etc/go-tk-png.png)

Le processus de traitement d'un fichier PNG est le suivant :

1. Convertir le fichier gopher.png intégré en un type chaîne contenant les options.
2. Convertir en type \*Img via la fonction NewPhoto.
3. Passer par la fonction Image, puis convertir en chaîne RAW avant de créer le widget Label.

Les fichiers ICO sont traités de la même manière ; la seule différence avec le format SVG réside dans le traitement interne de la fonction Data.

Examinons maintenant la nature de la « chaîne contenant les options » :

```go
type rawOption string
```

La chaîne contenant les options mentionnée précédemment n'est qu'une simple chaîne formatée.

```go
func (w *Window) optionString(_ *Window) string {
    return w.String()
}
```

La méthode optionString est une méthode du pointeur Window qui renvoie une chaîne de caractères.

Enfin, examinons brièvement la structure interne de la fonction Data :

```go
func Data(val any) Opt {
    switch x := val.(type) {
    case []byte:
        switch {
        case bytes.HasPrefix(x, pngSig):
            // ok
        case bytes.HasPrefix(x, icoSig):
            b := bytes.NewBuffer(x)
            img, err := ico.Decode(bytes.NewReader(x))
            if err != nil {
                fail(err)
                return rawOption("")
            }

            b.Reset()
            if err := png.Encode(b, img); err != nil {
                fail(err)
                return rawOption("")
            }

            val = b.Bytes()
        }
    }
    return rawOption(fmt.Sprintf(`-data %s`, optionString(val)))
}
```

Comme le montre le code, les fichiers ICO et PNG nécessitent un processus d'encodage/décodage.
Dans les autres cas, aucune conversion particulière n'est effectuée : seule l'option -data est ajoutée à la chaîne convertie en octets, afin d'indiquer qu'il s'agit du résultat de la fonction Data.

### Charger une image avec un widget de menu

En ajoutant un widget de menu aux exemples de chargement de PNG et d'ICO vus précédemment, on peut créer une application qui affiche l'image souhaitée.

Commençons par un exemple simple de widget de menu.

```go
package main

import (
    "fmt"
    . "modernc.org/tk9.0"
    "runtime"
)

func main() {
    menubar := Menu()

    fileMenu := menubar.Menu()
    fileMenu.AddCommand(Lbl("New"), Underline(0), Accelerator("Ctrl+N"))
    fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(func() { GetOpenFile() }))
    Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(1) }))
    fileMenu.AddCommand(Lbl("Save As..."), Underline(5))
    fileMenu.AddSeparator()
    fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler())
    Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(4) }))
    menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu))

    App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS))
    App.Configure(Mnu(menubar), Width("8c"), Height("6c")).Wait()
}
```

![résultat d'exécution de go-tk-barre-de-menu](/assets/images/go-tk-helloworld-svg-etc/go-tk-menu-bar.png)

Dans cet exemple, nous avons essayé la barre de menus et la création de menus, la mise en évidence de caractères, l'option Command, la liaison de raccourcis clavier, ainsi que la définition de la taille initiale de la fenêtre de l'application.

Modifions maintenant la fonction Command spécifiée pour GetOpenFile afin de créer un programme qui charge et affiche une image.

Le plan de réalisation du programme est le suivant :

1. Limiter l'ouverture aux seuls fichiers PNG et ICO
2. Traiter le fichier sélectionné dans la boîte de dialogue de fichier
3. Implémenter un widget d'affichage de l'image

Voici le code qui met en œuvre ce plan :

```go
package main

import (
    "fmt"
    "log"
    "os"
    "runtime"
    "strings"

    . "modernc.org/tk9.0"
)

func handleFileOpen() {
    s := GetOpenFile()
    for _, photo := range s {
        formatCheck := strings.Split(photo, ".")
        format := formatCheck[len(formatCheck)-1]

        if (strings.Compare(format, "png") == 0) || (strings.Compare(format, "ico") == 0) {
            picFile, err := os.Open(photo)
            if err != nil {
                log.Println("Error while opening photo, error is: ", err)
            }

            pictureRawData := make([]byte, 10000*10000)
            picFile.Read(pictureRawData)

            imageLabel := Label(Image(NewPhoto(Data(pictureRawData))))
            Pack(imageLabel,
                TExit(),
                Padx("1m"), Pady("2m"), Ipadx("1m"), Ipady("1m"))
        }
        picFile.Close()
    }
}

func main() {
    menubar := Menu()

    fileMenu := menubar.Menu()
    fileMenu.AddCommand(Lbl("Open..."), Underline(0), Accelerator("Ctrl+O"), Command(handleFileOpen))
    Bind(App, "<Control-o>", Command(func() { fileMenu.Invoke(0) }))
    fileMenu.AddCommand(Lbl("Exit"), Underline(1), Accelerator("Ctrl+Q"), ExitHandler())
    Bind(App, "<Control-q>", Command(func() { fileMenu.Invoke(1) }))
    menubar.AddCascade(Lbl("File"), Underline(0), Mnu(fileMenu))

    App.WmTitle(fmt.Sprintf("%s on %s", App.WmTitle(""), runtime.GOOS))
    App.Configure(Mnu(menubar), Width("10c"), Height("10c")).Wait()
}
```

![résultat d'exécution de go-tk-ouverture-image](/assets/images/go-tk-helloworld-svg-etc/go-tk-image-open.png)

Le code ci-dessus fonctionne de la manière suivante :

1. Vérifier l'extension du fichier à l'aide des fonctions de comparaison de chaînes du package strings
2. Ouvrir, lire, puis fermer le fichier à l'aide du package os
3. L'image lue est affichée dans un widget Label

## Conclusion

Dans cet article, nous avons traité, à l'aide de la bibliothèque Tcl/Tk pour Go, les points suivants :

1. La structure de base d'une application GUI
2. Le traitement de différents formats d'image tels que SVG, PNG et ICO
3. Le placement des widgets (packing) et la gestion de la mise en page
4. La structure de traitement des données d'image
5. La liaison de raccourcis clavier et les commandes des widgets

Utiliser ensemble le langage Go et Tcl/Tk permet de créer des programmes GUI simples mais utiles. Nous vous encourageons à vous lancer ensuite dans le développement d'applications GUI plus complexes à partir de ces bases.
