Go实战--使用golang开发Windows Gui桌面程序(lxn/walk)

前端之家收集整理的这篇文章主要介绍了Go实战--使用golang开发Windows Gui桌面程序(lxn/walk)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

生命不止,继续 go go go!!!

golang官方并没有提供Windows gui库,但是今天还是要跟大家分享一下使用golang开发Windows桌面程序,当然又是面向github编程了。

知乎上有一个问答:
golang为什么没有官方的gui包?

这里,主要使用第三方库lxn/walk,进行Windows GUI编程。

lxn/walk

github地址:
https://github.com/lxn/walk

star:
2018

描述:
A Windows GUI toolkit for the Go Programming Language

获取

go get@H_404_35@ github.com/lxn/walk

例子:

main.go

package@H_404_35@ main

import@H_404_35@ (
    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
    "strings"@H_404_35@
)

func@H_404_35@ main() {
    var@H_404_35@ inTE,outTE *walk.TextEdit

    MainWindow{
        Title:   "SCREAMO"@H_404_35@,MinSize: Size{600@H_404_35@, 400@H_404_35@},Layout:  VBox{},Children: []Widget{
            HSplitter{
                Children: []Widget{
                    TextEdit{AssignTo: &inTE},TextEdit{AssignTo: &outTE,ReadOnly: true@H_404_35@},},PushButton{
                Text: "SCREAM"@H_404_35@,OnClicked: func@H_404_35@() {
                    outTE.SetText(strings.ToUpper(inTE.Text()))
                },}.Run()
}

go build生成go_windows_gui.exe。

go_windows_gui.exe.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>@H_404_35@
    <assembly@H_404_35@ xmlns@H_404_35@="urn:schemas-microsoft-com:asm.v1"@H_404_35@ manifestVersion@H_404_35@="1.0"@H_404_35@>@H_404_35@
        <assemblyIdentity@H_404_35@ version@H_404_35@="1.0.0.0"@H_404_35@ processorArchitecture@H_404_35@="*"@H_404_35@ name@H_404_35@="SomeFunkyNameHere"@H_404_35@ type@H_404_35@="win32"@H_404_35@/>@H_404_35@
        <dependency@H_404_35@>@H_404_35@
            <dependentAssembly@H_404_35@>@H_404_35@
                <assemblyIdentity@H_404_35@ type@H_404_35@="win32"@H_404_35@ name@H_404_35@="Microsoft.Windows.Common-Controls"@H_404_35@ version@H_404_35@="6.0.0.0"@H_404_35@ processorArchitecture@H_404_35@="*"@H_404_35@ publicKeyToken@H_404_35@="6595b64144ccf1df"@H_404_35@ language@H_404_35@="*"@H_404_35@/>@H_404_35@
            </dependentAssembly@H_404_35@>@H_404_35@
        </dependency@H_404_35@>@H_404_35@
    </assembly@H_404_35@>@H_404_35@

运行结果:

什么是manifest

上面提到了manifest,这是干什么的呢?

https://msdn.microsoft.com/en-us/library/windows/desktop/aa375365(v=vs.85).aspx

介绍:
Manifests are XML files that accompany and describe side-by-side assemblies or isolated applications. Manifests uniquely identify the assembly through the assembly’s assemblyIdentity element. They contain information used for binding and activation,such as COM classes,interfaces,and type libraries,that has traditionally been stored in the registry. Manifests also specify the files that make up the assembly and may include Windows classes if the assembly author wants them to be versioned. Side-by-side assemblies are not registered on the system,but are available to applications and other assemblies on the system that specify dependencies in manifest files.

是一种xml文件,标明所依赖的side-by-side组建。

如果用VS开发,可以Set通过porperty->configuration properties->linker->manifest file->Generate manifest To Yes来自动创建manifest来指定系统的和CRT的assembly版本。

详解
观察上面的manifest文件

<xml>这是xml声明:

版本号----<?xml version="1.0"?>@H_404_35@。
这是必选项。 尽管以后的 XML 版本可能会更改该数字,但是 1.0 是当前的版本。

编码声明------<?xml version="1.0" encoding="UTF-8"?>@H_404_35@
这是可选项。 如果使用编码声明,必须紧接在 XML 声明的版本信息之后,并且必须包含代表现有字符编码的值。

standalone表示该xml是不是独立的,如果是yes,则表示这个XML文档时独立的,不能引用外部的DTD规范文件;如果是no,则该XML文档不是独立的,表示可以用外部的DTD规范文档。

<dependency>这一部分指明了其依赖于一个库:

<assemblyIdentity>属性里面还分别是:
type@H_404_35@----系统类型,@H_404_35@@H_404_35@
version@H_404_35@----版本号,@H_404_35@
processorArchitecture@H_404_35@----平台环境,@H_404_35@
publicKeyToken@H_404_35@----公匙@H_404_35@

应用

做一个巨丑无比的登录

这里用到了LineEdit、LineEdit控件

package@H_404_35@ main

import@H_404_35@ (
    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
)

func@H_404_35@ main() {
    var@H_404_35@ usernameTE,passwordTE *walk.LineEdit
    MainWindow{
        Title:   "登录"@H_404_35@,MinSize: Size{270@H_404_35@, 290@H_404_35@},Children: []Widget{
            Composite{
                Layout: Grid{Columns: 2@H_404_35@,Spacing: 10@H_404_35@},Children: []Widget{
                    VSplitter{
                        Children: []Widget{
                            Label{
                                Text: "用户名"@H_404_35@,VSplitter{
                        Children: []Widget{
                            LineEdit{
                                MinSize:  Size{160@H_404_35@, 0@H_404_35@},AssignTo: &usernameTE,VSplitter{
                        Children: []Widget{
                            Label{MaxSize: Size{160@H_404_35@, 40@H_404_35@},Text: "密码"@H_404_35@,AssignTo: &passwordTE,PushButton{
                Text:    "登录"@H_404_35@,MinSize: Size{120@H_404_35@, 50@H_404_35@},OnClicked: func@H_404_35@() {
                    if@H_404_35@ usernameTE.Text() == ""@H_404_35@ {
                        var@H_404_35@ tmp walk.Form
                        walk.MsgBox(tmp,"用户名为空"@H_404_35@,""@H_404_35@,walk.MsgBoxIconInformation)
                        return@H_404_35@
                    }
                    if@H_404_35@ passwordTE.Text() == ""@H_404_35@ {
                        var@H_404_35@ tmp walk.Form
                        walk.MsgBox(tmp,"密码为空"@H_404_35@,walk.MsgBoxIconInformation)
                        return@H_404_35@
                    }
                },}.Run()
}

效果

TableView的使用

这里主要使用的是TableView控件,代码参考github:

package@H_404_35@ main

import@H_404_35@ (
    "fmt"@H_404_35@
    "sort"@H_404_35@

    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
)

type@H_404_35@ Condom struct@H_404_35@ {
    Index   int@H_404_35@
    Name    string@H_404_35@
    Price   int@H_404_35@
    checked bool@H_404_35@
}

type@H_404_35@ CondomModel struct@H_404_35@ {
    walk.TableModelBase
    walk.SorterBase
    sortColumn int@H_404_35@
    sortOrder  walk.SortOrder
    items      []*Condom
}

func@H_404_35@ (m *CondomModel) RowCount() int@H_404_35@ {
    return@H_404_35@ len@H_404_35@(m.items)
}

func@H_404_35@ (m *CondomModel) Value(row,col int@H_404_35@) interface@H_404_35@{} {
    item := m.items[row]

    switch@H_404_35@ col {
    case@H_404_35@ 0@H_404_35@:
        return@H_404_35@ item.Index
    case@H_404_35@ 1@H_404_35@:
        return@H_404_35@ item.Name
    case@H_404_35@ 2@H_404_35@:
        return@H_404_35@ item.Price
    }
    panic@H_404_35@("unexpected col"@H_404_35@)
}

func@H_404_35@ (m *CondomModel) Checked(row int@H_404_35@) bool@H_404_35@ {
    return@H_404_35@ m.items[row].checked
}

func@H_404_35@ (m *CondomModel) SetChecked(row int@H_404_35@,checked bool@H_404_35@) error {
    m.items[row].checked = checked
    return@H_404_35@ nil@H_404_35@
}

func@H_404_35@ (m *CondomModel) Sort(col int@H_404_35@,order walk.SortOrder) error {
    m.sortColumn,m.sortOrder = col,order

    sort.Stable(m)

    return@H_404_35@ m.SorterBase.Sort(col,order)
}

func@H_404_35@ (m *CondomModel) Len() int@H_404_35@ {
    return@H_404_35@ len@H_404_35@(m.items)
}

func@H_404_35@ (m *CondomModel) Less(i,j int@H_404_35@) bool@H_404_35@ {
    a,b := m.items[i],m.items[j]

    c := func@H_404_35@(ls bool@H_404_35@) bool@H_404_35@ {
        if@H_404_35@ m.sortOrder == walk.SortAscending {
            return@H_404_35@ ls
        }

        return@H_404_35@ !ls
    }

    switch@H_404_35@ m.sortColumn {
    case@H_404_35@ 0@H_404_35@:
        return@H_404_35@ c(a.Index < b.Index)
    case@H_404_35@ 1@H_404_35@:
        return@H_404_35@ c(a.Name < b.Name)
    case@H_404_35@ 2@H_404_35@:
        return@H_404_35@ c(a.Price < b.Price)
    }

    panic@H_404_35@("unreachable"@H_404_35@)
}

func@H_404_35@ (m *CondomModel) Swap(i,j int@H_404_35@) {
    m.items[i],m.items[j] = m.items[j],m.items[i]
}

func@H_404_35@ NewCondomModel() *CondomModel {
    m := new@H_404_35@(CondomModel)
    m.items = make@H_404_35@([]*Condom, 3@H_404_35@)

    m.items[0@H_404_35@] = &Condom{
        Index: 0@H_404_35@,Name:  "杜蕾斯"@H_404_35@,Price: 20@H_404_35@,}

    m.items[1@H_404_35@] = &Condom{
        Index: 1@H_404_35@,Name:  "杰士邦"@H_404_35@,Price: 18@H_404_35@,}

    m.items[2@H_404_35@] = &Condom{
        Index: 2@H_404_35@,Name:  "冈本"@H_404_35@,Price: 19@H_404_35@,}

    return@H_404_35@ m
}

type@H_404_35@ CondomMainWindow struct@H_404_35@ {
    *walk.MainWindow
    model *CondomModel
    tv    *walk.TableView
}

func@H_404_35@ main() {
    mw := &CondomMainWindow{model: NewCondomModel()}

    MainWindow{
        AssignTo: &mw.MainWindow,Title:    "Condom展示"@H_404_35@,Size:     Size{800@H_404_35@, 600@H_404_35@},Layout:   VBox{},Children: []Widget{
            Composite{
                Layout: HBox{MarginsZero: true@H_404_35@},Children: []Widget{
                    HSpacer{},PushButton{
                        Text: "Add"@H_404_35@,OnClicked: func@H_404_35@() {
                            mw.model.items = append@H_404_35@(mw.model.items,&Condom{
                                Index: mw.model.Len() + 1@H_404_35@,Name:  "第六感"@H_404_35@,Price: mw.model.Len() * 5@H_404_35@,})
                            mw.model.PublishRowsReset()
                            mw.tv.SetSelectedIndexes([]int@H_404_35@{})
                        },PushButton{
                        Text: "Delete"@H_404_35@,OnClicked: func@H_404_35@() {
                            items := []*Condom{}
                            remove := mw.tv.SelectedIndexes()
                            for@H_404_35@ i,x := range@H_404_35@ mw.model.items {
                                remove_ok := false@H_404_35@
                                for@H_404_35@ _,j := range@H_404_35@ remove {
                                    if@H_404_35@ i == j {
                                        remove_ok = true@H_404_35@
                                    }
                                }
                                if@H_404_35@ !remove_ok {
                                    items = append@H_404_35@(items,x)
                                }
                            }
                            mw.model.items = items
                            mw.model.PublishRowsReset()
                            mw.tv.SetSelectedIndexes([]int@H_404_35@{})
                        },PushButton{
                        Text: "ExecChecked"@H_404_35@,OnClicked: func@H_404_35@() {
                            for@H_404_35@ _,x := range@H_404_35@ mw.model.items {
                                if@H_404_35@ x.checked {
                                    fmt.Printf("checked: %v\n"@H_404_35@,x)
                                }
                            }
                            fmt.Println()
                        },PushButton{
                        Text: "AddPriceChecked"@H_404_35@,OnClicked: func@H_404_35@() {
                            for@H_404_35@ i,x := range@H_404_35@ mw.model.items {
                                if@H_404_35@ x.checked {
                                    x.Price++
                                    mw.model.PublishRowChanged(i)
                                }
                            }
                        },Composite{
                Layout: VBox{},ContextMenuItems: []MenuItem{
                    Action{
                        Text:        "I&nfo"@H_404_35@,OnTriggered: mw.tv_ItemActivated,Action{
                        Text: "E&xit"@H_404_35@,OnTriggered: func@H_404_35@() {
                            mw.Close()
                        },Children: []Widget{
                    TableView{
                        AssignTo:         &mw.tv,CheckBoxes:       true@H_404_35@,ColumnsOrderable: true@H_404_35@,MultiSelection:   true@H_404_35@,Columns: []TableViewColumn{
                            {Title: "编号"@H_404_35@},{Title: "名称"@H_404_35@},{Title: "价格"@H_404_35@},Model: mw.model,OnCurrentIndexChanged: func@H_404_35@() {
                            i := mw.tv.CurrentIndex()
                            if@H_404_35@ 0@H_404_35@ <= i {
                                fmt.Printf("OnCurrentIndexChanged: %v\n"@H_404_35@,mw.model.items[i].Name)
                            }
                        },OnItemActivated: mw.tv_ItemActivated,}.Run()
}

func@H_404_35@ (mw *CondomMainWindow) tv_ItemActivated() {
    msg := ``@H_404_35@
    for@H_404_35@ _,i := range@H_404_35@ mw.tv.SelectedIndexes() {
        msg = msg + "\n"@H_404_35@ + mw.model.items[i].Name
    }
    walk.MsgBox(mw,"title"@H_404_35@,msg,walk.MsgBoxIconInformation)
}

效果

文件选择器(加入了icon)

这里就是调用Windows的文件选择框
主要是使用:FileDialog

package@H_404_35@ main

import@H_404_35@ (
    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
)

import@H_404_35@ (
    "fmt"@H_404_35@
    "os"@H_404_35@
)

type@H_404_35@ MyMainWindow struct@H_404_35@ {
    *walk.MainWindow
    edit *walk.TextEdit

    path string@H_404_35@
}

func@H_404_35@ main() {
    mw := &MyMainWindow{}
    MW := MainWindow{
        AssignTo: &mw.MainWindow,Icon:     "test.ico"@H_404_35@,Title:    "文件选择对话框"@H_404_35@,MinSize:  Size{150@H_404_35@, 200@H_404_35@},Size:     Size{300@H_404_35@,Children: []Widget{
            TextEdit{
                AssignTo: &mw.edit,PushButton{
                Text:      "打开"@H_404_35@,OnClicked: mw.pbClicked,}
    if@H_404_35@ _,err := MW.Run(); err != nil@H_404_35@ {
        fmt.Fprintln(os.Stderr,err)
        os.Exit(1@H_404_35@)
    }

}

func@H_404_35@ (mw *MyMainWindow) pbClicked() {

    dlg := new@H_404_35@(walk.FileDialog)
    dlg.FilePath = mw.path
    dlg.Title = "Select File"@H_404_35@
    dlg.Filter = "Exe files (*.exe)|*.exe|All files (*.*)|*.*"@H_404_35@

    if@H_404_35@ ok,err := dlg.ShowOpen(mw); err != nil@H_404_35@ {
        mw.edit.AppendText("Error : File Open\r\n"@H_404_35@)
        return@H_404_35@
    } else@H_404_35@ if@H_404_35@ !ok {
        mw.edit.AppendText("Cancel\r\n"@H_404_35@)
        return@H_404_35@
    }
    mw.path = dlg.FilePath
    s := fmt.Sprintf("Select : %s\r\n"@H_404_35@,mw.path)
    mw.edit.AppendText(s)
}

效果

文本检索器

package@H_404_35@ main

import@H_404_35@ (
    "fmt"@H_404_35@
    "log"@H_404_35@
    "strings"@H_404_35@

    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
)

func@H_404_35@ main() {
    mw := &MyMainWindow{}

    if@H_404_35@ _,err := (MainWindow{
        AssignTo: &mw.MainWindow,Title:    "SearchBox"@H_404_35@,MinSize:  Size{300@H_404_35@,Children: []Widget{
            GroupBox{
                Layout: HBox{},Children: []Widget{
                    LineEdit{
                        AssignTo: &mw.searchBox,PushButton{
                        Text:      "检索"@H_404_35@,OnClicked: mw.clicked,TextEdit{
                AssignTo: &mw.textArea,ListBox{
                AssignTo: &mw.results,Row:      5@H_404_35@,}.Run()); err != nil@H_404_35@ {
        log.Fatal(err)
    }

}

type@H_404_35@ MyMainWindow struct@H_404_35@ {
    *walk.MainWindow
    searchBox *walk.LineEdit
    textArea  *walk.TextEdit
    results   *walk.ListBox
}

func@H_404_35@ (mw *MyMainWindow) clicked() {
    word := mw.searchBox.Text()
    text := mw.textArea.Text()
    model := []string@H_404_35@{}
    for@H_404_35@ _,i := range@H_404_35@ search(text,word) {
        model = append@H_404_35@(model,fmt.Sprintf("%d检索成功"@H_404_35@,i))
    }
    log.Print(model)
    mw.results.SetModel(model)
}

func@H_404_35@ search(text,word string@H_404_35@) (result []int@H_404_35@) {
    result = []int@H_404_35@{}
    i := 0@H_404_35@
    for@H_404_35@ j,_ := range@H_404_35@ text {
        if@H_404_35@ strings.HasPrefix(text[j:],word) {
            log.Print(i)
            result = append@H_404_35@(result,i)
        }
        i += 1@H_404_35@
    }
    return@H_404_35@
}

效果

邮件群发器

别人写的邮件群发器,出自:
https://studygolang.com/articles/2078
关于golang中stmp的使用,请看博客
Go实战–通过net/smtp发送邮件(The way to go)

package@H_404_35@ main

import@H_404_35@ (
    "bufio"@H_404_35@
    "encoding/gob"@H_404_35@
    "errors"@H_404_35@
    "fmt"@H_404_35@
    "io"@H_404_35@
    "net/smtp"@H_404_35@
    "os"@H_404_35@
    "strconv"@H_404_35@
    "strings"@H_404_35@
    "time"@H_404_35@
)

import@H_404_35@ (
    "github.com/lxn/walk"@H_404_35@
    . "github.com/lxn/walk/declarative"@H_404_35@
)

type@H_404_35@ ShuJu struct@H_404_35@ {
    Name    string@H_404_35@
    Pwd     string@H_404_35@
    Host    string@H_404_35@
    Subject string@H_404_35@
    Body    string@H_404_35@
    Send    string@H_404_35@
}

func@H_404_35@ SendMail(user,password,host,to,subject,body,mailtype string@H_404_35@) error {
    fmt.Println("Send to "@H_404_35@ + to)
    hp := strings.Split(host,":"@H_404_35@)
    auth := smtp.PlainAuth(""@H_404_35@,user,hp[0@H_404_35@])
    var@H_404_35@ content_type string@H_404_35@
    if@H_404_35@ mailtype == "html"@H_404_35@ {
        content_type = "Content-Type: text/html;charset=UTF-8"@H_404_35@
    } else@H_404_35@ {
        content_type = "Content-Type: text/plain;charset=UTF-8"@H_404_35@
    }
    body = strings.TrimSpace(body)
    msg := []byte@H_404_35@("To: "@H_404_35@ + to + "\r\nFrom: "@H_404_35@ + user + "<"@H_404_35@ + user + ">\r\nSubject: "@H_404_35@ + subject + "\r\n"@H_404_35@ + content_type + "\r\n\r\n"@H_404_35@ + body)
    send_to := strings.Split(to,";"@H_404_35@)
    err := smtp.SendMail(host,auth,send_to,msg)
    if@H_404_35@ err != nil@H_404_35@ {
        fmt.Println(err.Error())
    }
    return@H_404_35@ err
}

func@H_404_35@ readLine2Array(filename string@H_404_35@) ([]string@H_404_35@,error) {
    result := make@H_404_35@([]string@H_404_35@, 0@H_404_35@)
    file,err := os.Open(filename)
    if@H_404_35@ err != nil@H_404_35@ {
        return@H_404_35@ result,errors.New("Open file Failed."@H_404_35@)
    }
    defer@H_404_35@ file.Close()
    bf := bufio.NewReader(file)
    for@H_404_35@ {
        line,isPrefix,err1 := bf.ReadLine()
        if@H_404_35@ err1 != nil@H_404_35@ {
            if@H_404_35@ err1 != io.EOF {
                return@H_404_35@ result,errors.New("ReadLine no finish"@H_404_35@)
            }
            break@H_404_35@
        }
        if@H_404_35@ isPrefix {
            return@H_404_35@ result,errors.New("Line is too long"@H_404_35@)
        }
        str := string@H_404_35@(line)
        result = append@H_404_35@(result,str)
    }
    return@H_404_35@ result,nil@H_404_35@
}

func@H_404_35@ DelArrayVar(arr []string@H_404_35@,str string@H_404_35@) []string@H_404_35@ {
    str = strings.TrimSpace(str)
    for@H_404_35@ i,v := range@H_404_35@ arr {
        v = strings.TrimSpace(v)
        if@H_404_35@ v == str {
            if@H_404_35@ i == len@H_404_35@(arr) {
                return@H_404_35@ arr[0@H_404_35@ : i-1@H_404_35@]
            }
            if@H_404_35@ i == 0@H_404_35@ {
                return@H_404_35@ arr[1@H_404_35@:len@H_404_35@(arr)]
            }
            a1 := arr[0@H_404_35@:i]
            a2 := arr[i+1@H_404_35@ : len@H_404_35@(arr)]
            return@H_404_35@ append@H_404_35@(a1,a2...)
        }
    }
    return@H_404_35@ arr
}

func@H_404_35@ LoadData() {
    fmt.Println("LoadData"@H_404_35@)
    file,err := os.Open("data.dat"@H_404_35@)
    defer@H_404_35@ file.Close()
    if@H_404_35@ err != nil@H_404_35@ {
        fmt.Println(err.Error())
        SJ.Name = "用户名"@H_404_35@
        SJ.Pwd = "用户密码"@H_404_35@
        SJ.Host = "SMTP服务器:端口"@H_404_35@
        SJ.Subject = "邮件主题"@H_404_35@
        SJ.Body = "邮件内容"@H_404_35@
        SJ.Send = "要发送的邮箱,每行一个"@H_404_35@
        return@H_404_35@
    }
    dec := gob.NewDecoder(file)
    err2 := dec.Decode(&SJ)
    if@H_404_35@ err2 != nil@H_404_35@ {
        fmt.Println(err2.Error())
        SJ.Name = "用户名"@H_404_35@
        SJ.Pwd = "用户密码"@H_404_35@
        SJ.Host = "SMTP服务器:端口"@H_404_35@
        SJ.Subject = "邮件主题"@H_404_35@
        SJ.Body = "邮件内容"@H_404_35@
        SJ.Send = "要发送的邮箱,每行一个"@H_404_35@
    }
}

func@H_404_35@ SaveData() {
    fmt.Println("SaveData"@H_404_35@)
    file,err := os.Create("data.dat"@H_404_35@)
    defer@H_404_35@ file.Close()
    if@H_404_35@ err != nil@H_404_35@ {
        fmt.Println(err)
    }
    enc := gob.NewEncoder(file)
    err2 := enc.Encode(SJ)
    if@H_404_35@ err2 != nil@H_404_35@ {
        fmt.Println(err2)
    }
}

var@H_404_35@ SJ ShuJu
var@H_404_35@ runing bool@H_404_35@
var@H_404_35@ chEnd chan@H_404_35@ bool@H_404_35@

func@H_404_35@ main() {
    LoadData()
    chEnd = make@H_404_35@(chan@H_404_35@ bool@H_404_35@)
    var@H_404_35@ emails,msgBox *walk.TextEdit
    var@H_404_35@ user,subject *walk.LineEdit
    var@H_404_35@ startBtn *walk.PushButton
    MainWindow{
        Title:   "邮件发送器"@H_404_35@,MinSize: Size{800@H_404_35@,Layout:  HBox{},Children: []Widget{
            TextEdit{AssignTo: &emails,Text: SJ.Send,ToolTipText: "待发送邮件列表,每行一个"@H_404_35@},VSplitter{
                Children: []Widget{
                    LineEdit{AssignTo: &user,Text: SJ.Name,CueBanner: "请输入邮箱用户名"@H_404_35@},LineEdit{AssignTo: &password,Text: SJ.Pwd,PasswordMode: true@H_404_35@,CueBanner: "请输入邮箱登录密码"@H_404_35@},LineEdit{AssignTo: &host,Text: SJ.Host,CueBanner: "SMTP服务器:端口"@H_404_35@},LineEdit{AssignTo: &subject,Text: SJ.Subject,CueBanner: "请输入邮件主题……"@H_404_35@},TextEdit{AssignTo: &body,Text: SJ.Body,ToolTipText: "请输入邮件内容"@H_404_35@,ColumnSpan: 2@H_404_35@},TextEdit{AssignTo: &msgBox,PushButton{
                        AssignTo: &startBtn,Text:     "开始群发"@H_404_35@,OnClicked: func@H_404_35@() {
                            SJ.Name = user.Text()
                            SJ.Pwd = password.Text()
                            SJ.Host = host.Text()
                            SJ.Subject = subject.Text()
                            SJ.Body = body.Text()
                            SJ.Send = emails.Text()
                            SaveData()

                            if@H_404_35@ runing == false@H_404_35@ {
                                runing = true@H_404_35@
                                startBtn.SetText("停止发送"@H_404_35@)
                                go@H_404_35@ sendThread(msgBox,emails)
                            } else@H_404_35@ {
                                runing = false@H_404_35@
                                startBtn.SetText("开始群发"@H_404_35@)
                            }
                        },}.Run()
}

func@H_404_35@ sendThread(msgBox,es *walk.TextEdit) {
    sendTo := strings.Split(SJ.Send,"\r\n"@H_404_35@)
    susscess := 0@H_404_35@
    count := len@H_404_35@(sendTo)
    for@H_404_35@ index,to := range@H_404_35@ sendTo {
        if@H_404_35@ runing == false@H_404_35@ {
            break@H_404_35@
        }
        msgBox.SetText("发送到"@H_404_35@ + to + "..."@H_404_35@ + strconv.Itoa((index/count)*100@H_404_35@) + "%"@H_404_35@)
        err := SendMail(SJ.Name,SJ.Pwd,SJ.Host,SJ.Subject,SJ.Body,"html"@H_404_35@)
        if@H_404_35@ err != nil@H_404_35@ {
            msgBox.AppendText("\r\n失败:"@H_404_35@ + err.Error() + "\r\n"@H_404_35@)
            if@H_404_35@ err.Error() == "550 MailBox not found or access denied"@H_404_35@ {
                SJ.Send = strings.Join(DelArrayVar(strings.Split(SJ.Send,"\r\n"@H_404_35@),to),"\r\n"@H_404_35@)
                es.SetText(SJ.Send)
            }
            time.Sleep(1@H_404_35@ * time.Second)
            continue@H_404_35@
        } else@H_404_35@ {
            susscess++
            msgBox.AppendText("\r\n发送成功!"@H_404_35@)
            SJ.Send = strings.Join(DelArrayVar(strings.Split(SJ.Send,"\r\n"@H_404_35@)
            es.SetText(SJ.Send)
        }
        time.Sleep(1@H_404_35@ * time.Second)
    }
    SaveData()
    msgBox.AppendText("停止发送!成功 "@H_404_35@ + strconv.Itoa(susscess) + " 条\r\n"@H_404_35@)
}

效果


原文链接:https://www.f2er.com/go/187990.html

猜你在找的Go相关文章