M5CardComputer Keyboard

Aus Micropython Referenz
Zur Navigation springen Zur Suche springen

Die Keyboardabfrage ist interrupt gesteuert.

Keyboard initialisieren[Bearbeiten | Quelltext bearbeiten]

  1. Eine Instanz erzeugen
  2. die ISR schreiben
  3. die ISR dem Keyboardinterrupt zuweisen.
kb = MatrixKeyboard()

def kb_pressed_event(kb_0):
    pass

kb.set_callback(kb_pressed_event)
...
kb.tick() # steht in der MainLoop -> Grund?

Methoden von MatrixKeyboard[Bearbeiten | Quelltext bearbeiten]

kb.tick()
ist in der Mainloop erforderlich damit die Keyboard Abfrage funktioniert. Es scheint so, als ob diese Methode die Abfrage durchführt. Wenn sleep() in der Schleife vorkommt, wird nur entsprechend selten oder gar nicht abgefragt.
Hiermit lässt sich die Keyboard Abfrage auch ein- und ausschalten:
if key_active:
    kb.tick()
key_active ist eine Variable die True oder False enthält.
True: Keyboard wird abgefragt.
False: Keyboard wird nicht abgefragt.
kb.get_string()
gibt des Wert der gedrückten Taste als String zurück
kb.get_key()
gibt des Wert der gedrückten Taste als Integer zurück
kb.is_pressed()
gibt True zurück, wenn eine Taste gedrückt wurde/ist oder False wenn nicht.
KeyCode.KEYCODE_BACKSPACE => 8
KeyCode.KEYCODE_TAB => 9
KeyCode.KEYCODE_ENTER => 13
KeyCode.KEYCODE_ESC => 27
KeyCode.KEYCODE_SPACE => 32
KeyCode.KEYCODE_DEL => 8
KeyCode.KEYCODE_LEFT => 180
KeyCode.KEYCODE_RIGHT => 183
KeyCode.KEYCODE_UP => 181
KeyCode.KEYCODE_DOWN => 182
Konstanten für die Sondertasten
Funktionieren nicht: KeyCode nicht defined



Keyboard benutzen[Bearbeiten | Quelltext bearbeiten]

Um auf das Keyboard zugreifen zu können ist zum Einen die Initialisierung des Keyboards (s.o.) und zum Anderen die Funktion kb.tick() erforderlich. Erst kb.tick() sorgt dafür, das die Keyboard Abfrage überhaupt erfolgt. Wenn kb.tick() in einer Schleife mit einer sleep() Anweisung steht, wird das Keyboard auch nur in dem Abstand abgefragt!

Die Verarbeitung der Tasteneingabe erfolgt in der ISR. Ihr Name ist beliebig. Er muss nur korrekt an den Interrupt übergeben werden:

kb.set_callback(kb_pressed_event)

Zum Auslesen der gedrückten Taste gibt es 2 Methoden. Mit der Einen wird eine Zahl, mit der Anderen ein Charakter zurückgegeben. Die gedrückte Taste kann nur entweder als Zahl oder als Charakter geholt werden. Erfolgen beide Abfragen direkt hintereinander, so liefert die 2. Abfrage None zurück!

x = kb.get_key()
y = kb.get_string()


Keyboard Puffer einsetzen[Bearbeiten | Quelltext bearbeiten]

dissy hat ein Blockly-Programm (UIFlow2) geschrieben, das Zeichen von der Tastatur holt, in einen Puffer schreibt und dann auf dem Display ausgibt. Bei der nächsten Ausgabe wird die alte Zeile nach oben geschoben und die neue Zeile darunter gesetzt. Die Eingabe ist mit Backspace editierbar. Ich habe den Micropythoncode mit seiner Zustimmung hier eingefügt:

import os, sys, io
import M5
from M5 import *
from hardware import *


# Vom GUI-Editor erstellt
title0 = None
line1 = None
line2 = None
line3 = None
line0 = None
buffer = None

kb = None


_keyBuffer = None
L = None
_screenLine1 = None
_screenLine2 = None
x = None
_screenLine3 = None
_keyBufMaxLen = None
keyCode = None
keyChar = None

