Turtle: Binärwerte in Matrizen in Pixel umwandeln

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
MikeOrMichael
User
Beiträge: 16
Registriert: Mittwoch 22. Februar 2012, 20:37

Hey Leute,

zuerst: danke, dass ihr das Thema angeklickt habt, und ich hoffe, ihr könnt helfen. Ich weiß, dass ihr noch am Anfang der Python-Programmation stehe, also wundert euch nicht, dass der Code recht dilletanisch geschrieben ist. Mein Problem ist folgendes: ich will eine Matrix mit Binärwerten in Pixel in Turtle umwandeln (1 -> schwarz ; 0 -> weiß), wobei jedoch die Form der Matrix erhalten bleiben soll (d.h. eine 2x4 Matrix soll bei den Koordinaten (x/y) entstehen: die Werte der Matrix werden dann je nach ihrem Wert in Pixel umgewandelt). Bsp:

0 1 0 1 -> w. s. w. s.
1 0 1 0 s. w. s. w.

Ich habe auch schon passende Funktionen:

Code: Alles auswählen

from turtle import *
def white(x, y):
	speed('fastest')
	up()
	goto (x, y)
	down()
	color("white")
	fillcolor("white")
	i = 0
	begin_fill()
	while i < 4:
		forward(10)
		right(90)
		i = i + 1
	end_fill()

def black(x, y):
	speed('fastest')
	up()
	goto (x, y)
	down()
	color("black")
	fillcolor("black")
	i = 0
	begin_fill()
	while i < 4:
		forward(8)
		right(90)
		i = i + 1
	end_fill()

Das Problem ist nun die Funktion, die alle Werte einer Matrix in Pixel umwandeln soll: diese ist wiefolgt:

Code: Alles auswählen

def pixel(mat, x, y):
	for int in mat:
		for yy in range(len(mat)):                        # Geht Reihe nach Reihe vor. yy = Reihe des Binärwerts i. Matirx / xx = Kolonne des Binärwerts i. Matrix
			if int == 0 in mat[yy]:
				xx = mat[yy].index(int)
				white((x - xx)*8, (y - yy)*8)   # Man subtrahiert um die relative Position des Pixels zur (x/y) Anfangsposition zu erhalten. *8 da in white/black "forward(8)"
			elif int == 1 in mat[yy]:
				xx = mat[yy].index(int)
				black((x - xx)*8, (y - yy)*8)
			else:
				pass
Das x-xx scheint verwirrend, doch wenn man zum Beispiel wieder eine 2x4 Matrix hat und zu den Koordinaten (10/10) geht, dann weiß man sich der letzte Wert
der Matrix an [1][3] sich an (10-3 = 7/10-1 = 9) befindet.

Das Problem ist, wenn ich die Funktion ausführen will, passiert nichts. Nichtmal das Turtle-Modul öffnet sich. Ich habe es probiert und beide Funktionen black und white funktionieren.

Wäre nett, wenn ihr euch die Zeit nehmen könnt, kurz reinzuschauen, zu korrigieren, zu verbessern, Bei meinem letzten Post hat das auch sehr geholfen, und ich möchte allen danken, die mir das letzte Mal dabei geholfen haben, und mich entschuldigen, dass ich vergessen habe, die Lösung zu posten. ja, ich bin ziemlich verplant :oops: .

MfG, Michael
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Mir ist leider überhaupt nicht klar, was Du machen willst. Vielleicht liegt es daran, dass ich von ``turtle`` keine Ahnung habe? Was ist denn bei Dir eine "Matrix"? Wie sieht die als Datentyp aus? Und was ist dann ein "Pixel"? Wieso muss da etwas umgewandelt werden?

Diese Informationen und Zusammenhänge fehlen mir.

Zu Deinen ersten beiden Funktionen kann ich jedoch sagen: Das ist pure Copy&Paste-Programmierung! (Was ist "Programmation" btw? :mrgreen: ) Schau Dir doch mal beide Funktionen an und frage Dich, worin die sich eigentlich unterscheiden?

Ich würde das spontan und ungetestet so umschreiben:

Code: Alles auswählen

def _paint(x, y, color):
    speed('fastest')
    up()
    goto (x, y)
    down()
    # hier sind die beiden einzigen Unterschiede!
    color(color)
    fillcolor(color)
    i = 0
    begin_fill()
    while i < 4:
        forward(10)
        right(90)
        i = i + 1
    end_fill()

def paint_black(x, y):
    _paint(x, y, "black")

def paint_white(x, y):
    _paint(x, y, "white")
In nehme mal an, dass die Zahl bei ``foward`` ein Typo ist? Oder soll da wirklich einmal 8 und einmal 10 stehen? Wenn ja, muss man das eben als zusätzlichen Parameter an ``_paint`` übergeben, wenn nein, siehst Du einen der Nachteile Deiner Vorgehensweise ;-)

Die Schleife sieht bei Dir schematisch so aus:

Code: Alles auswählen

i = 0
while i < 4:
    # tu was
    i = i + 1
Das kann man viel eleganter so lösen:

Code: Alles auswählen

for _ in xrange(4):
    # tu was
Damit sparst Du Dir die Laufvarable ``i``, deren Initialisierung und das manuelle Inkrementieren im Schleifenrumpf.

Grundsätzlich kannst Du Dir merken, dass man für alle Schleifen, bei denen man die Anzahl der Durchläufe im voraus kennt, immer ``for`` benutzen sollte.

Bitte beachte darüber hinaus PEP8 und verwende vier Leerzeichen für die Einrückung. Du verwendest vermutlich Tabs, die hier im Forum dann in 8 Leerzeichen resultieren.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ohne mir den Code jetzt im Detail angeschaut zu haben, würe ich zwei mögliche Fehlerquellen Vermuten: black/white arbeiten nicht richtig oder du rufst die pixel-Funktion falsch auf. Die beiden erstgenannten Funktionen solltest du direkt testen und prüfen, ob die Ergebnisse den Erwartungen entsprechen. Erst dann lohnt es sich über die pixel-Funktion nachzudenken. Bei letzterer wäre es interessant, wie genau du sie aufrufst. Dazu könntest du den gesamten Code hier posten und verlinken, dann kann man sich ein besseres Bild von deinem Programm machen. Hilfreich sind auch immer ein paar print-Anweisungen um zu testen, ob eine bestimmte Funktion überhaupt erreicht wird.

An deinem Code selbst gibt es noch sehr viel zu verbessern, da fehlt mir aber gerade ein wenig die Zeit zu. Es wird sich aber sicher jemand anderes finden (oder bereits gefunden haben, wie ich sehe).

Sebastian
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:Grundsätzlich kannst Du Dir merken, dass man für alle Schleifen, bei denen man die Anzahl der Durchläufe im voraus kennt, immer ``for`` benutzen sollte.
IMHO eine sehr ungewöhnliche Definition. Es gibt sooo viele Fälle, wo man die Anzahl der Durchläufe nicht kennt und trotzdem `for` benutzt...

`while` kommt halt immer dann ins Spiel, wenn es keinen Sinn macht, über die Elemente eines Iterables zu gehen. Das ist das einzige, was mir als Faustregel dazu einfällt. Alles weitere ist situationsabhängig.
BlackJack

@snafu: Ich verstehe Deinen Einwand nicht? So kenne ich das auch. Wenn man vor Schleifenbeginn weiss oder bestimmen kann wie viele Durchläufe die Schleife haben wird, verwendet man ``for`` und nicht ``while``.

Du scheinst daraus den Umkehrschluss ziehen zu wollen, was natürlich Unsinn ist. Wenn man nicht weiss wie viele Durchläufe es geben wird, folgt aus der Aussage ja nicht, dass man dafür kein ``for`` verwenden soll. Das hat aber auch niemand behauptet.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mir war tatsächlich nicht klar, dass sich die Aussage wohl mehr oder weniger auf das `for _ in xrange(4)`-Konstrukt bezogen hat. Missverständnis meinerseits. Sorry.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

snafu hat geschrieben:Mir war tatsächlich nicht klar, dass sich die Aussage wohl mehr oder weniger auf das `for _ in xrange(4)`-Konstrukt bezogen hat. Missverständnis meinerseits. Sorry.
Nein. Die Aussage steht so, wie sie ist und BlackJack es ebenfalls noch einmal formuliert hat! Das hat nichts mit diesem Spezialfall oben zu tun. Du machst - vermutlich - immer noch den Fehler aus "b" auf "a" schließen zu wollen... ich habe ja nie gesagt, dass man in anderen Fällen kein ``for`` benutzen sollte! Das wäre ja eben falsch.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
MikeOrMichael
User
Beiträge: 16
Registriert: Mittwoch 22. Februar 2012, 20:37

Zur Verständlichkeit des Themas kommt hier ein von mir meisterhaft gezeichnetes Schema(keine Ahnung wieso der Titel so ist, hab den Anfangstitel des Whitescreens genommen):

Bild

Die Umrandungen sollen später nicht da sein.
Die Funktion geht Reihe nach Reihe vor (Reihe = yy), überprüft den Wert der Zahl und ordnet ihr eine Farbe zu.

@Hyperion: Danke, werde ich in Betracht ziehen. Btw, stimmt, typo :mrgreen: .
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

MikeOrMichael hat geschrieben:Bild
Lädt bei mir nicht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Bei mir auch nicht — da war ein Schrägstrich zu viel am Ende der URL. Das hier sollte gehen:
Bild
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ok, nur wird mir aus dem Bild immer noch nicht klar, wieso man da irgend etwas mit Pixeln und "komplizierten" Rechungen macht.

Man bräuchte doch nur die "Matrix" durchgehen und eben für jeden schwarzen Eintrag die `paint_black` und für jeden weißen die `paint_white` Funktion aufrufen :K

Das würde mit meinem Vorschlag dann trivialer Weise so aussehen:

Code: Alles auswählen

paint_mapping = {0: paint_black, 1: paint_white}
for x, row in enumerate(matrix):
    for y, color in enumerate(row):
        paint_mapping[color](x, y)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
MikeOrMichael
User
Beiträge: 16
Registriert: Mittwoch 22. Februar 2012, 20:37

@BlackJack: Danke dir :oops: .
Ich habe die Funktion jetzt so geschrieben nach Hyperions Methode:

Code: Alles auswählen

def px(matrix, x, y):
	paint_mapping = {0: paint_black, 1: paint_white}
	for x, row in enumerate(matrix):
		for y, color in enumerate(row):
			paint_mapping[color](x, y)
Leider steht das hier:

Code: Alles auswählen

Traceback (most recent call last):
  File "<pyshell#40>", line 1, in <module>
    px(mat, 10, 10)
  File "<pyshell#35>", line 5, in px
    paint_mapping[color](x, y)
  File "<pyshell#27>", line 2, in paint_white
    _paint(x, y, "white")
  File "<pyshell#24>", line 6, in _paint
    color(color)
TypeError: 'str' object is not callable
Kann mir jemand die Funktion ganz kurz erklären und warum sie nicht funktioniert ? Ich wäre sehr dankbar. :mrgreen:
Wo ich zum ersten nicht verstehe, warum color am Ende der FUnktion wiederholt wird.
Außerdem ist mir gerade aufgefallen, dass die Farben verwechselt wurden :P .
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Kann es sein, dass paint_black und paint_white bei dir keine Funktionen, sondern strings sind? Warum?
BlackJack

@derdon: Nein, das Problem liegt in der `_paint()`-Funktion von Hyperion. Es kann halt immer nur ein Wert zur gleichen Zeit an einen Namen gebunden sein und der Aufruf ``color(color)``, der versucht die Funktion `color()` mit der Zeichenkette `color` als Argument aufzurufen geht natürlich nicht weil `color` nur an *ein* Objekt gebunden werden kann — in diesem Fall ist es die Zeichenkette 'black' oder 'white'. ;-)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ooops... ja, das kommt davon, wenn man Code nicht selber testet :oops:

Na gut, dann ändert man eben den Namen des Parameters von `color` in `colorname` o.ä.. Dann klappt's auch mit dem Aufruf von `color()` :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hyperion hat geschrieben:Na gut, dann ändert man eben den Namen des Parameters von `color` in `colorname` o.ä.. Dann klappt's auch mit dem Aufruf von `color()` :-D
Oder noch besser: man lässt die *-Importe sein.
Das Leben ist wie ein Tennisball.
MikeOrMichael
User
Beiträge: 16
Registriert: Mittwoch 22. Februar 2012, 20:37

@Hyperion: Okay :mrgreen: .

Ich habe das jetzt so umgeschrieben:

Code: Alles auswählen

def _paint(x, y, bcolor):
    speed('fastest')
    up()
    goto (x, y)
    down()
    color(bcolor)
    fillcolor(bcolor)
    i = 0
    begin_fill()
    while i < 4:
        forward(10)
        right(90)
        i = i + 1
    end_fill()

def px(matrix, x, y):
        paint_mapping = {0: paint_black, 1: paint_white}
        for x, row in enumerate(matrix):
                for y, bcolor in enumerate(row):
                        paint_mapping[bcolor](x, y)
Er malt mir dann genau ein schwarzes Viereck, dann läuft da nix mehr und es hängt.
Außerdem erkennt er mir nicht xrange, was ich doch schon sehr komisch finde. Range() erkennt er tadellos.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann verwendest Du vermutlich Python 3.x. Dort wurde aus `xrange` einfach `range`.

Wieso er hängt kann ich nicht sagen. Lass Dir halt mal die Werte der Schleifendurchläufe ausgeben. Dann siehst Du ja, ob er alles druchläuft, wie er soll. Wenn ja, dann liegt es an etwas anderem. Ich vermute mal, dass Du bei den Koordinaten noch einen Offset addieren / multiplizieren musst? Schließlich gehe ich ja nur Indexe durch... die laufen also von 0 bis 4 o.ä. `forward(10)` und `right(90)` hantieren da ja mit recht großen Zahlen. Wenn analog zu x und y die selbe Auflösung haben, ist ja klar, dass Du nicht viel siehst...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
MikeOrMichael
User
Beiträge: 16
Registriert: Mittwoch 22. Februar 2012, 20:37

@Hyperion: Könntest du mir ganz kurz die beiden for- Schleifen und die enumerate-Funktion erklären ? Ich verstehe zum Bsp. nicht, warum x,row im selben Loop sind, da x,y die Koordinaten sind und row halt die Reihe ist.
Muss nicht lang sein, ich will halt nur verstehen, um zu lernen :) .
BlackJack

@MikeOrMichael: `enumerate()` liefert Tupel mit einer laufenden Nummer als erstes Element und dem dazugehörigen Element aus dem übergebenen iterierbaren Objekt. Die Matrix besteht aus Zeilen und die Zeilen aus den einzelnen Spaltenwerten in der Zeile. Traditionell hätte zu den Zeilen eher die `y`-Koordinate gehört, aber das bewirkt ja erst einmal nur, dass die Matrix 90° gedreht gezeichnet wird. Probier doch einfach in einer Python-Shell mal aus was die Funktion so macht:

Code: Alles auswählen

In [214]: matrix = [[1, 0, 1, 0], [1, 0, 0, 1]]

In [216]: for item in enumerate(matrix):
   .....:     print item
   .....: 
(0, [1, 0, 1, 0])
(1, [1, 0, 0, 1])

In [217]: for y, row in enumerate(matrix):
   .....:     for x, cell in enumerate(row):
   .....:         print x, y, cell
   .....: 
0 0 1
1 0 0
2 0 1
3 0 0
0 1 1
1 1 0
2 1 0
3 1 1
Antworten