我一开始一直在研究单窗口的布局规范和方式,后面我慢慢意识到了一件事情,多窗口怎么互相关联呢,任务之间的数据该怎么传输呢,于是乎我在四月份新发表的multiple windows的example找到了答案。
官方是通过用ctx(一个很让人头疼的东西,简单的说就是上下文的联系)来进行信息传导的,之前报了毛大的课学习架构的时候,一直不明白上下文到底什么意思,结果在研究gio的时候慢慢理解了它的作用。
这次我通过multiple windows写出了一个后端简洁监控系统,我其实很早就完成了,但是我是拿来当作毕业设计的,所以最近才决定把代码发出来,里面有一块地方我不算展示,也就是cardtoken那块的运算代码,那是我之前写某个网站js逆向出来的代码。其实还是有一点值得大家注意,就还是变量作用域的问题,这玩意真的是有点恶心的。
main.go
package main
import (
"context"
"gioui.org/app"
"gioui.org/font/gofont"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
"gioui.org/widget/material"
"os"
"os/signal"
"sync"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
go func() {
a := NewApplication(ctx)
login_in := NewLogin()
a.NewWindow("Login_In", login_in, app.Size(unit.Dp(400),unit.Dp(200)))
a.Wait()
select {
case <-login_in.Check:
grpc_info := NewGrpc()
go Open_Server(grpc_info)
a.NewWindow("Monitoring_System", grpc_info)
}
a.Wait()
os.Exit(0)
}()
app.Main()
}
type Application struct {
Context context.Context
Shutdown func()
active sync.WaitGroup
}
func NewApplication(ctx context.Context) *Application {
ctx, cancel := context.WithCancel(ctx)
return &Application{
Context: ctx,
Shutdown: cancel,
}
}
func (a *Application) Wait() {
a.active.Wait()
}
func (a *Application) NewWindow(title string, view View, opts ...app.Option) {
opts = append(opts, app.Title(title))
w := &Window{
App: a,
Window: app.NewWindow(opts...),
}
a.active.Add(1)
go func() {
defer a.active.Done()
view.Run(w)
}()
}
type Window struct {
App *Application
*app.Window
}
type View interface {
Run(w *Window) error
}
type WidgetView func(gtx layout.Context, th *material.Theme) layout.Dimensions
func (view WidgetView) Run(w *Window) error {
var ops op.Ops
th := material.NewTheme(gofont.Collection())
applicationClose := w.App.Context.Done()
for {
select {
case <-applicationClose:
return nil
case e := <-w.Events():
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
view(gtx, th)
e.Frame(gtx.Ops)
}
}
}
}
monitoring,go
package main
import (
"fmt"
"gioui.org/font/gofont"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/widget"
"gioui.org/widget/material"
)
type Monitoring_Info struct {
list widget.List
addLine chan string
lines []string
}
func NewGrpc() *Monitoring_Info {
return &Monitoring_Info{
addLine: make(chan string, 100),
list: widget.List{List: layout.List{Axis: layout.Vertical}},
}
}
func (monitor *Monitoring_Info)Run(w *Window) error{
th := material.NewTheme(gofont.Collection())
var ops op.Ops
applicationClose := w.App.Context.Done()
for {
select {
case <-applicationClose:
return nil
case line := <-monitor.addLine:
monitor.lines = append(monitor.lines, line)
w.Invalidate()
case e := <-w.Events():
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
monitor.Layout(gtx, th)
e.Frame(gtx.Ops)
}
}
}
}
func (monitor *Monitoring_Info) Printf(format string, args ...interface{}) {
s := fmt.Sprintf(format, args...)
select {
case monitor.addLine <- s:
default:
}
}
func (monitor Monitoring_Info)Layout(gtx layout.Context, th *material.Theme){
layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return material.List(th, &monitor.list).Layout(gtx, len(monitor.lines), func(gtx layout.Context, i int) layout.Dimensions {
return material.Body1(th, monitor.lines[i]).Layout(gtx)
})
}),
)
}
login_windows.go
package main
import (
"gioui.org/app"
"gioui.org/font/gofont"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"image/color"
)
type Login_in struct {
postBtn widget.Clickable
list widget.List
info Information
App *Application
Check chan bool
}
func NewLogin() *Login_in {
return &Login_in{
info: Information{},
Check: make(chan bool, 1),
list: widget.List{List: layout.List{Axis: layout.Vertical}},
}
}
func (login *Login_in)Run(w *Window) error{
th := material.NewTheme(gofont.Collection())
var ops op.Ops
login.App = w.App
applicationClose := w.App.Context.Done()
for{
select {
case <-applicationClose:
return nil
case e:= <-w.Events():
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
//if login.postBtn.Clicked(){
// check := Check_Account(login.info.AccountNum.Text(),login.info.Passport.Text())
// if check {
// w.Window.Perform(system.ActionClose)
// login.Check <- true
// return nil
// }else {
// login.App.NewWindow("login failed",
// WidgetView(func(gtx layout.Context, th *material.Theme) layout.Dimensions {
// return layout.Center.Layout(gtx, material.H3(th, "login failed").Layout)
// }),
// app.Size(unit.Dp(400),unit.Dp(150)),
// )
// }
//}
login.Layout(gtx, th, w)
e.Frame(gtx.Ops)
}
}
}
}
type Information struct {
AccountNum Feild
Passport Feild
}
type Feild struct {
widget.Editor
}
func (login *Login_in)Layout(gtx layout.Context, th *material.Theme, w *Window) layout.Dimensions {
spacer := layout.Rigid(layout.Spacer{Width: unit.Dp(10)}.Layout)
if login.postBtn.Clicked(){
check := Check_Account(login.info.AccountNum.Text(),login.info.Passport.Text())
if check {
w.Window.Perform(system.ActionClose)
login.Check <- true
}else {
login.App.NewWindow("login failed",
WidgetView(func(gtx layout.Context, th *material.Theme) layout.Dimensions {
return layout.Center.Layout(gtx, material.H3(th, "login failed").Layout)
}),
app.Size(unit.Dp(400),unit.Dp(150)),
)
}
}
widgets := []layout.Widget{
func(gtx layout.Context) layout.Dimensions {
return layout.Flex{}.Layout(gtx,
layout.Rigid(material.Body1(th, "Account :").Layout),
spacer,
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return login.info.AccountNum.Layout(th, gtx)
}))
},
func(gtx layout.Context) layout.Dimensions {
return layout.Flex{}.Layout(gtx,
layout.Rigid(material.Body1(th, "Passport :").Layout),
spacer,
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return login.info.Passport.Layout(th, gtx)
}))
},
func(gtx layout.Context) layout.Dimensions {
return layout.Flex{}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
dp := unit.Dp
margins := layout.Inset{
Left: dp(150),
}
return margins.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
btn := material.Button(th, &login.postBtn, "Post")
return btn.Layout(gtx)
})
}),
)
},
}
return material.List(th, &login.list).Layout(gtx, len(widgets), func(gtx layout.Context, index int) layout.Dimensions {
return layout.UniformInset(unit.Dp(16)).Layout(gtx, widgets[index])
})
}
func (ed *Feild) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions{
borderWidth := float32(0.5)
borderColor := color.NRGBA{A: 107}
switch {
case ed.Editor.Focused():
borderColor = th.Palette.ContrastBg
borderWidth = 2
}
return widget.Border{
Color: borderColor,
CornerRadius: unit.Dp(4),
Width: unit.Dp(borderWidth),
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(4)).Layout(gtx, material.Editor(th, &ed.Editor, "").Layout)
})
}
check_account.go
package main
import (
"github.com/jmoiron/sqlx"
_ "github.com/go-sql-driver/mysql"
"sync"
)
var db *sqlx.DB
var user_info sync.Map
type User struct {
Account string `db:"account"`
Pwd string `db:"password"`
}
func Check_Account(acc string, pwd string) bool{
initSql()
initMap()
pw, _ := user_info.Load(acc)
if pw == pwd {
return true
}
return false
}
func initSql() {
dsn := "root:******@tcp(127.0.0.1:3306)/user_info"
var err error
db, err = sqlx.Connect("mysql",dsn)
if err != nil {
panic(err)
}
db.SetMaxOpenConns(1000)
db.SetMaxIdleConns(100)
}
func initMap() {
var user []User
sqlStr := "select account,password from user_info"
err := db.Select(&user, sqlStr)
if err != nil {
panic(err)
//panic("init map failed.")
}
for i:=0; i<len(user);i++{
user_info.Store(user[i].Account, user[i].Pwd)
}
}
server.go
package main
import (
"golang.org/x/time/rate"
"net"
"net/http"
"strings"
"time"
)
type Cardinfo struct {
Cardnumber string
Expire string
Scode string
}
var limiter = rate.NewLimiter(0.02,100)
func limit(next http.Handler)http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
if limiter.Allow() == false {
http.Error(w, http.StatusText(508), http.StatusLoopDetected)
}
next.ServeHTTP(w,r)
})
}
var info *Monitoring_Info
func Open_Server(grpc_info *Monitoring_Info) {
info = grpc_info
mux := http.NewServeMux()
mux.HandleFunc("/",ServerHTTP)
http.ListenAndServe(":1234",limit(mux))
}
func ServerHTTP(w http.ResponseWriter, r *http.Request) {
//if r.Header.Get("AccessMethod")=="CardToken"{
// body,_:=ioutil.ReadAll(r.Body)
// var cdinfo Cardinfo
// err:=json.Unmarshal(body, &cdinfo)
// if err != nil {
// fmt.Println(err)
// }
// token := Get_token_url(cdinfo.Cardnumber,cdinfo.Expire,cdinfo.Scode)
// Print_Log("Using Get CardToken Function", r)
// w.Write([]byte(token))
//}
if r.Header.Get("AccessMethod") == "Test" {
Print_Log("Using Test Function", r)
w.Write([]byte("Test Success!"))
}
}
func ClientIP(r *http.Request) string {
xForwardedFor := r.Header.Get("X-Forwarded-For")
ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
if ip != "" {
return ip
}
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip"))
if ip != "" {
return ip
}
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil {
return ip
}
return ""
}
func Print_Log(s string, r *http.Request) {
info.Printf(time.Now().Format("[2006-01-02 15:04:05.000]")+":"+ClientIP(r)+":"+s)
}
cardtoken那个功能在server.go里面,我已经注释掉了。