You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Beeware/Toga中全局捕获键盘与鼠标事件?

在Toga框架中实现数独应用的全局事件捕获与UI方案

一、全局按键事件捕获(应用层面)

Toga的App类内置全局按键事件钩子,直接重写on_key_down/on_key_up方法即可捕获所有按键操作,无需绑定到单个控件:

核心实现代码示例

from toga import App, MainWindow, Grid, Label
from toga.style import Pack

class SudokuApp(App):
    def startup(self):
        self.main_window = MainWindow(title="Sudoku")
        self.grid = Grid()
        self.cells = []
        self.selected_cell = None  # 记录当前选中单元格的坐标(row, col)

        # 初始化9x9数独网格
        for row in range(9):
            row_cells = []
            for col in range(9):
                cell = Label(
                    text="",
                    style=Pack(
                        width=60,
                        height=60,
                        text_align="center",
                        font_size=18,
                        background_color="white",
                        border_width=1,
                        border_color="#ccc"
                    )
                )
                cell.on_click = self.handle_cell_click
                row_cells.append(cell)
                self.grid.add(cell, row=row, column=col)
            self.cells.append(row_cells)
        
        self.main_window.content = self.grid
        self.main_window.show()

    def on_key_down(self, widget, event):
        # 1. 方向键切换选中单元格
        if self.selected_cell:
            row, col = self.selected_cell
            if event.key == "ArrowUp" and row > 0:
                self.update_selected_cell(row-1, col)
            elif event.key == "ArrowDown" and row < 8:
                self.update_selected_cell(row+1, col)
            elif event.key == "ArrowLeft" and col > 0:
                self.update_selected_cell(row, col-1)
            elif event.key == "ArrowRight" and col < 8:
                self.update_selected_cell(row, col+1)
        
        # 2. 数字键填入数值
        if event.key in [str(i) for i in range(1, 10)]:
            if self.selected_cell:
                row, col = self.selected_cell
                self.cells[row][col].text = event.key
                # 此处添加清除候选数的逻辑
        
        # 3. Alt+数字移除候选数
        if event.alt and event.key in [str(i) for i in range(1, 10)]:
            if self.selected_cell:
                row, col = self.selected_cell
                # 此处添加移除对应候选数的逻辑

    def handle_cell_click(self, widget, event):
        # 定位点击的单元格坐标
        for row_idx, row in enumerate(self.cells):
            if widget in row:
                col_idx = row.index(widget)
                self.update_selected_cell(row_idx, col_idx)
                break

    def update_selected_cell(self, row, col):
        # 取消上一个选中单元格的样式
        if self.selected_cell:
            prev_row, prev_col = self.selected_cell
            self.cells[prev_row][prev_col].style.background_color = "white"
        # 设置当前选中单元格的高亮样式
        self.cells[row][col].style.background_color = "lightblue"
        self.selected_cell = (row, col)

if __name__ == "__main__":
    SudokuApp().main_loop()

二、全局鼠标点击事件处理

  • 数独场景下,最直接的方式是给每个单元格控件(如Label)绑定on_click事件,如上述代码中的handle_cell_click,精准处理单元格选中逻辑。
  • 若需捕获窗口内所有鼠标点击(包括空白区域),可给主容器(如Grid)绑定on_mouse_down事件:
    self.grid.on_mouse_down = self.handle_global_click
    
    注意:子控件的点击事件默认会冒泡到父容器,若子控件已处理on_click,需在子控件的事件处理函数中调用event.propagate(),父容器才能收到事件。

三、UI控件选型建议

针对数独的显示需求,推荐优化Label控件的使用:

  • 已填单元格:直接设置大字体显示数字,通过样式区分固定数(如黑色)和用户填入数(如蓝色)。
  • 未填单元格:用小字体显示候选数,通过空格或换行分隔,例如:
    # 设置候选数示例:显示1、2、3三个候选数
    self.cells[row][col].text = "1 2 3"
    self.cells[row][col].style.font_size = 12
    
  • 避免使用TextInput(readonly=True):默认带输入框样式,不符合数独单元格的简洁需求;Canvas绘制成本较高,不如Label灵活;Button自带点击反馈样式,需额外禁用交互效果,反而繁琐。

四、注意事项

  • Toga的样式系统在不同平台可能有细微差异,Windows下测试时可调整border_width、font_size等参数确保显示正常。
  • 若需要更精细的候选数排版,可嵌套Box布局在单元格内,用多个小Label分别显示每个候选数,实现更规整的排列。

内容的提问来源于stack exchange,提问作者gimix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:07:44