1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
package gui
import (
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"os"
"path/filepath"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
// ImageDisplay is a custom widget for displaying images
type ImageDisplay struct {
widget.BaseWidget
container *fyne.Container
imageCanvas *canvas.Image
imageLabel *widget.Label
currentImage string
}
// NewImageDisplay creates a new image display widget
func NewImageDisplay() *ImageDisplay {
d := &ImageDisplay{}
// Create image canvas
d.imageCanvas = canvas.NewImageFromResource(nil)
d.imageCanvas.FillMode = canvas.ImageFillContain
d.imageCanvas.SetMinSize(fyne.NewSize(200, 150)) // Half the size
// Create label
d.imageLabel = widget.NewLabel("No image")
d.imageLabel.Alignment = fyne.TextAlignCenter
// Create main container - no navigation buttons here
d.container = container.NewBorder(
nil,
d.imageLabel,
nil, nil,
d.imageCanvas,
)
d.ExtendBaseWidget(d)
return d
}
// CreateRenderer implements fyne.Widget
func (d *ImageDisplay) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(d.container)
}
// SetImage sets a single image to display
func (d *ImageDisplay) SetImage(imagePath string) {
if imagePath == "" {
d.Clear()
return
}
d.currentImage = imagePath
// Load image from file
file, err := os.Open(imagePath)
if err != nil {
d.imageLabel.SetText(fmt.Sprintf("Error loading image: %v", err))
return
}
defer file.Close()
// Get file info to ensure it's fully written
stat, err := file.Stat()
if err != nil {
d.imageLabel.SetText(fmt.Sprintf("Error getting file info: %v", err))
return
}
// If file size is 0, it might still be writing
if stat.Size() == 0 {
d.imageLabel.SetText("Image file is empty")
return
}
img, format, err := image.Decode(file)
if err != nil {
d.imageLabel.SetText(fmt.Sprintf("Error decoding image: %v", err))
return
}
// Update canvas
d.imageCanvas.Image = img
d.imageCanvas.Refresh()
// Update label with format info
d.imageLabel.SetText(fmt.Sprintf("%s (%s)", filepath.Base(imagePath), format))
}
// SetImages sets multiple images but only displays the first one
func (d *ImageDisplay) SetImages(images []string) {
if len(images) > 0 {
d.SetImage(images[0])
} else {
d.Clear()
}
}
// Clear clears the display
func (d *ImageDisplay) Clear() {
d.currentImage = ""
d.imageCanvas.Image = nil
d.imageCanvas.Refresh()
d.imageLabel.SetText("No image")
}
// SetGenerating shows a generating status
func (d *ImageDisplay) SetGenerating() {
d.currentImage = ""
d.imageCanvas.Image = nil
d.imageCanvas.Refresh()
d.imageLabel.SetText("Generating...")
}
// ResourceFromPath creates a Fyne resource from a file path
func ResourceFromPath(path string) (fyne.Resource, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return fyne.NewStaticResource(filepath.Base(path), data), nil
}
|