pythonuser-interfacebeeware

Capturing key and mouse events application wide in Beeware/Toga


I recently discovered Beeware/Toga and I'm considering switching to it: it's so much nicer than Tkinter and it seems to be more straighforward than wxPython, and faster both at dev and run time. So I tried to build a few toy apps, one of them being a look-alike of SimpleSudoku.

The UI is simple: it shows the value in a solved cell, or the remaining "candidates" in a cell that's still unsolved. Then you select a cell by clicking it or moving to it with the arrow keys, and type the value you want to insert, or Alt-<value> to remove a candidate value. There are a few more possibilities but let's ignore them for the moment.

The following code is a first attempt to build a preview (tested on Windows):

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

class SSClone(toga.App):
    
    def startup(self):
        main_box = toga.Box(style=Pack(direction=COLUMN, padding=5))
        
        self.tiles = []
        self.values = '123456789'
        self.size = len(self.values)
        label_text = ' '.join(self.values)
        tile_size = 40

        vbox = toga.Box(style=Pack(direction=COLUMN, padding=0, background_color='grey'))
        for i,r in enumerate(range(self.size)):
            hbox = toga.Box(style=Pack(direction=ROW, padding=(2,0,0,0), background_color='lightgrey'))
            for c in range(self.size-1):
                tile = toga.Label(label_text, style=Pack(padding=2, width=tile_size, height=tile_size, font_family='monospace', font_size=7))
                hbox.add(tile)
                self.tiles.append(tile)
            tile = toga.Label(f' {self.values[i]}', style=Pack(padding=2, width=tile_size, height=tile_size, font_family='monospace', font_size=15, alignment='center', font_weight='bold', background_color='aqua'))
            hbox.add(tile)
            self.tiles.append(tile)
            vbox.add(hbox)
        main_box.add(vbox)
        self.tiles[0].style.background_color='yellow'

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

I have used Label here, but one may also think of TextInput with readonly=True, Canvas (ouch!), or even a Button for each candidate (ouch!). All of these alternatives have disadvantages, but do at least solve some issues. What I would need, however, is a way to capture all keystrokes and mouse clicks at the application level. In Tkinter or wxPython I would simply bind the relevant events, respectively to the app or to a panel, but I can't find anything similar in Toga. Can someone help?

Thanks


Solution

  • For games, or other apps where the exact appearance matters, I think you'll probably need to use Canvas to get what you want. We've made some significant improvements to this in the current development version of Toga, which should be released within the next couple of weeks.

    Canvas has handlers for mouse events, but not for key presses. However, Toga does have an application-level key press handler in the Command API. There is some incomplete documentation for this (1, 2), which we'll improve in the next version. Meanwhile, you can find more example code in the example apps.