Golang中如何检测键盘按键按下

Golang中如何检测键盘按键按下 我正在尝试在连续循环运行时检测按键何时被按下。这个过程必须是非阻塞的,但到目前为止,我已经尝试了大约4个不同的库或建议,它们都是阻塞的!考虑到Go语言处理一些相对复杂的事情是多么容易,检测我按下Q键退出这样一个看似简单的任务竟然如此难以实现,这似乎令人难以置信。

PS. 回想80年代,我的Commodore VIC-20只用两行代码就能实现这个功能: get K$ if K$ = “Q” then end

1 回复

更多关于Golang中如何检测键盘按键按下的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现非阻塞键盘输入检测确实需要一些技巧,因为标准库没有直接提供这个功能。以下是几种有效的解决方案:

方案1:使用termbox-go库(推荐)

package main

import (
    "fmt"
    "github.com/nsf/termbox-go"
)

func main() {
    err := termbox.Init()
    if err != nil {
        panic(err)
    }
    defer termbox.Close()

    fmt.Println("按 Q 键退出...")

    eventQueue := make(chan termbox.Event)
    go func() {
        for {
            eventQueue <- termbox.PollEvent()
        }
    }()

    for {
        select {
        case ev := <-eventQueue:
            if ev.Type == termbox.EventKey {
                if ev.Ch == 'q' || ev.Ch == 'Q' || ev.Key == termbox.KeyCtrlC {
                    fmt.Println("\n检测到退出按键")
                    return
                }
                fmt.Printf("按键: %c (Key: %v)\n", ev.Ch, ev.Key)
            }
        default:
            // 非阻塞 - 这里可以执行其他任务
            // fmt.Println("执行其他任务...")
            // time.Sleep(100 * time.Millisecond)
        }
    }
}

方案2:使用golang.org/x/term(标准库扩展)

package main

import (
    "fmt"
    "os"
    "syscall"
    "time"
    "golang.org/x/term"
)

func main() {
    fmt.Println("按 Q 键退出...")

    // 将终端设置为原始模式
    oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
    if err != nil {
        panic(err)
    }
    defer term.Restore(int(os.Stdin.Fd()), oldState)

    for {
        // 非阻塞读取
        var buf [1]byte
        n, err := syscall.Read(int(os.Stdin.Fd()), buf[:])
        
        if err == nil && n > 0 {
            key := buf[0]
            if key == 'q' || key == 'Q' || key == 3 { // 3 = Ctrl+C
                fmt.Println("\n检测到退出按键")
                return
            }
            fmt.Printf("按键: %c\n", key)
        }
        
        // 执行其他任务
        // fmt.Println("执行其他任务...")
        time.Sleep(50 * time.Millisecond)
    }
}

方案3:使用tcell库(功能更全面)

package main

import (
    "fmt"
    "github.com/gdamore/tcell/v2"
)

func main() {
    screen, err := tcell.NewScreen()
    if err != nil {
        panic(err)
    }
    
    if err := screen.Init(); err != nil {
        panic(err)
    }
    defer screen.Fini()

    fmt.Println("按 Q 键退出...")

    for {
        ev := screen.PollEvent()
        switch ev := ev.(type) {
        case *tcell.EventKey:
            if ev.Key() == tcell.KeyRune {
                if ev.Rune() == 'q' || ev.Rune() == 'Q' {
                    fmt.Println("\n检测到退出按键")
                    return
                }
                fmt.Printf("按键: %c\n", ev.Rune())
            } else if ev.Key() == tcell.KeyCtrlC {
                fmt.Println("\n检测到Ctrl+C")
                return
            }
        case *tcell.EventResize:
            screen.Sync()
        }
    }
}

方案4:使用bufio.Scanner(简单但有限)

package main

import (
    "bufio"
    "fmt"
    "os"
    "time"
)

func main() {
    fmt.Println("按 Q 键退出...")

    scanner := bufio.NewScanner(os.Stdin)
    
    // 使用goroutine非阻塞读取
    inputChan := make(chan string)
    go func() {
        for scanner.Scan() {
            inputChan <- scanner.Text()
        }
    }()

    for {
        select {
        case input := <-inputChan:
            if input == "q" || input == "Q" {
                fmt.Println("检测到退出按键")
                return
            }
            fmt.Printf("输入: %s\n", input)
        default:
            // 非阻塞执行其他任务
            // fmt.Println("执行其他任务...")
            time.Sleep(100 * time.Millisecond)
        }
    }
}

安装依赖

# termbox-go
go get github.com/nsf/termbox-go

# tcell
go get github.com/gdamore/tcell/v2

# x/term
go get golang.org/x/term

termbox-go 通常是最佳选择,因为它轻量且专门为终端UI设计。这些方案都提供了非阻塞的键盘输入检测,允许你在循环中同时处理其他任务,类似于你提到的BASIC代码的简单性。

回到顶部