Проблема с отображением пробелов в многострочной строке при использовании TextStim в PsychoPy

Вопросы и ответы

Редактирование: использование другого пробела '\u00a0' решило проблему:

Я пытаюсь отобразить три строчки текста в рамках стимула с использованием Psychopy. Первая и третья строки содержат случайно сгенерированные слова (из словаря общих слов), а середина содержит триграмму, позиция которой варьируется в зависимости от целочисленного значения, переданного в функцию. Каждая строка содержит ровно 15 символов, и средняя строка включает триграмму, окруженную пробелами (ее позиция меняется). Когда я распечатываю строку в терминале, она всегда отображается правильно, но не когда она назначена TextStim.

Внизу я включил измененную версию, которая окружает триграмму словами и одним пробелом между, которая работает. Однако я надеюсь понять, почему первая версия не работает. Я просто не могу разобраться, что вызывает ошибку (ниже приведены изображения того, как ошибка влияет на отображение)

from psychopy import visual, event
import random
import csv
from numpy.random import multinomial

# создание словаря из файла
def word_dict(file):
    wordlist = []
    input_file = open(file)
    reader = csv.reader(input_file, delimiter=",")
    next(reader)
    for row in reader:
        for word in row:
            wordlist.append(word)
    input_file.close()

    words = {len(word) : [] for word in wordlist}
    for word in wordlist:
        words[len(word)].append(word)

    return words

test_dict = word_dict("wordlist.csv")

# эта функция генерирует текстовые стимулы для верхней и нижней строк в строке. 
# сначала определяет, сколько слов, а затем случайным образом выбирает слова соответствующей длины
def random_string(words: dict):
    n = random.randint(2,4)
    if n == 2:
        while True:
            numbers = multinomial(14, [1/2.] * 2) # определяет длину слов
            if 0 not in numbers:
                break
        string = (f"{random.choice(words[numbers[0]])} "
                  f"{random.choice(words[numbers[1]])}")
    elif n == 3:
        while True:
            numbers = multinomial(13, [1/3.] * 3)
            if 0 not in numbers:
                break
        string = (f"{random.choice(words[numbers[0]])} "
                  f"{random.choice(words[numbers[1]])} "
                  f"{random.choice(words[numbers[2]])}")
    elif n == 4:
        while True:
            numbers = multinomial(12, [1/4.] * 4)
            if 0 not in numbers:
                break
        string = (f"{random.choice(words[numbers[0]])} "
                  f"{random.choice(words[numbers[1]])} "
                  f"{random.choice(words[numbers[2]])} "
                  f"{random.choice(words[numbers[3]])}")

    return string

# Строка, которая назначается textStim, создается с помощью следующей функции
def generate_stimuli(pos, type):
    if type == "cue":
        stimulus = "###"
    elif type == "target":
        # Генерация случайных букв для средней строки
        letter1 = chr(random.randint(ord('a'), ord('z')))
        letter2 = chr(random.randint(ord('a'), ord('z')))
        letter3 = chr(random.randint(ord('a'), ord('z')))
        stimulus = letter1 + letter2 + letter3

    # задайте позицию для средней строки
    right_side = 13 - pos
    left_side = 12 - right_side
    center_string = (left_side * " ") + stimulus + (right_side * " ")

    # создайте случайные строки для выше и ниже цели
    above = random_string(test_dict) # text_dict это словарь общих слов
    below = random_string(test_dict) # text_dict это словарь общих слов
    if type == "cue":
        return center_string
    elif type == "target":
        return (above + '\n' + center_string + '\n' + below)

# Если я вызываю приведенную выше функцию и затем распечатываю результат, он работает правильно.  
# Единственная проблема возникает, когда я передаю это объекту текста в visual.TextStim 

win = visual.Window(
        fullscr=True,
        size=[1920, 1080], 
        screen=0,
        color=[+1] * 3,
        units="deg"
    )

target_string = visual.TextStim(
    win=win,
    anchorHoriz="center",
    anchorVert="center",
    font="courier new",
    color=[-1] * 3,
    units="deg",
    height=1.2
)

stimulus = generate_stimuli(1, "target")
target_string.text = stimulus

target_string.draw()
win.flip()
event.waitKeys()

win.close()

По сути, я должен передать строку с тремя строками ровно по 15 символов в TextStim, но средняя строка продолжает отображаться неправильно.

Если я заменю center_string = (left_side * " ") + stimulus + (right_side * " ") на center_string = (left_side * "*") + stimulus + (right_side * "*"), она отображается правильно. но если я вернусь к пустым пробелам, она снова не отображается

использование “*” с позицией == 1
Использование ” ” с позицией == 1

Я просто не могу понять, почему она не отображается правильно с пустыми пробелами. Могло бы anchorHoriz центрировать каждую строку независимо, и некоторые из пробелов не переходят? Я все еще не могу понять, после того как просмотрел код классов.

Измененная версия ниже работает отлично, но я все еще пытаюсь понять, почему выше не работает

def random_word(length: int, words: dict):
    word_length = length - 1
    return random.choice(words[word_length])

def generate_stimuli(pos, type):
    if type == "cue":
        stimulus = "###"
    elif type == "target":
        # Генерация случайных букв для триграммы
        letter1 = chr(random.randint(ord('a'), ord('z')))
        letter2 = chr(random.randint(ord('a'), ord('z')))
        letter3 = chr(random.randint(ord('a'), ord('z')))
        stimulus = letter1 + letter2 + letter3

    # задайте позицию для стимулов
    right_side = 13 - pos
    left_side = 12 - right_side

    # выберите слова для правой и левой сторон
    if right_side > 1:
        word_right = random_word(right_side, test_dict)
    else:
        word_right = " "

    if left_side > 1:
        word_left = random_word(left_side, test_dict)
    else:
        word_left = ""

    if type == "cue":
        center_string = (left_side * " ") + stimulus + (right_side * " ")
    elif type == "target":
        if pos == 1:
            center_string = f"{stimulus} {word_right}"
        elif pos == 14:
            center_string = f"{word_left} {stimulus}"
        else:
            center_string = f"{word_left} {stimulus} {word_right}"

    # создайте случайные строки для выше и ниже триграммы
    above = random_string(test_dict)
    below = random_string(test_dict)

    if type == "cue":
        return center_string
    elif type == "target":
        return (above + '\n' + center_string + '\n' + below)

Ответ

Проблема с отображением пробелов в рамках многопоточного стимула (TextStim) в PsychoPy может быть связана с тем, что стандартный пробел (‘ ‘) не интерпретируется в контексте текстового объекта. Вместо этого PsychoPy может игнорировать последовательности пробелов, что и приводит к искажениям в отображении текста.

Решение проблемы

Как вы уже отметили, использование или замена пробелов символом неразрывного пробела ('\u00a0') решает проблему. Это происходит потому, что неразрывный пробел интерпретируется как символ, предназначенный для отображения, что позволяет PsychoPy правильно отобразить текст.

Вот упрощенное решение вашей проблемы, которое позволяет корректно отображать пробелы:

  1. Замените все пробелы в строке на '\u00a0' перед тем, как назначить текст объекту TextStim:
center_string = (left_side * '\u00a0') + stimulus + (right_side * '\u00a0')
  1. Обновите код вашего функции generate_stimuli:
def generate_stimuli(pos, type):
    if type == "cue":
        stimulus = "###"
    elif type == "target":
        # Генерация случайных букв для триграмма
        letter1 = chr(random.randint(ord('a'), ord('z')))
        letter2 = chr(random.randint(ord('a'), ord('z')))
        letter3 = chr(random.randint(ord('a'), ord('z')))
        stimulus = letter1 + letter2 + letter3

    # Установка позиции для средней строки
    right_side = 13 - pos
    left_side = 12 - right_side
    center_string = (left_side * '\u00a0') + stimulus + (right_side * '\u00a0')

    # Создание случайных строк для выше и ниже цели
    above = random_string(test_dict) # текст_dict это словарь общих слов
    below = random_string(test_dict) # текст_dict это словарь общих слов
    if type == "cue":
        return center_string
    elif type == "target":
        return (above + '\n' + center_string + '\n' + below)

# Далее ваш основной код

Пояснение

Использование неразрывного пробела (код \u00a0) гарантирует, что PsychoPy корректно отобразит каждый пробел, так как он не будет игнорироваться.

Кроме того, стоит отметить, что поведение отображения, которое вы наблюдаете, может также зависеть от настроек шрифта и его размера в PsychoPy. Тестируйте разные шрифты и размеры, чтобы убедиться, что они не влияют на вывод.

Обратите внимание, что данное решение должно исправить вашу проблему с отображением и гарантировать, что текст будет виден так, как вы ожидаете, с правильными пробелами.

Оцените материал
Добавить комментарий

Капча загружается...