forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
3.6 KiB
166 lines
3.6 KiB
package xkcd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/layout"
|
|
"fyne.io/fyne/v2/widget"
|
|
)
|
|
|
|
// XKCD is an app to get xkcd images and display them
|
|
type XKCD struct {
|
|
ID int `json:"num"`
|
|
Title string `json:"title"`
|
|
Day string `json:"day"`
|
|
Month string `json:"month"`
|
|
Year string `json:"year"`
|
|
Link string `json:"link"`
|
|
SafeTitle string `json:"safe_title"`
|
|
Transcript string `json:"transcript"`
|
|
News string `json:"news"`
|
|
Alt string `json:"alt"`
|
|
Img string `json:"img"`
|
|
|
|
image *canvas.Image
|
|
iDEntry *widget.Entry
|
|
labels map[string]*widget.Label
|
|
}
|
|
|
|
func (x *XKCD) newLabel(name string) *widget.Label {
|
|
w := widget.NewLabel("")
|
|
x.labels[name] = w
|
|
return w
|
|
}
|
|
|
|
// NewXKCD returns a new xkcd app
|
|
func NewXKCD() *XKCD {
|
|
rand.Seed(time.Now().UnixNano())
|
|
return &XKCD{
|
|
labels: make(map[string]*widget.Label),
|
|
}
|
|
}
|
|
|
|
// Submit will lookup the xkcd cartoon and do something useful with it
|
|
func (x *XKCD) Submit() {
|
|
// Get the ID
|
|
id, _ := strconv.Atoi(x.iDEntry.Text)
|
|
if id == 0 {
|
|
id = rand.Intn(2075)
|
|
}
|
|
|
|
resp, err := http.Get(fmt.Sprintf("https://xkcd.com/%d/info.0.json", id))
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode == http.StatusOK {
|
|
data, _ := ioutil.ReadAll(resp.Body)
|
|
json.Unmarshal(data, x)
|
|
x.DataToScreen()
|
|
} else {
|
|
fmt.Println("Error getting ID", id, resp.Status, resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func (x *XKCD) downloadImage(url string) {
|
|
response, e := http.Get(url)
|
|
if e != nil {
|
|
log.Fatal(e)
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
file, err := ioutil.TempFile(os.TempDir(), "xkcd.png")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = io.Copy(file, response.Body)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
x.image.File = file.Name()
|
|
canvas.Refresh(x.image)
|
|
}
|
|
|
|
// DataToScreen copies the data model to the screen
|
|
func (x *XKCD) DataToScreen() {
|
|
myType := reflect.TypeOf(x).Elem()
|
|
myValue := reflect.ValueOf(x).Elem()
|
|
for i := 0; i < myType.NumField(); i++ {
|
|
tag := myType.Field(i).Tag.Get("json")
|
|
switch tag {
|
|
case "": // not a display field
|
|
case "img": // special field for images
|
|
url := myValue.Field(i).String()
|
|
|
|
go x.downloadImage(url)
|
|
case "num":
|
|
v := myValue.Field(i).Int()
|
|
x.iDEntry.SetText(fmt.Sprintf("%d", v))
|
|
default:
|
|
v := myValue.Field(i).String()
|
|
if newline := strings.IndexAny(v, "\n.-,"); newline > -1 {
|
|
v = v[:newline] + "..."
|
|
}
|
|
x.labels[tag].SetText(v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// NewForm generates a new XKCD form
|
|
func (x *XKCD) NewForm(w fyne.Window) fyne.Widget {
|
|
form := &widget.Form{}
|
|
tt := reflect.TypeOf(x).Elem()
|
|
for i := 0; i < tt.NumField(); i++ {
|
|
fld := tt.Field(i)
|
|
tag := fld.Tag.Get("json")
|
|
switch tag {
|
|
case "": // not a display field
|
|
case "img": // special field for images
|
|
// we created this in the setup
|
|
case "num": // special field for ID
|
|
entry := widget.NewEntry()
|
|
x.iDEntry = entry
|
|
form.Append(fld.Name, entry)
|
|
default:
|
|
form.Append(fld.Name, x.newLabel(tag))
|
|
}
|
|
}
|
|
return form
|
|
}
|
|
|
|
// Show starts a new xkcd widget
|
|
func Show(win fyne.Window) fyne.CanvasObject {
|
|
x := NewXKCD()
|
|
|
|
form := x.NewForm(win)
|
|
submit := widget.NewButton("Submit", func() {
|
|
x.Submit()
|
|
})
|
|
submit.Importance = widget.HighImportance
|
|
buttons := container.NewHBox(
|
|
layout.NewSpacer(),
|
|
widget.NewButton("Random", func() {
|
|
x.iDEntry.Text = ""
|
|
x.Submit()
|
|
}),
|
|
submit)
|
|
x.image = &canvas.Image{FillMode: canvas.ImageFillOriginal}
|
|
return container.NewBorder(form, buttons, nil, nil, x.image)
|
|
}
|
|
|