Pygame eingabe bei mehreren Zeilen

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.
Antworten
Henri.py
User
Beiträge: 20
Registriert: Sonntag 28. März 2021, 15:33

Hi,
ich programmiere momentan ein Programm, bei dem es auch eine Zeicheneingabe über verschiedene Zeilen geben soll, das kriege ich jedoch nicht richtig hin. Mein Ziel ist es, dass das Programm sobald die maximale Anzahl an Zeichen in einer Zeile erreicht ist, es in eine neue Zeile springt. Um den Text, soll es auch einen Text kasten geben. Den Text benötige ich dann in einer Variabel

Code: Alles auswählen

import pygame, sys

pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([800,800])
base_font = pygame.font.Font(None,32)
msgpm = ''
user_text = ''

input_rect = pygame.Rect(200,125,140,32)
input_rect1 = pygame.Rect(200,200,140,32)
color_active = pygame.Color('lightskyblue3')
color_passive = pygame.Color('gray15')
color = color_passive
color1 = color_passive
active = False
active1 = False
text1 = 'msgs per minute'
text2 = 'words to send'

while True:
    keys = pygame.key.get_pressed()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()


        if event.type == pygame.MOUSEBUTTONDOWN:
            if input_rect.collidepoint(event.pos):
                active = True
            else:
                active = False
            if input_rect1.collidepoint(event.pos):
                active1 = True
            else:
                active1 = False

        if event.type == pygame.KEYDOWN:
            if active == True:
                if event.key == pygame.K_BACKSPACE:
                    msgpm = msgpm[:-1]
                else:
                    msgpm += event.unicode
                    for letter in msgpm:
                            if letter not in '1234567890':
                                msgpm = msgpm[:-1]
            if active1 == True:
                if event.key == pygame.K_BACKSPACE:
                    user_text = user_text[:-1]
                else:
                    user_text += event.unicode

    screen.fill((0,0,0))

    if active:
        color = color_active
    else:
        color = color_passive
    if active1:
        color1 = color_active
    else:
        color1 = color_passive

    pygame.draw.rect(screen,color,input_rect,2)
    pygame.draw.rect(screen,color1,input_rect1,2)
    text_surface = base_font.render(msgpm,True,(255,255,255))
    text_surface1 = base_font.render(text1,True,(255,255,255))
    text_surface2 = base_font.render(user_text,True,(255,255,255))
    text_surface3 = base_font.render(text2,True,(255,255,255))
    screen.blit(text_surface,(input_rect.x + 5, input_rect.y + 5))
    screen.blit(text_surface1,(input_rect.x - 190, input_rect.y + 5))
    screen.blit(text_surface2,(input_rect1.x + 5, input_rect1.y + 5))
    screen.blit(text_surface3,(input_rect1.x - 190, input_rect1.y + 5))
    input_rect.w = max(100,text_surface.get_width() + 10)
    input_rect1.w = max(100,text_surface2.get_width() + 10)



    pygame.display.flip()
    clock.tick(60)
Danke schon mal für die Hilfe
LG Henri
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Hallo Henri.py,

laut PyGame Dokumentation ist das nicht direkt möglich:
http://www.pygame.org/docs/ref/font.htm ... ont.render

Der Text muss als Grafik eingefügt werden. Daher must du dir einen eigenen Algorithmus schreiben, der nachhält wieviele und welche Zeichen eingegeben wurden.
Daraus kannst du berechnen wieviele Zeilen der Text benötigt und welche Zeichen in welcher Zeile gerendert werden müssen.

Z.B. dort wo die neuen Zeichen zum 'user_text' hinzugefügt werden, etwa, müsste es die entsprechende Logik geben.

Code: Alles auswählen

user_text += event.unicode
Du könntest eine Liste von Strings, entsprechende für jede Zeile einen erzeugen.

Ich hoffe das macht Sinn!?
Henri.py
User
Beiträge: 20
Registriert: Sonntag 28. März 2021, 15:33

ja ich versuchs mal danke
ich kann mit pygame auch die länge des quadrates bestimmen in dem der text steht mit print(input_rect.w)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Henri.py: Anmerkungen zum Quelltext:

Namen nummerieren ist keine gute Idee. Auch nicht die Variante lauter Namen zu haben und die gleichen Namen dann noch einmal mit einer 1 angehängt.

Die Werte die `color` und `color1` vor der ``while``-Schleife zugewiesen werden, werden nirgends verwendet. Die beiden Zuweisungen kann man sich also sparen.

`keys` wird definiert, aber nirgends verwendet.

Wenn das einzige was man in den ``if``- und ``else``-Zweigen macht, die Zuweisung von `True` oder `False` an einen Namen ist, dann braucht man ``if`` und ``else`` nicht, denn man kann in dem Fall ja einfach das Ergebnis der Bedingung an den Namen binden:

Code: Alles auswählen

                if input_rect.collidepoint(event.pos):
                    active = True
                else:
                    active = False
                
                # =>
                
                active = input_rect.collidepoint(event.pos)
Man vergleicht nicht mit literalen Wahrheitswerten. Da kommt doch sowieso nur wieder ein Wahrheitswert bei heraus. Entweder der, den man sowieso schon hatte, dann hätte man den auch gleich ohne einen Vergleich nehmen können, oder man will auf das Gegenteil prüfen, dann kann man den einfach mit ``not`` negieren.

Das mit den Ziffern bei `msgpm` ist schräg bis falsch gelöst. Da wird *ein* Zeichen engehängt, und *danach* geht der Code *alle* Zeichen durch, und entfernt für jede Nicht-Ziffer ein Zeichen am Ende. Da nur ein ein Zeichen am Ende angehängt wurde, braucht man sich doch gar nicht *alle* Zeichen anschauen. Sollte, was ja eigentlich nicht passieren kann, vorher schon eine Nicht-Ziffer enthalten sein, wird dafür das letzte Zeichen entfernt, was auch immer das sein mag. Das ist macht keinen Sinn. Viel deutlicher und dann auch einfacher wäre es *vor* dem hinzufügen zu prüfen ob es eine Ziffer ist, und falls nicht, das Zeichen gar nicht erst hinzuzufügen:

Code: Alles auswählen

                        msgpm += event.unicode
                        for letter in msgpm:
                            if letter not in "1234567890":
                                msgpm = msgpm[:-1]
                        
                        # =>

                        if event.unicode in "1234567890":
                            msgpm += event.unicode
Es gehören einige von den Namen ohne die angehängte 1 und die Namen mit der angehängten 1 jeweils zusammen und im Code wird mit diesen Gruppen von Werten nahezu das gleiche gemacht. Text, Beschriftung, Rahmen, Aktiv-Flag ergeben zusammen jeweils so etwas wie ein Eingabefeld, und alles gibt es zweimal. Die Einzelwerte sollte man zu einem Objekt zusammenfassen.

Wobei man sich dann die `active`-Flags sparen kann, weil man nun mit *einem* Namen einfach das aktive Eingabefeld-Objekt bestimmen kann.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import pygame
from attr import attrib, attrs

ACTIVE_COLOR = pygame.Color("lightskyblue3")
PASSIVE_COLOR = pygame.Color("gray15")


@attrs
class Entry:
    label = attrib()
    input_rect = attrib()
    text = attrib(default="")
    character_filter = attrib(default=lambda character: True)

    def contains(self, position):
        return self.input_rect.collidepoint(position)

    def handle_key(self, event):
        if event.key == pygame.K_BACKSPACE:
            self.text = self.text[:-1]
        else:
            if self.character_filter(event.unicode):
                self.text += self.text + event.unicode

    def draw(self, surface, is_active, font):
        pygame.draw.rect(
            surface,
            ACTIVE_COLOR if is_active else PASSIVE_COLOR,
            self.input_rect,
            2,
        )
        text_surface = font.render(self.text, True, (255, 255, 255))
        surface.blit(
            text_surface, (self.input_rect.x + 5, self.input_rect.y + 5)
        )
        surface.blit(
            font.render(self.label, True, (255, 255, 255)),
            (self.input_rect.x - 190, self.input_rect.y + 5),
        )
        self.input_rect.w = max(100, text_surface.get_width() + 10)


def main():
    pygame.init()
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((800, 800))
    base_font = pygame.font.Font(None, 32)

    active_entry = None
    entries = [
        Entry(
            "msgs per minute",
            pygame.Rect(200, 125, 140, 32),
            character_filter=lambda character: character in "1234567890",
        ),
        Entry("words to send", pygame.Rect(200, 200, 140, 32)),
    ]

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return

            if event.type == pygame.MOUSEBUTTONDOWN:
                active_entry = None
                for entry in entries:
                    if entry.contains(event.pos):
                        active_entry = entry
                        break

            if event.type == pygame.KEYDOWN:
                if active_entry:
                    active_entry.handle_key(event)

        screen.fill((0, 0, 0))
        for entry in entries:
            entry.draw(screen, entry is active_entry, base_font)

        pygame.display.flip()
        clock.tick(60)


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten