更新代码
This commit is contained in:
1 parent
eda7e8bc96
commit
6ff632e0b0
19 files changed
+2033
-197
No files matched your search
@@ -0,0 +1,172 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
|
||||
"viewsagent/internal/logging"
|
||||
)
|
||||
|
||||
// logWindow 是独立的「运行日志」窗口。
|
||||
//
|
||||
// 程序是打包成无控制台窗口的桌面应用,日志不再往 cmd 输出,
|
||||
// 想看的时候点工具栏的日志按钮,这个窗口就会单独弹出来。
|
||||
type logWindow struct {
|
||||
buf *logging.Buffer
|
||||
win fyne.Window
|
||||
list *widget.List
|
||||
lines []string
|
||||
|
||||
// shown 是界面上已经显示的日志版本号,用原子变量跨 goroutine 传递。
|
||||
shown atomic.Uint64
|
||||
// stop 用来结束自动刷新的后台协程。
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
// newLogWindow 创建运行日志窗口。logDir 是日志文件所在目录。
|
||||
func newLogWindow(a fyne.App, buf *logging.Buffer, logDir string) *logWindow {
|
||||
w := &logWindow{buf: buf}
|
||||
|
||||
w.win = a.NewWindow("运行日志")
|
||||
w.win.Resize(fyne.NewSize(900, 560))
|
||||
// 点关闭只是隐藏窗口,方便下次再打开。
|
||||
w.win.SetCloseIntercept(func() {
|
||||
w.stopTicker()
|
||||
w.win.Hide()
|
||||
})
|
||||
w.win.SetContent(w.buildContent(logDir))
|
||||
return w
|
||||
}
|
||||
|
||||
// buildContent 组装窗口内容:上面一排按钮,下面是日志列表。
|
||||
func (w *logWindow) buildContent(logDir string) fyne.CanvasObject {
|
||||
w.list = widget.NewList(
|
||||
func() int { return len(w.lines) },
|
||||
func() fyne.CanvasObject {
|
||||
l := widget.NewLabel("")
|
||||
l.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
l.Truncation = fyne.TextTruncateOff
|
||||
return l
|
||||
},
|
||||
func(id widget.ListItemID, obj fyne.CanvasObject) {
|
||||
l, ok := obj.(*widget.Label)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if id < 0 || id >= len(w.lines) {
|
||||
l.SetText("")
|
||||
return
|
||||
}
|
||||
l.SetText(w.lines[id])
|
||||
},
|
||||
)
|
||||
|
||||
refreshBtn := widget.NewButtonWithIcon("刷新", theme.ViewRefreshIcon(), func() { w.refresh() })
|
||||
copyBtn := widget.NewButtonWithIcon("复制全部", theme.ContentCopyIcon(), func() { w.copyAll() })
|
||||
clearBtn := widget.NewButtonWithIcon("清空", theme.DeleteIcon(), func() { w.clear() })
|
||||
dirBtn := widget.NewButtonWithIcon("打开日志目录", theme.FolderOpenIcon(), func() { openDir(logDir) })
|
||||
|
||||
bar := container.NewHBox(refreshBtn, copyBtn, clearBtn, layout.NewSpacer(), dirBtn)
|
||||
hint := widget.NewLabel("日志同时写入该目录下的 app.log 文件,重启程序后仍可查看。")
|
||||
hint.Importance = widget.LowImportance
|
||||
hint.Wrapping = fyne.TextWrapWord
|
||||
|
||||
return container.NewBorder(bar, hint, nil, nil, w.list)
|
||||
}
|
||||
|
||||
// show 显示日志窗口(已经显示时只刷新内容并置前)。
|
||||
func (w *logWindow) show() {
|
||||
if w.win == nil {
|
||||
return
|
||||
}
|
||||
w.refresh()
|
||||
w.win.Show()
|
||||
w.win.RequestFocus()
|
||||
w.startTicker()
|
||||
}
|
||||
|
||||
// refresh 把最新日志刷到列表里,并滚动到底部。
|
||||
// 只能在 UI 线程调用。
|
||||
func (w *logWindow) refresh() {
|
||||
if w.list == nil {
|
||||
return
|
||||
}
|
||||
w.lines = w.buf.Lines()
|
||||
w.shown.Store(w.buf.Seq())
|
||||
w.list.Refresh()
|
||||
w.list.ScrollToBottom()
|
||||
}
|
||||
|
||||
// copyAll 把全部日志复制到剪贴板。
|
||||
func (w *logWindow) copyAll() {
|
||||
text := w.buf.Text()
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
fyne.CurrentApp().Clipboard().SetContent(text)
|
||||
}
|
||||
|
||||
// clear 清空内存里的日志显示(磁盘上的文件不受影响)。
|
||||
func (w *logWindow) clear() {
|
||||
w.buf.Clear()
|
||||
w.refresh()
|
||||
}
|
||||
|
||||
// startTicker 启动自动刷新:每秒检查一次有没有新日志。
|
||||
func (w *logWindow) startTicker() {
|
||||
if w.stop != nil {
|
||||
return
|
||||
}
|
||||
stop := make(chan struct{})
|
||||
w.stop = stop
|
||||
|
||||
go func() {
|
||||
t := time.NewTicker(time.Second)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case <-t.C:
|
||||
if w.buf.Seq() == w.shown.Load() {
|
||||
continue
|
||||
}
|
||||
fyne.Do(w.refresh)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// stopTicker 停止自动刷新,窗口隐藏时调用。
|
||||
func (w *logWindow) stopTicker() {
|
||||
if w.stop == nil {
|
||||
return
|
||||
}
|
||||
close(w.stop)
|
||||
w.stop = nil
|
||||
}
|
||||
|
||||
// close 停掉后台刷新,程序退出时调用。
|
||||
// 退出后 fyne.Do 不再走主线程队列,回调会直接执行,
|
||||
// 这时再去刷界面已经没有意义了。
|
||||
func (w *logWindow) close() {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
w.stopTicker()
|
||||
}
|
||||
|
||||
// openDir 用资源管理器打开目录。
|
||||
func openDir(dir string) {
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
_ = exec.Command("explorer", dir).Start()
|
||||
}
|
||||
Reference in new issue
Block a user