@mmueller-87: Modulebene ist der Code der Namen definiert die im ganzen Modul sichtbar sind. Also Importe, Funktionsdefinitionen, und Klassendefinitionen. Und Variablen. *Die* gehören da aber nicht hin, sofern sie nicht Konstanten sind. Wenn es Konstanten sind, dann werden sie auch gross geschrieben, damit man sie als solche erkennen kann.
Die Anzahl der Argumente die `oled_show()` fängt IMHO dann schon an grenzwertig zu werden, dass ich da drüber nachdenken würde alles was da zusätzlich gebraucht wird und bei jedem Aufruf gleich ist, zu einem Objekt zusammenzufassen.
Die Importe könnten aufgeräumt werden — da sind auch drei dabei, die überhaupt nicht benutzt werden.
Also für mich ist die Diskussion von Namen keine Frage: Gute Namen sind wichtig. Wenn man Sachen falsch benennt, ist es im besten Fall schwieriger zu verstehen was da passiert, im schlechten Fall ist der Namen falsch, man denkt man hat verstanden was passiert, aber es passiert eigentlich was anderes. Und wenn man etwas nicht oder falsch versteht, macht man unnötig Fehler.
Ausprobieren reicht nicht. Das mag so aussehen als ob alles funktioniert, muss es aber nicht. Und es kann sogar ”funktionieren”, aber eben nicht garantiert, also nicht immer oder auch unter bestimmten Umständen dann gar nicht. Wenn man zwei Knöpfe drückt ohne einen vorher wieder loszulassen wird der zweite Knopf den Prozess vom ersten nicht beendet. Python garantiert nicht wann (und im Grunde nicht einmal ob überhaupt) Objekte die im Programm nicht mehr zugreifbar sind, abgeräumt werden. Der erste Ton könnte also potentiell weiterlaufen solange das Programm läuft. Wenn man einen neuen Ton abspielt, muss man sicherstellen, das der alte auch wirklich aufhört.
`terminate()` reicht übrigens auch nicht aus, weil das nur den Prozess beendet, aber nicht den Rückgabecode abfragt, was zu Zombie-Prozessen führt. Das Betriebssystem räumt Prozesse nämlich erst ab wenn der Rückgabecode abgefragt wurde, weil erst dann sicher ist, dass der Elternprozess da keinen Zugriff mehr drauf haben will.
Zwischenstand (ungetestet):
Code: Alles auswählen
#!/usr/bin/env python3
import subprocess
from queue import Queue
import adafruit_ssd1306
import board
import busio
from board import SCL, SDA
from PIL import Image, ImageDraw, ImageFont
from RPi import GPIO
PIN_TO_FREQUENCY = {4: 88.5, 17: 103.5}
def get_rendered_size(font, text):
"""
Calculate the size of the text in pixels.
"""
left, top, right, bottom = font.getbbox(text)
return right - left, bottom - top
def show_text(oled, image, draw, font, text):
font_width, font_height = get_rendered_size(font, text)
#
# Draw a black filled box to clear the image.
#
draw.rectangle((0, 0, oled.width, oled.height), outline=0, fill=0)
draw.text(
(
oled.width // 2 - font_width // 2,
oled.height // 6 - font_height // 6,
),
text,
font=font,
fill=255,
)
oled.image(image)
oled.show()
def setup_pins():
queue = Queue()
GPIO.setmode(GPIO.BCM)
for pin in PIN_TO_FREQUENCY:
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(
pin, GPIO.BOTH, callback=queue.put, bouncetime=200
)
return queue
def stop_process(process):
if process:
process.terminate()
process.wait()
def main():
process = None
try:
with busio.I2C(SCL, SDA) as oled_i2c:
oled = adafruit_ssd1306.SSD1306_I2C(128, 32, oled_i2c)
image = Image.new("1", (oled.width, oled.height))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("paul.ttf", 30)
queue = setup_pins()
for channel in iter(queue.get, None):
if not GPIO.input(channel):
frequency = PIN_TO_FREQUENCY[channel]
stop_process(process)
process = subprocess.Popen(
[
"play",
"-n",
"-c1",
"synth",
"0",
"sine",
str(frequency),
]
)
else:
frequency = 0
stop_process(process)
show_text(oled, image, draw, font, f"{frequency} Hz")
finally:
GPIO.cleanup()
stop_process(process)
if __name__ == "__main__":
main()
Das Töne abspielen würde sich auch anbieten in einem Objekt zusammengefasst zu werden.