@DatMichi: Auch wenn es gut aussieht noch ein paar Anmerkungen:
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert.
Leerzeichen um Zuweisungen und nach Kommas machen den Quelltext leichter lesbar.
Das `Image`-Modul sollte explizit aus dem `PIL`-Package importiert werden. Das es einfach so importierbar ist, ist „deprecated“, wird also in einer Folgeversion von `PIL` oder Pillow nicht mehr funktionieren.
Die `sys.argv`-Zugriffe im Code zu verteilen ist unübersichtlich. Jemand der den Code nicht kennt, und herausfinden möchte mit welchen Argumenten er das Programm aufrufen muss, ist gezwungen sich den gesamten Code durchzulesen um die Zugriffe auf die Kommandozeilenargumente zu finden, und muss dann auch noch herausfinden was die Werte die er angeben muss, bedeuten.
Kryptische Abkürzungen bei Namen sollte man vermeiden. Also `image` statt `im` und `pixels` statt `pix`.
Was soll die 1 bei `led1` bedeuten? Nicht nur das es gar keine weiteren LEDs gibt, will man Namen auch nicht durchnummerieren. Das ist in der Regel ein Zeichen das man entweder bessere Namen braucht, oder das man die Werte eigentlich in eine Datenstruktur packen möchte. Meistens eine Liste.
Für ”magische Zahlen” wie die PIN-Nummern definiert man am Anfang Konstanten. Dann weiss man im Programm immer wenn sie verwendet werden, was sie bedeuten, und man kann sie am Anfang des Quelltextes leicht verändern.
Die Werte für Breite und Höhe des Bildes würde ich auch an Namen binden, damit es verständlicher wird.
Dann kommt mit `x1` und `x2` wieder ein nummerierter Name. Was in diesem Fall unnötig ist, weil man in beiden Schleifen einfach `x` verwenden kann. Das `x` hat ja nur innerhalb der jeweiligen Schleife eine Bedeutung. Und in beiden Schleifen die gleiche Bedeutung. Also warum verschiedene Namen?
Wenn man das geändert hat, sieht man, dass die beiden Schleifenkörper exakt die gleichen fünf Zeilen Code enthalten. Sowohl Code als auch Daten sollte man nicht wiederholen. Das verletzt das DRY-Prinzip („Don't Repeat Yourself“). Kopien blähen den Code unnötig auf und erhöhen die Fehleranfälligkeit bei Änderungen, weil man Änderungen immer an allen Kopien durchführen muss, und auch bei allen äquivalent.
Eine Änderung wäre beispielsweise aus den drei Zeilen für die `r`, `g`, `b`-Zuweisungen *eine* ohne die Indexzahlen zu machen, in dem man das Pixel einfach auf die drei Namen entpackt:
Code: Alles auswählen
r = pixels[x, y][0]
g = pixels[x, y][1]
b = pixels[x, y][2]
# =>
r, g, b = pixels[x, y]
Da mit jedem der drei Werte das gleiche gemacht wird, braucht man die Namen aber auch nicht wirklich. Das lässt sich gleich bei der Zuweisung an das `color`-Attribut per „list comprehension“ erledigen.
Umgekehrte `range()`\es zu berechnen ist fehleranfällig. Ich weiss zum Beispiel jetzt nicht ob es Absicht von Dir ist, das `x` von 0 bis `width`-1 hochgezählt, dann aber nur von `width`-1 bis 2 runtergezählt wird:
Code: Alles auswählen
In [16]: range(10)
Out[16]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [17]: range(10-1, 1, -1)
Out[17]: [9, 8, 7, 6, 5, 4, 3, 2]
Ich vermute mal Du wolltest eigentlich komplett wieder runter. `reversed()` ist da IMHO leichter zu verstehen:
Code: Alles auswählen
In [18]: list(reversed(range(10)))
Out[18]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Und um aus den beiden inneren Schleifen eine einzige Schleife zu machen, braucht man dann noch `itertools.chain()`. Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import sys
from itertools import chain
from time import sleep
from gpiozero import RGBLED
from PIL import Image
RED_PIN = 16
GREEN_PIN = 20
BLUE_PIN = 21
def main():
image_filename = sys.argv[1]
delay = float(sys.argv[2])
image = Image.open(image_filename).convert('RGB')
width, height = image.size
pixels = image.load()
led = RGBLED(RED_PIN, GREEN_PIN, BLUE_PIN)
for y in range(height):
for x in chain(range(width), reversed(range(width))):
led.color = [rgb_component / 255 for rgb_component in pixels[x, y]]
sleep(delay)
if __name__ == '__main__':
main()