# Describe this function...
def updateScreen():
  global _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar, title0, line1, line2, line3, line0, buffer, kb
  buffer.setText(str(_keyBuffer))
  line1.setText(str(_screenLine1))
  line2.setText(str(_screenLine2))
  line3.setText(str(_screenLine3))

# Describe this function...
def key_Delete():
  global _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar, title0, line1, line2, line3, line0, buffer, kb
  # We are a bit limited in block options here.
  # First get the length of the buffer and make sure
  #  there is a character to delete.
  # Next get a substring of the buffer, excluding the last char.
  #  and store that back into the buffer.
  L = len(_keyBuffer)
  if L < 1:
    return
  x = L - 1
  _keyBuffer = _keyBuffer[:x]
  buffer.setText(str(_keyBuffer))
  Speaker.tone(1000, 50)

# Describe this function...
def key_Enter():
  global _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar, title0, line1, line2, line3, line0, buffer, kb
  # First, scroll the history lines up for visual effect
  # Then put the buffer into the last line
  # Finally clear the buffer, play a happy sound
  _screenLine1 = _screenLine2
  _screenLine2 = _screenLine3
  _screenLine3 = _keyBuffer
  _keyBuffer = ''
  updateScreen()
  Speaker.tone(2000, 100)


def kb_pressed_event(kb_0):
  global title0, line1, line2, line3, line0, buffer, kb, _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar
  # Check if buffer is full
  # If so, empty it and play an error sound
  L = len(_keyBuffer)
  if L > _keyBufMaxLen:
    _keyBuffer = ''
    buffer.setText(str(' '))
    Speaker.tone(1000, 750)
  # We can not get both the key value and key string
  #   (Either get clears the last key pressed)
  # Start with the special cases by value
  keyCode = kb.get_key()
  if keyCode==8:
    # 8 is the Delete key
    key_Delete()
  elif keyCode==9:
    # 9 is the Tab key, which we will just ignore
    Speaker.tone(3000, 50)
  elif keyCode==13:
    # 13 is the Enter key
    key_Enter()
  else:
    # All other keys end up here, so lets convert the keycode to ascii,
    #  append to the buffer, and play a click.
    keyChar = ''
    keyChar = chr(keyCode)
    _keyBuffer = (str(_keyBuffer) + str(keyChar))
    buffer.setText(str(_keyBuffer))
    Speaker.tone(3000, 50)
  updateScreen()


def setup():
  global title0, line1, line2, line3, line0, buffer, kb, _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar

  M5.begin()
  title0 = Widgets.Title("Key Buffer Test", 3, 0xffffff, 0x0000FF, Widgets.FONTS.DejaVu18)
  line1 = Widgets.Label("line1", 10, 25, 1.0, 0xffffff, 0x000000, Widgets.FONTS.DejaVu18)
  line2 = Widgets.Label("line2", 10, 45, 1.0, 0xffffff, 0x000000, Widgets.FONTS.DejaVu18)
  line3 = Widgets.Label("line3", 10, 65, 1.0, 0xffffff, 0x000000, Widgets.FONTS.DejaVu18)
  line0 = Widgets.Line(5, 91, 235, 91, 0xffffff)
  buffer = Widgets.Label("Hello line buffer world!", 12, 101, 1.0, 0x00caff, 0x000000, Widgets.FONTS.DejaVu18)

  Widgets.fillScreen(0x000000)
  kb = MatrixKeyboard()
  kb.set_callback(kb_pressed_event)
  _keyBufMaxLen = 20
  _keyBuffer = ''
  title0.setVisible(True)
  line0.setVisible(True)
  _screenLine1 = ''
  _screenLine2 = ''
  _screenLine3 = ''
  updateScreen()


def loop():
  global title0, line1, line2, line3, line0, buffer, kb, _keyBuffer, L, _screenLine1, _screenLine2, x, _screenLine3, _keyBufMaxLen, keyCode, keyChar
  M5.update()
  kb.tick()


if __name__ == '__main__':
  try:
    setup()
    while True:
      loop()
  except (Exception, KeyboardInterrupt) as e:
    try:
      from utility import print_error_msg
      print_error_msg(e)
    except ImportError:
      print("please update to latest firmware")

Die Einrückung beträgt hier nur 2 Leerzeichen! Das macht M5Stack so.


Die Keyboard ISR schreiben[Bearbeiten | Quelltext bearbeiten]

Ich habe keinen Weg gefunden die ISR im laufenden Betrieb zu ändern. Deshalb sind bei der Entwicklung der ISR alle in Frage kommenden Fälle zu beachten! Das o.a. Programm von dissy kann als Orientierung dienen, wie die verschiedenen Aufgaben gelöst werden können.

Ich halte ein Modul für sinnvoll, das das Keyboard einrichtet, aber nur bei Bedarf abfragt und die Eingabe zurückgibt.

Ein Modul zur Keyboardabfrage[Bearbeiten | Quelltext bearbeiten]

English (translated with DeepL.com):
Based on dissy's Blockly design, I have created a module that fetches a complete input line (up to Enter) from the keyboard.
When importing, the keyboard is set up.
The keyboard query is started with the function kb_input(...). It is ended by entering Enter and kb_input(...) returns the entered text line.
Two text boxes (Widgets.Label(...)) are required.
In one a text specifying the desired input is output, in the other the input line is displayed.
The length of the input line must also be specified.
To use the module, only kb_input(...) needs to be imported:

Auf der Grundlage von dissy's Blockly Entwurf habe ich ein Modul erstellt das eine komplette Eingabezeile (bis Enter) vom Keyboard holt.
Beim Importieren wird das Keyboard eingerichtet.
Mit der Funktion kb_input(...) wird die Keyboardabfrage gestartet. Mit der Eingabe von Enter wird sie beendet und kb_input(...) gibt die eingegebene Textzeile zurück.
Es werden 2 Textboxen (Widgets.Label(...)) benötigt. In einer wird ein Text ausgegeben, der die gewünschte Eingabe spezifiziert, in der anderen wird die Eingabezeile angezeigt.
Ausserdem muss die Länge der Eingabezeile angegeben werden.

Zur Nutzung des Moduls braucht nur kb_input(...) import zu werden:

from cckeyboard import kb_import

Die Abfrage erfolgt dann so:

eingabezeile = kb_import('EingabeAnzeigeText', Textbox_für_die_Anzeige, Textbox_für_die_Eingabezeile, Maximale_Eingabelänge)

Hier des Script des Modules:

# cckeyboard.py
#
# english (translated by DeepL.com):
# This script is a module for Micropython.
# It enables the input of words or numbers with the keyboard of the CardComputer.
#
# The keyboard query is made with kb_input(message, msg_box, input_box, buff_len).
# The input is returned as a string.
# 
# The display takes place in two widgets.label().
# One for a text and the other for the input.
# When kb_input(message, msg_box, input_box, buff_len) is called,
# the message, the two Widgets.Label() and the buffer length are passed.
# 
# The calling program is responsible for the rest of the display.
# This module simply writes to the boxes and displays them.
# 
# The keyboard is initialized when this module is imported.
# from cckeyboard import kb_import
#

# Deutsch:
# Dieses Script ist ein Modul für Micropython.
# Es ermöglicht die Eingabe von Worten oder Zahlen
# mit der Tastatur des CardComputer.
#
# Die Tastaturabfrage erfolgt mit
# kb_input(message, msg_box, input_box, buff_len).
# Die Eingabe wird als String zurückgegeben.
#
# Die Anzeige erfolgt in zwei Widgets.Label().
# Eine für einen Text und die andere für die Eingabe
# Beim Aufruf von kb_input(message, msg_box, input_box, buff_len)
# wird die Mitteilung, die beiden Widgets.Label() und
# die Pufferlänge übergeben.
#
# Das aufrufende Programm ist für die übrige Gestaltung
# des Displays verantwortlich.
# Dieses Modul schreibt einfach in die Boxen und zeigt sie an.
#
# Beim Importieren dieses Modules wird das Keyboard initialisiert.
# from cckeyboard import kb_import
#

file_name = 'cckeyboard.py'
author = 'dissy', 'Peter Stöck'
kb_version = '01.00.00'
date = '16.04.2024'


import os, sys, io
import M5
from M5 import *
from hardware import *


# globale Variablen
kb_buffer = ''              # Puffer für die eingegebenen Werte
kb_buf_max_len = None       # Max. Pufferlänge

kb_input_box = None

# Keyboard Instanz initialisieren
kb = MatrixKeyboard()

# Schreibt Text in ein Widgets.Labell und zeigt es an
def update_box(val, box):
    box.setText(str(val))
    box.setVisible(True)


# Löscht das letzte Zeichen
def kb_delete():
    global kb_buffer
    l = len(kb_buffer)
    if l < 1:
        return
    x = l - 1
    kb_buffer = kb_buffer[:x]
    update_box(kb_buffer, kb_input_box)
    Speaker.tone(1000, 50)


# Bearbeitet die Eingabe von Enter
def kb_enter():
    global kb_aktive
    kb_aktive = False
    Speaker.tone(2000, 100)

# ISR
def kb_pressed_event(kb_0):
    global kb_buffer
    # Puffer voll?
    l = len(kb_buffer)
    if l > kb_buf_max_len:
        kb_buffer = ''
        update_box(kb_buffer, kb_input_box)
        Speaker.tone(1000, 750)
    # Tastendruck holen auswerten und ggf. an den Puffer anhängen
    keyCode = kb.get_key()
    if keyCode==8:                # 8 is the Delete key
        kb_delete()
    elif keyCode==9:              # 9 is the Tab key, which we will just ignore
        Speaker.tone(3000, 50)
    elif keyCode==13:             # 13 is the Enter key
        kb_enter()
    else:
        keyChar = ''
        keyChar = chr(keyCode)
        kb_buffer = (str(kb_buffer) + str(keyChar))
        Speaker.tone(3000, 50)
    update_box(kb_buffer, kb_input_box)
    
# Startet die Keyboardabfrage
def kb_input(message, msg_box, input_box, buf_len):
    global kb_aktive, kb_buffer, kb_msg_box, kb_input_box, kb_buf_max_len
    update_box(message, msg_box)
    kb_input_box = input_box
    kb_buffer = ''
    update_box(kb_buffer, kb_input_box)
    kb_buf_max_len = buf_len    
    kb_aktive = True 
    while kb_aktive:
        M5.update()
        kb.tick()
    return kb_buffer

# Keyboard ISR zuweisen
kb.set_callback(kb_pressed_event)

# Nun ist alles ist bereit

# Demo

def test_run():
    Widgets.setRotation(1)

    lab_msg = Widgets.Label("---", 10, 30, 1.0, 0xffffff, 0x0, Widgets.FONTS.DejaVu18)
    lab_input = Widgets.Label("---", 10, 60, 1.0, 0xffffff, 0x0, Widgets.FONTS.DejaVu24)

    lab_msg.setVisible(True)
    lab_input.setVisible(True)

    inp = ' '
    while inp:
        inp = ''
        inp = kb_input('Test-Eingabe:', lab_msg, lab_input, 13)
        print(inp)
    
if __name__ == '__main__':
    test_run()

Z.Z. müsst Ihr es aus dem Browser kopieren und in einen Editor einfügen. Dort unter dem Namen cckeyboard.py abspeichern.
Wenn Ihr das Modul direkt startet wird eine Demo ausgeführt, mit Ihr es testen könnt.

Keycode Konstanten[Bearbeiten | Quelltext bearbeiten]

Da die eingebauten Keycode-Konstanten nicht funktionieren, habe ich ein eigenes Modul geschrieben.
Es erfordert den Präfix keycodes anstatt KeyCode. Das kann aber durch ändern des Filenamen oder einer entsprechenden Import-Anweisung (siehe Code) geändert werden.

# keycodes.py
# use: import keycodes as KeyCode

KEYCODE_BACKSPACE = const(8)
KEYCODE_TAB = const(9)
KEYCODE_ENTER = const(13)
KEYCODE_ESC = const(27)
KEYCODE_SPACE = const(32)
KEYCODE_DEL = const(8)
KEYCODE_LEFT = const(180)
KEYCODE_RIGHT = const(183)
KEYCODE_UP = const(181)
KEYCODE_DOWN = const(182)

Lager[Bearbeiten | Quelltext bearbeiten]

Navigation[Bearbeiten | Quelltext bearbeiten]

Zurück zur M5CardComputer Startseite