Textformatierungsprogramm hat eine unheimliche Endlosschleife

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
Anagkai
User
Beiträge: 8
Registriert: Donnerstag 15. Mai 2014, 09:35

Ich möchte mit pygame png-Bilder inklusive formatiertem Text aus anderen png-Bildern und nicht formatiertem Text erstellen. Damit bin ich fast fertig, allerdings habe ich noch Probleme mit der Funktion zum Formatieren.
Die Funktion soll mit einer gegeben Breite die Positionen der Wörter so berechnen, dass sich eine Blocksatzformatierung ergibt. Allerdings hat die Funktion eine Endlosschleife, deren Existenz ich mir nicht erklären kann. Ich habe bei meinen Untersuchungen herausgefunden, dass die Variable n, die für das Ende der while-Schleife ja maßgeblich ist, immer wieder 0 wird. Außerdem habe ich print-Statements eingebaut um den Verlauf zu verfolgen, dabei wurde ein print-Statement, das in der Funktion ÜBER der while-Schleife ist, regelmäßig wieder aufgerufen, NACHDEM schon Code in der while-Schleife ausgeführt wurde. Auch das ist mir unerklärlich.
Das ist die Funktion, dabei ist textblock[1] der zu formatierende String.

Code: Alles auswählen

    def formattextblock(self, textblock, y0, size):
        x = self.vtext[3]
        y = y0
        n = 0
        w = 0
        shortstring = ""
        longstring = ""
        
        textfont = pygame.font.SysFont("georgia", size)   
        wordlist = textblock[1].split(" ")
        lines = []
        
        length = len(wordlist)
        while n < length:
            longstring = shortstring + wordlist[n]
            testlong = textfont.render(longstring, True, (0,0,0))
            if (testlong.get_width() + self.vtext[8]*(w-1) <= self.vtext[2]) and n < length - 1:
                shortstring = longstring
                w += 1
                n += 1
            else:
                if n == length - 1:
                    w += 1
                testshort = textfont.render(shortstring, True, (0,0,0))
                if w > 1:
                    actualinter = (self.vtext[2]-testshort.get_width())/(w-1)
                else:
                    actualinter = 0
                line = []
                nstart = n - w 
                while nstart < n:
                    word = [x, y, wordlist[nstart]]
                    line.append(word)
                    testword = textfont.render(wordlist[nstart], True, (0,0,0))
                    x += (testword.get_width() + actualinter)
                    nstart += 1
                lines.append(line)
                x = self.vtext[3]
                if n < length - 1:
                    y += (size + self.vtext[5])
                else: 
                    y += (size + self.vtext[9])
                longstring = ""
                shortstring = ""
                w = 0
                
        return lines, y                   
Vielen Dank schon mal für jede Hilfe.
Zuletzt geändert von Anonymous am Montag 3. Oktober 2016, 14:56, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Anagkai: Schreib mal ein Beispiel das man tatsächlich ausprobieren kann. Denn das was Du beschreibst kann nicht passieren. `n` kann innerhalb der Schleife nicht wieder 0 werden und etwas davor kann auch nicht ausgeführt werden. Das könnte nur passieren wenn die Methode irgendwo rekursiv aufgerufen wird. Dann ist das natürlich ein anderes `n` aber das ist dann natürlich 0 und es wird auch wieder der Code vor der Schleife ausgeführt.

Das `vtext`-Attribut mit den vielen magischen Indexzahlen hilft übrigens nicht unbedingt um das ganze lesbar und verständlich zu machen.
Anagkai
User
Beiträge: 8
Registriert: Donnerstag 15. Mai 2014, 09:35

Code: Alles auswählen

import pygame
from pygame.locals import *

pygame.init()

