Go Gio 實(shí)戰(zhàn):煮蛋計(jì)時(shí)器的實(shí)現(xiàn) 01 — 空窗口
接下來我們將一步步實(shí)通過 gio 實(shí)現(xiàn)一個(gè)煮蛋計(jì)時(shí)器。
這是完全從頭開始實(shí)現(xiàn)的,使用 Gio 這個(gè) Go GUI 庫實(shí)現(xiàn)的獨(dú)立 Go 應(yīng)用程序,會(huì)解釋每一個(gè)步驟。
實(shí)現(xiàn)的最終效果如下:

本系列大綱:
第 1 章 - 空窗口 第 2 章 - 標(biāo)題和大小 第 3 章 - 按鈕 第 4 章 - 低按鈕(Low Button) 第 5 章 - 重構(gòu) 第 6 章 - 帶邊距的按鈕 第 7 章 - 進(jìn)度條 第 8 章 - 畫圓 第 9 章 畫雞蛋 第 10 章 - 輸入沸騰時(shí)間
先看如何實(shí)現(xiàn)一個(gè)空窗口。
01 目標(biāo)
本節(jié)的目的是創(chuàng)建一個(gè)空白畫布,我們稍后可以在其上進(jìn)行繪制。

02 主要內(nèi)容
本節(jié)代碼主要做三件事:
導(dǎo)入 Gio 創(chuàng)建并調(diào)用 goroutine: 創(chuàng)建一個(gè)新窗口: w啟動(dòng)一個(gè)無限循環(huán),等待窗口中的事件,具體事件后續(xù)實(shí)現(xiàn)
接下來看看具體的代碼。
03 代碼
package?main
import?(
?"gioui.org/app"
)
func?main()?{
?go?func()?{
??//?創(chuàng)建一個(gè)新窗口(Window)
??w?:=?app.NewWindow()
??//?監(jiān)聽窗口的事件
??for?range?w.Events()?{
??}
?}()
?app.Main()
}
04 代碼詳解
代碼看起來很簡單。不過,還是花點(diǎn)時(shí)間看看發(fā)生了什么,畢竟 GUI 編程大家接觸的少。
1)我們導(dǎo)入了 gioui.org/app,它干什么用的?
我們需要查看文檔:https://pkg.go.dev/gioui.org/app:
包 app 為運(yùn)行圖形用戶界面的操作系統(tǒng)功能提供了一個(gè)獨(dú)立于平臺(tái)的界面。
這是很棒的特性。Gio 為我們處理所有與平臺(tái)相關(guān)的事情,這和 Go 一樣,是跨平臺(tái)的。
這可能比你意識(shí)到的要更重要。因?yàn)?,即使你今天的?yīng)用程序是單平臺(tái)的,但指不定哪天想遷移到其他平臺(tái)。
上篇文章簡單介紹了,Gio 對(duì)各個(gè)平臺(tái)都有支持,包括移動(dòng)端,甚至 tvOS。
2)goroutine 中的事件循環(huán)
事件循環(huán)的代碼是:
for range w.Events(),它循環(huán)監(jiān)聽窗口中的事件?,F(xiàn)在我們只是監(jiān)聽,并沒有對(duì)事件做任何處理。后續(xù)章節(jié)會(huì)實(shí)現(xiàn)。從 app.Main[1] 我們了解到:
因?yàn)?Main 在某些平臺(tái)上也是阻塞的,所以 Window 的事件循環(huán)必須在 goroutine 中運(yùn)行。
有 Go 經(jīng)驗(yàn)的同學(xué)應(yīng)該理解這塊內(nèi)容。因?yàn)槿绻?app.Main 不堵塞,最后 main 函數(shù)就返回,程序退出了。
一個(gè)沒有名字的 goroutine,即匿名函數(shù),被創(chuàng)建并運(yùn)行事件循環(huán)。由于它在 goroutine 中,它將與程序的其余部分同時(shí)運(yùn)行。
go?func?{
??//?...
}()這是 Go 語言常見的寫法。
3)最后上文提到的調(diào)用 app.Main() 啟動(dòng)程序,app.Main 的文檔提到:
Main 函數(shù)必須從程序的 main 函數(shù)中調(diào)用,以便將主線程的控制權(quán)移交給需要它的操作系統(tǒng)。Main 的具體實(shí)現(xiàn),不同系統(tǒng)不一樣。
比如 Unix、Windows 等系統(tǒng),直接調(diào)用 select {},感興趣的可以查看 gio 的源碼。
不過,上面的簡單代碼,在 Mac 下運(yùn)行不正常,會(huì) panic:
panic:?runtime?error:?invalid?memory?address?or?nil?pointer?dereference
[signal?SIGSEGV:?segmentation?violation?code=0x1?addr=0x0?pc=0x40f7f85]
可以先忽略。如果就想看看,可以用下面代碼:
package?main
import?(
?"gioui.org/app"
?"gioui.org/io/system"
?"gioui.org/layout"
?"gioui.org/op"
)
func?main()?{
?go?func()?{
??w?:=?app.NewWindow()
??var?ops?op.Ops
??for?e?:=?range?w.Events()?{
???switch?e?:=?e.(type)?{
???case?system.FrameEvent:
????gtx?:=?layout.NewContext(&ops,?e)
????e.Frame(gtx.Ops)
???}
??}
?}()
?app.Main()
}
05 小結(jié)
通過 gio 創(chuàng)建 GUI 應(yīng)用程序,和普通服務(wù)端應(yīng)用程序類似:
一個(gè)循環(huán),處理各種事件 一個(gè)堵塞,好比 select{} 或 HTTP 服務(wù)中的 http.ListenAndServe
趕緊動(dòng)手試試吧!
參考資料
app.Main: https://pkg.go.dev/gioui.org/app#hdr-Main
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。
堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio
