Buchstaben einzeln ausgeben , jeden mit zufälliger Grösse

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
hell
User
Beiträge: 5
Registriert: Montag 1. Oktober 2018, 18:01

Montag 1. Oktober 2018, 18:48

Hallo liebe Forum-User,
ich bin ziemlich neu mit python und pygame und komme eher von processing her.
Jetzt habe ich das Problem, die Buchstaben eines Textes animiert in zufälliger Grösse auf dem Bildschirm auszugeben. Ich habe absolut keine Ahnung, wie das geht. Hier der Code tw. von stack overflow. Meine Lösung mit size = random.randint(xx,yy) setzt die Buchstabengrösse
zufällig für alle Buchstaben.
Für Hilfe und Tips danke ich schon mal im Voraus.

import pygame, time, random
pygame.init()

SCREEN_WIDTH = 820
SCREEN_HEIGHT = 680
def main():
# Farben als Konstanten
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255,255, 0)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

background = pygame.Surface(screen.get_size())
background.fill( YELLOW )
background = background.convert()

# String, der Buchstabe für Buchstabe gezeichnet wird
str = "Das ist ein Text!"

size = random.randint( 12, 48)
x, y = SCREEN_WIDTH/2, SCREEN_HEIGHT/2

char = ' '
letter = 0

basicfont = pygame.font.SysFont(None , size)
for i in range(len(str)):
time.sleep(0.3)
char = char + str[letter]
textSurf = basicfont.render(char, True, GREEN, YELLOW)
textRect = textSurf.get_rect(center=(x, y))

letter += 1
print(char) # debug

screen.blit(background,(0,0)) # blitte background
screen.blit(textSurf, textRect)
#pygame.display.update(textrect)
pygame.display.flip() # update alles

clock = pygame.time.Clock()# das geht nicht
keepGoing = True
while keepGoing:
clock.tick(60)

for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False

if __name__ == "__main__":
main()
Benutzeravatar
__blackjack__
User
Beiträge: 1567
Registriert: Samstag 2. Juni 2018, 10:21

Dienstag 2. Oktober 2018, 11:49

@hell: Um das Problem zu lösen kommt man nicht darum herum etwas mehr Arbeit zu erledigen. Du darfst halt nicht *einmal* eine zufällige Grösse ermitteln, sondern musst das für jeden Buchstaben tun. Und die müssen dann natürlich auch einzeln gerendert werden, und Du musst sie dann auch einzeln auf den Screen „blitten“, und das entsprechend an Koordinaten um die Du Dich selbst kümmern musst.

Unüberwindbar scheinende Probleme zerlegt man üblicherweise in kleinere Teilprobleme. Und die dann wieder in Teilprobleme. Solange bis man Teilprobleme hat, die mit einer Funktion mit ein paar Zeilen Code einfach zu lösen sind. Diese Teillösung testet man dann, und wenn sie funktioniert, macht man mit der nächsten Teillösung weiter. Irgendwann hat man dann aus den (getesteten) Teillösungen eine Gesamtlösung zusammengestellt.

Noch ein paar Anmerkungen zum Quelltext: Der `init()`-Aufruf gehört nicht auf Modulebene, dafür die Konstantendefinitionen schon.

Üblicherweise wird pro ``import``-Anweisung nur ein Modul importiert.

Den Namen `str` an einen anderen Wert als den eingebauten `str`-Datentyp zu binden ist keine gute Idee. Abkürzungen bei Namen ja sowieso auch nicht, also kann man den Wert besser `string` nennen. Oder, da später damit in Verbindung stehende Werte `text_irgendwas` genannt werden, auch gerne `text`.

`char` und `letter` sind auch keine guten Namen. Zum einen kann man beide mit „Buchstabe“ übersetzen, das kann verwirrend sein, zum anderen steht keiner der Namen für einen Buchstaben — das ist definitiv verwirrend. `letter` macht an sich als Variable zudem keinen Sinn, da diese Variable in der Schleife immer den gleichen Wert wie `i` hat, `i` aber gar nicht verwendet wird.

Man bräuchte im vorliegenden Code aber letztendlich weder `i` noch `letter`, denn diese ``range(len(text))`` in der ``for``-Schleife ist in Python ein „anti pattern“ wenn mit der Laufvariablen ausschliesslich als Index auf `text` zugegriffen wird. Man kann auch *direkt* über die Zeichen einer Zeichenkette iterieren, ohne den Umweg über einen Index.

Namenskonvention in Python sind kleinbuchstaben_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import random
import time

import pygame


SCREEN_WIDTH = 820
SCREEN_HEIGHT = 680

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)


def main():
    pygame.init()

    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

    background = pygame.Surface(screen.get_size())
    background.fill(YELLOW)
    background = background.convert()
    
    text = 'Das ist ein Text!'
    
    size = random.randint(12, 48)   
    center = screen.get_rect().center

    basicfont = pygame.font.SysFont(None, size)
    text_part = ' '         
    for character in text:
        time.sleep(0.3)       
        text_part += character
        text_surface = basicfont.render(text_part, True, GREEN, YELLOW) 
        text_rect = text_surface.get_rect(center=center)
        print(text_part)
        screen.blit(background, (0, 0))
        screen.blit(text_surface, text_rect)
        pygame.display.flip()
       
    clock = pygame.time.Clock()
    keep_going = True
    while keep_going:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keep_going = False   


if __name__ == '__main__':
    main()

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
hell
User
Beiträge: 5
Registriert: Montag 1. Oktober 2018, 18:01

Dienstag 2. Oktober 2018, 13:52

Danke __blackjack__ für deinen ausführlichen Kommentar zu dem Code und für deine Mühe. Die Ausführung zur Verwendung von char und str verstehe ich, ebenso zur for-Schleife; hätte ich eigenlich wissen sollen, dass python das soooo! lösst.
Was das eigentliche Problem betrifft, so muss ich erst noch einmal über deine Hinweise nachdenken.
Die Koordinaten für die Plazierung der Buchstaben entnehme ich deinem Hinweis text_rect = text_surface.get_rect(center=center), also vielleicht
center += buchstabenbreite ? Mal sehen.
Was die zufällige Grösse der Buchstaben betrifft, so habe ich schon 'mal an eine Liste der Länge text mit Zufallszahlen im zufälligen Bereich
[min, max ) gedacht, die dann size in basic_font zugewiesen werden. Aber wie, weiss ich (noch ) nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 1567
Registriert: Samstag 2. Juni 2018, 10:21

Dienstag 2. Oktober 2018, 14:46

@hell: Im Moment wird die Mitte vom gesamten Text genommen, weil der ja komplett in einem Surface gerendert wird. Wenn man jeden Buchstaben, wegen der unterschiedlichen Grössen, in eigene Surfaces rendert, muss man sich um die Gesamtausmasse wenn man die nebeneinander blittet und den daraus resultierenden Mittelpunkt selbst kümmern. Wobei man sich dafür auch `Rect`-Objekte anschauen sollte und was die für Funktionalität bieten, damit man sich da keine unnötige Arbeit macht.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
hell
User
Beiträge: 5
Registriert: Montag 1. Oktober 2018, 18:01

Dienstag 2. Oktober 2018, 18:31

Danke noch mal. Was ich verstehe ist, dass ein Surface keine Position hat und deswegen die Position in rect gespeichert werden muss. Auch verstehe ich, dass ich die Gesamtbreite des Textes benötigt, um die Buchstaben voneinander abzusetzen. Wie um Himmels willen erzeugt man
jedoch für jeden Buchstaben ein eigenes Surface? Wenn text_surface.get_rect() ein Surface mit Position erzeugt, so muss ich das wiederholt aufrufen. Ich habe keinen Schimmer.
Benutzeravatar
__blackjack__
User
Beiträge: 1567
Registriert: Samstag 2. Juni 2018, 10:21

Dienstag 2. Oktober 2018, 19:16

Du weisst ja wie man mehr als einen Buchstaben als Surface bekommt. Das kann man genau so mit nur einem Buchstaben machen und das dann für jeden Buchstaben. Wie gesagt: Kleinere Teilprobleme lösen.

`text_surface.get_rect()` erzeugt kein Surface mit Position, sondern liefert einfach nur das `Rect` das die Ausmasse des Surface beschreibt. Also mit 0, 0 als obere linke Ecke und der Breite und Höhe des Surface. Man kann dann gleich beim Aufruf dieses `Rect` verschieben in dem man einen der vielen Punkte setzt die `Rect`-Objekte haben. In Deinem Beispiel ist das `center`.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
hell
User
Beiträge: 5
Registriert: Montag 1. Oktober 2018, 18:01

Dienstag 2. Oktober 2018, 22:56

Danke __blackjack__ für deine Geduld!!!!
ich habe noch lange herumprobiert und jetzt eine etwas umständliche Lösung gefunden.
Muss mich noch etwas in der pygame Doku umschauen und eine Lösung finden für
- zentrieren des Textes
- 'richtiger' Abstand der Buchstaben zueinander


#!/usr/bin/env python3
import random
import time

import pygame


SCREEN_WIDTH = 820
SCREEN_HEIGHT = 680

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)


def main():
pygame.init()

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

background = pygame.Surface(screen.get_size())
background.fill(YELLOW)
background = background.convert()

screen.blit(background, (0, 0))

text = 'Das ist mein Lösung'

center = screen.get_rect().center
y = screen.get_rect().centery

x = SCREEN_WIDTH /2
#y = SCREEN_HEIGHT / 2

# speichere len(text) Zufallswerte in Liste
sizes =[]
for i in range(len(text)):
sizes.append(random.randint(20, 60))

text_part = ' '

for (i ,character) in enumerate(text):

basicfont = pygame.font.SysFont(None, sizes)
time.sleep(1)
text_part = character
# wie bekomme ich richtigen Abstand der Buchstaben?
x += 20 # Abstand der Buchstaben

text_surface = basicfont.render(text_part, True, GREEN, YELLOW)
text_rect = text_surface.get_rect(center = (x , y))

screen.blit(text_surface, text_rect)
pygame.display.flip()





clock = pygame.time.Clock()
keep_going = True
while keep_going:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keep_going = False


if __name__ == '__main__':
main()
Benutzeravatar
__blackjack__
User
Beiträge: 1567
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 3. Oktober 2018, 10:21

@hell: `center` wird in dem Code jetzt nicht mehr benutzt und Du berechnest `x` selbst, so wie `center` das ja auch schon geliefert hätte‽

Für `sizes` würde sich eine „list comprehension“ anbieten.

`text_part` macht keinen Sinn mehr. Der Wert der vor der Schleife zugewiesen wird, wird nirgends verwendet und in der Schleife wird der Name immer an `character` gebunden. Warum? `character` enthält doch das was weiterverarbeitet werden soll, warum das noch mal zwischendurch umbenennen?

Das `i` das mit `enumerate()` erzeugt wird, wird nur als Index in die ”parallele” Liste `sizes` verwendet. Indexzugriffe sind in Python selten wirklich nötig und deswegen „unpythonisch“ wenn man sie vermeiden kann. Wenn man über mehr als eine Sequenz ”parallel” iterieren möchte, gibt es die `zip()`-Funktion.

Code: Alles auswählen

    sizes = [random.randint(20, 60) for i in range(len(text))]
    x, y = screen.get_rect().center
    for size, character in zip(sizes, text):
        basicfont = pygame.font.SysFont(None, size)
        time.sleep(0.3)
        text_surface = basicfont.render(character, True, GREEN) 
        text_rect = text_surface.get_rect(center=(x, y))
        screen.blit(text_surface, text_rect)
        pygame.display.flip()
        
        x += 20
Was die X-Position der Buchstaben und das zentrieren angeht, muss man halt wie schon gesagt selbst ein bisschen rechnen. Wie breit ein Buchstabe ist kann man von dem Surface abfragen in das man ihn gerendert hat, oder das `Rect` abfragen und einen Punkt von dem zur Bestimmung der Position des `Rect` des nächsten Buchstaben verwenden.

Wenn man zentrieren möchte, dann ist die Frage ob das wie ursprünglich im ersten Code so passieren soll, dass jede ausgegebene Teilzeichenkette zentriert sein soll, oder nur das Endergebnis, sich einmal positionierte Buchstaben sich also nicht mehr bewegen sollen. Man braucht in jedem Fall die Gesamtbreite der gerenderten Zeichen die zentriert werden sollen.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
hell
User
Beiträge: 5
Registriert: Montag 1. Oktober 2018, 18:01

Mittwoch 3. Oktober 2018, 10:45

__blackjack__ danke noch einmal für deine pythongerechten Vorschläge zum Code, ist für mich etwas schwierig in Python zu denken. Ich werde mir deine Vorschläge genau anschauen.
Wie schliesse ich jetzt den Thread ab?
Antworten