def formattextblock(textblock, y0, size):
    x = 250
    y = y0
    n = 0
    w = 0
    shortstring = ""
    longstring = ""
    
    textfont = pygame.font.SysFont("georgia", size)   
    wordlist = textblock.split(" ")
    lines = []
    
    length = len(wordlist)
    while n < length:
        print([n,w])
        longstring = shortstring + wordlist[n]
        testlong = textfont.render(longstring, True, (0,0,0))
        if(testlong.get_width()+20*(w-1) <= 2450 and n < length - 1):
            shortstring = longstring
            w += 1
            n += 1
        else:
            if n == length - 1:
                w += 1
            testshort = textfont.render(shortstring, True, (0,0,0))
            if w > 1:
                actualinter = (2450-testshort.get_width())/(w-1)
            else:
                actualinter = 0
            line = []
            nstart = n - w 
            while nstart < n:
                word = [x, y, wordlist[nstart]]
                line.append(word)
                testword = textfont.render(wordlist[nstart], True, (0,0,0))
                x += (testword.get_width() + actualinter)
                nstart += 1
            lines.append(line)
            x = 250
            if n < length - 1:
                y += (size + 20)
            else: 
                y += (size + 30)
            longstring = ""
            shortstring = ""
            w = 0
            
    return lines, y                   
    
formattextblock("This text for testing purposes is multiple lines long. This text for testing purposes is multiple lines long", 3000, 100)
Hier die Standalone-Version. Auch die produziert eine Endlosschleife.
Zuletzt geändert von Anonymous am Montag 3. Oktober 2016, 16:11, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Anagkai: Da wird `n` aber nicht 0. Es geht bis 17, also bis zum letzten Index weil es 18 Worte sind. Nur im ersten ``if``-Zweig wird n hochgezählt. Jetzt schau Dir mal an wann der ausgeführt wird. Also was dort die Bedingung an `n` ist, und was die Bedingung ist um die Schleife zu verlassen.
Anagkai
User
Beiträge: 8
Registriert: Donnerstag 15. Mai 2014, 09:35

Es muss natürlich

Code: Alles auswählen

while n < len(wordlist) - 1
heißen. Da war ich auch schon drauf gekommen, allerdings hat es nichts geholfen, deshalb habe ich es wieder geändert. Mir ist aber inzischen aufgefallen, dass zusätzlich in der übergeordneten Funktion eine Endlosschleife war, weil ich beim Iterieren über die Textblöcke das n+=1 vergessen habe.

Allerdings wird jetzt nur eine Zeile Text richtig formatiert und der Rest fehlt.
BlackJack

@Anagkai: Vielleicht hilft es ja die Funktion anders aufzubauen. Für meinen Geschmack macht die zu viel und ist zu unübersichtlich. Formuliere den Algorithmus in Worten, dann hast gute Funktionsnamen für die einzelnen Schritte.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Anagkai: die vielen Bedingungen und Zweige machen das Lesen und Verstehen sehr schwer. Du solltest das Problem in mehrere Teilprobleme zerlegen. Z.B. Berechnen der Wortbreite, und Aufteilen der Wörter auf eine Zeile und das Sammeln der Wörter bis eine Zeile voll ist:

Code: Alles auswählen

WIDTH = 2450
MIN_SPACE_WIDTH = 20

def create_line(line, y):
    space_width = (WIDTH - sum(w for w,_ in line)) / (len(line) - 1) if len(line) > 1 else 0
    result = []
    x = 250
    for word_width, word in line:
        line.append([x, y, word])
        x += width + space_width
    return result

def formattextblock(textblock, y0, size):
    y = y0
    textfont = pygame.font.SysFont("georgia", size)
    words = ((textfont.render(w, True, (0,0,0)).get_width(), w)
        for w in textblock.split())
    lines = []
    current_line = []
    current_line_width = 0
    for word_width, word in words:
        if current_line_width + word_width > WIDTH:
            lines.append(create_line(current_line, y))
            current_line = []
            current_line_width = 0
            y += size + 20
        current_line.append((width, word_word))
        current_line_width += width + MIN_SPACE_WIDTH
    lines.append(create_line(current_line, y))
    return lines, y + size + 30
Antworten