Go实战--使用golang开发Windows Gui桌面程序(lxn/walk)
生命不止,继续 go go go!!! golang官方并没有提供Windows gui库,但是今天还是要跟大家分享一下使用golang开发Windows桌面程序,当然又是面向github编程了。 知乎上有一个问答: 这里,主要使用第三方库lxn/walk,进行Windows GUI编程。 lxn/walkgithub地址: star: 描述: 获取: go get github.com/lxn/walk
例子: main.go package main
import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
"strings"
)
func main() {
var inTE,outTE *walk.TextEdit
MainWindow{
Title: "SCREAMO",MinSize: Size{600, 400},Layout: VBox{},Children: []Widget{
HSplitter{
Children: []Widget{
TextEdit{AssignTo: &inTE},TextEdit{AssignTo: &outTE,ReadOnly: true},},PushButton{
Text: "SCREAM",OnClicked: func() {
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"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
</assembly>
运行结果: 什么是manifest上面提到了manifest,这是干什么的呢? https://msdn.microsoft.com/en-us/library/windows/desktop/aa375365(v=vs.85).aspx 介绍: 是一种xml文件,标明所依赖的side-by-side组建。 如果用VS开发,可以Set通过porperty->configuration properties->linker->manifest file->Generate manifest To Yes来自动创建manifest来指定系统的和CRT的assembly版本。 详解 <xml>这是xml声明: 版本号----<?xml version="1.0"?>。
这是必选项。 尽管以后的 XML 版本可能会更改该数字,但是 1.0 是当前的版本。
编码声明------<?xml version="1.0" encoding="UTF-8"?>
这是可选项。 如果使用编码声明,必须紧接在 XML 声明的版本信息之后,并且必须包含代表现有字符编码的值。
standalone表示该xml是不是独立的,如果是yes,则表示这个XML文档时独立的,不能引用外部的DTD规范文件;如果是no,则该XML文档不是独立的,表示可以用外部的DTD规范文档。
<dependency>这一部分指明了其依赖于一个库: <assemblyIdentity>属性里面还分别是:
type----系统类型,
version----版本号,
processorArchitecture----平台环境,
publicKeyToken----公匙
应用做一个巨丑无比的登录框这里用到了LineEdit、LineEdit控件 package main
import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
func main() {
var usernameTE,passwordTE *walk.LineEdit
MainWindow{
Title: "登录",MinSize: Size{270, 290},Children: []Widget{
Composite{
Layout: Grid{Columns: 2,Spacing: 10},Children: []Widget{
VSplitter{
Children: []Widget{
Label{
Text: "用户名",VSplitter{
Children: []Widget{
LineEdit{
MinSize: Size{160, 0},AssignTo: &usernameTE,VSplitter{
Children: []Widget{
Label{MaxSize: Size{160, 40},Text: "密码",AssignTo: &passwordTE,PushButton{
Text: "登录",MinSize: Size{120, 50},OnClicked: func() {
if usernameTE.Text() == "" {
var tmp walk.Form
walk.MsgBox(tmp,"用户名为空","",walk.MsgBoxIconInformation)
return
}
if passwordTE.Text() == "" {
var tmp walk.Form
walk.MsgBox(tmp,"密码为空",walk.MsgBoxIconInformation)
return
}
},}.Run()
}
效果: TableView的使用这里主要使用的是TableView控件,代码参考github: package main
import (
"fmt"
"sort"
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
type Condom struct {
Index int
Name string
Price int
checked bool
}
type CondomModel struct {
walk.TableModelBase
walk.SorterBase
sortColumn int
sortOrder walk.SortOrder
items []*Condom
}
func (m *CondomModel) RowCount() int {
return len(m.items)
}
func (m *CondomModel) Value(row,col int) interface{} {
item := m.items[row]
switch col {
case 0:
return item.Index
case 1:
return item.Name
case 2:
return item.Price
}
panic("unexpected col")
}
func (m *CondomModel) Checked(row int) bool {
return m.items[row].checked
}
func (m *CondomModel) SetChecked(row int,checked bool) error {
m.items[row].checked = checked
return nil
}
func (m *CondomModel) Sort(col int,order walk.SortOrder) error {
m.sortColumn,m.sortOrder = col,order
sort.Stable(m)
return m.SorterBase.Sort(col,order)
}
func (m *CondomModel) Len() int {
return len(m.items)
}
func (m *CondomModel) Less(i,j int) bool {
a,b := m.items[i],m.items[j]
c := func(ls bool) bool {
if m.sortOrder == walk.SortAscending {
return ls
}
return !ls
}
switch m.sortColumn {
case 0:
return c(a.Index < b.Index)
case 1:
return c(a.Name < b.Name)
case 2:
return c(a.Price < b.Price)
}
panic("unreachable")
}
func (m *CondomModel) Swap(i,j int) {
m.items[i],m.items[j] = m.items[j],m.items[i]
}
func NewCondomModel() *CondomModel {
m := new(CondomModel)
m.items = make([]*Condom, 3)
m.items[0] = &Condom{
Index: 0,Name: "杜蕾斯",Price: 20,}
m.items[1] = &Condom{
Index: 1,Name: "杰士邦",Price: 18,}
m.items[2] = &Condom{
Index: 2,Name: "冈本",Price: 19,}
return m
}
type CondomMainWindow struct {
*walk.MainWindow
model *CondomModel
tv *walk.TableView
}
func main() {
mw := &CondomMainWindow{model: NewCondomModel()}
MainWindow{
AssignTo: &mw.MainWindow,Title: "Condom展示",Size: Size{800, 600},Layout: VBox{},Children: []Widget{
Composite{
Layout: HBox{MarginsZero: true},Children: []Widget{
HSpacer{},PushButton{
Text: "Add",OnClicked: func() {
mw.model.items = append(mw.model.items,&Condom{
Index: mw.model.Len() + 1,Name: "第六感",Price: mw.model.Len() * 5,})
mw.model.PublishRowsReset()
mw.tv.SetSelectedIndexes([]int{})
},PushButton{
Text: "Delete",OnClicked: func() {
items := []*Condom{}
remove := mw.tv.SelectedIndexes()
for i,x := range mw.model.items {
remove_ok := false
for _,j := range remove {
if i == j {
remove_ok = true
}
}
if !remove_ok {
items = append(items,x)
}
}
mw.model.items = items
mw.model.PublishRowsReset()
mw.tv.SetSelectedIndexes([]int{})
},PushButton{
Text: "ExecChecked",OnClicked: func() {
for _,x := range mw.model.items {
if x.checked {
fmt.Printf("checked: %vn",x)
}
}
fmt.Println()
},PushButton{
Text: "AddPriceChecked",OnClicked: func() {
for i,x := range mw.model.items {
if x.checked {
x.Price++
mw.model.PublishRowChanged(i)
}
}
},Composite{
Layout: VBox{},ContextMenuItems: []MenuItem{
Action{
Text: "I&nfo",OnTriggered: mw.tv_ItemActivated,Action{
Text: "E&xit",OnTriggered: func() {
mw.Close()
},Children: []Widget{
TableView{
AssignTo: &mw.tv,CheckBoxes: true,ColumnsOrderable: true,MultiSelection: true,Columns: []TableViewColumn{
{Title: "编号"},{Title: "名称"},{Title: "价格"},Model: mw.model,OnCurrentIndexChanged: func() {
i := mw.tv.CurrentIndex()
if 0 <= i {
fmt.Printf("OnCurrentIndexChanged: %vn",mw.model.items[i].Name)
}
},OnItemActivated: mw.tv_ItemActivated,}.Run()
}
func (mw *CondomMainWindow) tv_ItemActivated() {
msg := ``
for _,i := range mw.tv.SelectedIndexes() {
msg = msg + "n" + mw.model.items[i].Name
}
walk.MsgBox(mw,"title",msg,walk.MsgBoxIconInformation)
}
效果: 文件选择器(加入了icon)这里就是调用Windows的文件选择框 package main
import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
import (
"fmt"
"os"
)
type MyMainWindow struct {
*walk.MainWindow
edit *walk.TextEdit
path string
}
func main() {
mw := &MyMainWindow{}
MW := MainWindow{
AssignTo: &mw.MainWindow,Icon: "test.ico",Title: "文件选择对话框",MinSize: Size{150, 200},Size: Size{300,Children: []Widget{
TextEdit{
AssignTo: &mw.edit,PushButton{
Text: "打开",OnClicked: mw.pbClicked,}
if _,err := MW.Run(); err != nil {
fmt.Fprintln(os.Stderr,err)
os.Exit(1)
}
}
func (mw *MyMainWindow) pbClicked() {
dlg := new(walk.FileDialog)
dlg.FilePath = mw.path
dlg.Title = "Select File"
dlg.Filter = "Exe files (*.exe)|*.exe|All files (*.*)|*.*"
if ok,err := dlg.ShowOpen(mw); err != nil {
mw.edit.AppendText("Error : File Openrn")
return
} else if !ok {
mw.edit.AppendText("Cancelrn")
return
}
mw.path = dlg.FilePath
s := fmt.Sprintf("Select : %srn",mw.path)
mw.edit.AppendText(s)
}
效果: 文本检索器package main
import (
"fmt"
"log"
"strings"
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
func main() {
mw := &MyMainWindow{}
if _,err := (MainWindow{
AssignTo: &mw.MainWindow,Title: "SearchBox",MinSize: Size{300,Children: []Widget{
GroupBox{
Layout: HBox{},Children: []Widget{
LineEdit{
AssignTo: &mw.searchBox,PushButton{
Text: "检索",OnClicked: mw.clicked,TextEdit{
AssignTo: &mw.textArea,ListBox{
AssignTo: &mw.results,Row: 5,}.Run()); err != nil {
log.Fatal(err)
}
}
type MyMainWindow struct {
*walk.MainWindow
searchBox *walk.LineEdit
textArea *walk.TextEdit
results *walk.ListBox
}
func (mw *MyMainWindow) clicked() {
word := mw.searchBox.Text()
text := mw.textArea.Text()
model := []string{}
for _,i := range search(text,word) {
model = append(model,fmt.Sprintf("%d检索成功",i))
}
log.Print(model)
mw.results.SetModel(model)
}
func search(text,word string) (result []int) {
result = []int{}
i := 0
for j,_ := range text {
if strings.HasPrefix(text[j:],word) {
log.Print(i)
result = append(result,i)
}
i += 1
}
return
}
效果: 邮件群发器别人写的邮件群发器,出自: package main
import (
"bufio"
"encoding/gob"
"errors"
"fmt"
"io"
"net/smtp"
"os"
"strconv"
"strings"
"time"
)
import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
type ShuJu struct {
Name string
Pwd string
Host string
Subject string
Body string
Send string
}
func SendMail(user,password,host,to,subject,body,mailtype string) error {
fmt.Println("Send to " + to)
hp := strings.Split(host,":")
auth := smtp.PlainAuth("",user,hp[0])
var content_type string
if mailtype == "html" {
content_type = "Content-Type: text/html;charset=UTF-8"
} else {
content_type = "Content-Type: text/plain;charset=UTF-8"
}
body = strings.TrimSpace(body)
msg := []byte("To: " + to + "rnFrom: " + user + "<" + user + ">rnSubject: " + subject + "rn" + content_type + "rnrn" + body)
send_to := strings.Split(to,";")
err := smtp.SendMail(host,auth,send_to,msg)
if err != nil {
fmt.Println(err.Error())
}
return err
}
func readLine2Array(filename string) ([]string,error) {
result := make([]string, 0)
file,err := os.Open(filename)
if err != nil {
return result,errors.New("Open file failed.")
}
defer file.Close()
bf := bufio.NewReader(file)
for {
line,isPrefix,err1 := bf.ReadLine()
if err1 != nil {
if err1 != io.EOF {
return result,errors.New("ReadLine no finish")
}
break
}
if isPrefix {
return result,errors.New("Line is too long")
}
str := string(line)
result = append(result,str)
}
return result,nil
}
func DelArrayVar(arr []string,str string) []string {
str = strings.TrimSpace(str)
for i,v := range arr {
v = strings.TrimSpace(v)
if v == str {
if i == len(arr) {
return arr[0 : i-1]
}
if i == 0 {
return arr[1:len(arr)]
}
a1 := arr[0:i]
a2 := arr[i+1 : len(arr)]
return append(a1,a2...)
}
}
return arr
}
func LoadData() {
fmt.Println("LoadData")
file,err := os.Open("data.dat")
defer file.Close()
if err != nil {
fmt.Println(err.Error())
SJ.Name = "用户名"
SJ.Pwd = "用户密码"
SJ.Host = "SMTP服务器:端口"
SJ.Subject = "邮件主题"
SJ.Body = "邮件内容"
SJ.Send = "要发送的邮箱,每行一个"
return
}
dec := gob.NewDecoder(file)
err2 := dec.Decode(&SJ)
if err2 != nil {
fmt.Println(err2.Error())
SJ.Name = "用户名"
SJ.Pwd = "用户密码"
SJ.Host = "SMTP服务器:端口"
SJ.Subject = "邮件主题"
SJ.Body = "邮件内容"
SJ.Send = "要发送的邮箱,每行一个"
}
}
func SaveData() {
fmt.Println("SaveData")
file,err := os.Create("data.dat")
defer file.Close()
if err != nil {
fmt.Println(err)
}
enc := gob.NewEncoder(file)
err2 := enc.Encode(SJ)
if err2 != nil {
fmt.Println(err2)
}
}
var SJ ShuJu
var runing bool
var chEnd chan bool
func main() {
LoadData()
chEnd = make(chan bool)
var emails,msgbox *walk.TextEdit
var user,subject *walk.LineEdit
var startBtn *walk.PushButton
MainWindow{
Title: "邮件发送器",MinSize: Size{800,Layout: HBox{},Children: []Widget{
TextEdit{AssignTo: &emails,Text: SJ.Send,ToolTipText: "待发送邮件列表,每行一个"},VSplitter{
Children: []Widget{
LineEdit{AssignTo: &user,Text: SJ.Name,CueBanner: "请输入邮箱用户名"},LineEdit{AssignTo: &password,Text: SJ.Pwd,PasswordMode: true,CueBanner: "请输入邮箱登录密码"},LineEdit{AssignTo: &host,Text: SJ.Host,CueBanner: "SMTP服务器:端口"},LineEdit{AssignTo: &subject,Text: SJ.Subject,CueBanner: "请输入邮件主题……"},TextEdit{AssignTo: &body,Text: SJ.Body,ToolTipText: "请输入邮件内容",ColumnSpan: 2},TextEdit{AssignTo: &msgbox,PushButton{
AssignTo: &startBtn,Text: "开始群发",OnClicked: func() {
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 runing == false {
runing = true
startBtn.SetText("停止发送")
go sendThread(msgbox,emails)
} else {
runing = false
startBtn.SetText("开始群发")
}
},}.Run()
}
func sendThread(msgbox,es *walk.TextEdit) {
sendTo := strings.Split(SJ.Send,"rn")
susscess := 0
count := len(sendTo)
for index,to := range sendTo {
if runing == false {
break
}
msgbox.SetText("发送到" + to + "..." + strconv.Itoa((index/count)*100) + "%")
err := SendMail(SJ.Name,SJ.Pwd,SJ.Host,SJ.Subject,SJ.Body,"html")
if err != nil {
msgbox.AppendText("rn失败:" + err.Error() + "rn")
if err.Error() == "550 Mailbox not found or access denied" {
SJ.Send = strings.Join(DelArrayVar(strings.Split(SJ.Send,"rn"),to),"rn")
es.SetText(SJ.Send)
}
time.Sleep(1 * time.Second)
continue
} else {
susscess++
msgbox.AppendText("rn发送成功!")
SJ.Send = strings.Join(DelArrayVar(strings.Split(SJ.Send,"rn")
es.SetText(SJ.Send)
}
time.Sleep(1 * time.Second)
}
SaveData()
msgbox.AppendText("停止发送!成功 " + strconv.Itoa(susscess) + " 条rn")
}
效果: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |