Search code examples
pythonpydictionary

pygame, removing special characters, check encoding for PyDictionary


I am a complete beginner so if the question seems obvious or stupid I apologize beforehand.

Working on an app in PyGame that reads a random word from a file and then searches it's definition using PyDictionary.

The code I started with is as follows;

WORD = random.choice(WORDS)
dict = PyDictionary()

meaning = dict.meaning(WORD)

the word is printed on screen without issue;

word_text = play_again_font.render(f"{WORD.upper()}", True, "#FFB90F")

but when I try to call the meaning I get;

meaning_text = meaning_font.render(f"{meaning()}", True, "#FFB90F")
TypeError: 'dict' object is not callable

As I found the PyDictionary was sending the output as dict which is indeed not callable.

So I tried a new approach.

My new code is; I have a python file with a WORDS list

WORD = WORDS
WORD_LIST = list(WORD)
word_n = random.choice(WORD_LIST)

I send the random word, defined in the variable word_n, to a txt file:

file = open('test.txt', 'wb')
pickle.dump(word_n,file)
file.close()

As the word has special characters and white spaces I read, clean and send to another file;

def clean_word():
    string = open('test.txt').read().lstrip().strip()
    new_str = ''.join([i for i in string if i.isalpha()]).strip()
    open('mean.txt', 'w').write(new_str)



def get_mean():
    clean = open('mean.txt').readlines()
    dicto = PyDictionary()
    meaning = dicto.meaning(clean)

The join function works in as far as it removes white spaces (NUL) and so, but it leaves this character before the word: œ these characters get removed: €EOT•ENONULNULNULŒSOHo”.

I've tried:

new_str = ''.join([i for i in string if i.isalpha()]).strip().replace('œ','')

This does not work as the œ symbol remains at the beginning of the word.

I've read that it could be encoding so tried:

new_str = ''.join([i for i in string if i.isalpha()]).strip().encode('utf-8')

also tried .encode('cp1252') but neither method works. Tried decode but this doesn't appear to be an option.

Right now I cannot send the word to PyDictionary as it returns "None".

meaning_text = meaning_font.render(f"{clean_word()}", True, "#FFB90F")

I'm assuming this is due to the symbol in front of the word.

How can I strip everything to be able to pass the variable to PyDictionary?

EDIT

This does not appear to be encoding.

I kept trying and have found that the word does get passed to PyDictionary and that it does produce a definition.

I have tested by;

def get_mean():
clean = open('mean.txt').readlines()
dicto = PyDictionary()
meaning = dicto.meaning(clean)
print(meaning)

The meaning is indeed shown on Terminal.

Another test I did was:

def get_mean():
clean = open('mean.txt').readline()
dicto = PyDictionary()
meaning = dicto.meaning(clean)
file = open('meaners.txt', 'wb')
pickle.dump(meaning, file)
file.close()

I can confirm that the file meaners.txt gets created meaning gets sent to it.

Why am I able to pass the variable word_n but not get_mean?

ie.

the below works and renders the word as expected;

play_again_font.render(f"{word_n.upper()}", True, "#FFB90F")

But this does not work:

meaning_text = meaning_font.render(f"{get_mean()}", True, "#FFB90F")

It always returns None even when the definition gets sent to the created file.

If I try to create a variable ie;

my_var = get_mean()

I get error;

meaning_text = meaning_font.render(f"{my_var()}", True, "#FFB90F") TypeError: 'NoneType' object is not callable

if I call the function directly it returns None;

meaning_text = meaning_font.render(f"{get_mean()}", True, "#FFB90F")

Do I need to redirect stdout perhaps?

Thanks in advance.

EDIT;

minimal reproducible example:

I have a words.py file which contains:

WORDS = [ "a", "bunch", "of", "words", "bared" ]

import random import pickle import words from PyDictionary import PyDictionary import pygame import pygame.display

WORD = words.WORDS
WORD_list = list(WORD)
word_n = random.choice(WORD_list)
file = open('mean.txt', 'wb')
pickle.dump(word_n,file)
file.close()
print(word_n)

def get_mean():
    dicto = PyDictionary()
    meaning = dicto.meaning(word_n)
    print(meaning)
    file = open('meaners.txt', 'wb')
    pickle.dump(meaning, file)
    file.close()

get_mean()

test.py" bared {'Verb': ['lay bare', 'make public', 'lay bare'], 'Adjective': ['having the head uncovered']} {'Verb': ['lay bare', 'make public', 'lay bare'], 'Adjective': ['having the head uncovered']}

Process finished with exit code 0

The issue I'm facing is when I try to render text with the get_mean() The word_n variable is displayed without problems.

test_text = play_again_font.render(f"{word_n.upper()}", True, "#FFB90F")
test_rect = test_text.get_rect(center=(WIDTH / 2, 650))

The above works and the random word is displayed on screen.

But the below, displaying the get_mean() does not display the definition.

meaning_font = pygame.font.SysFont('segoe-ui-symbol.ttf', 20)
meaning_text = meaning_font.render(f"{get_mean()}", True, "#FFB90F")
meaning_rect = meaning_text.get_rect(center=(WIDTH / 2, 200))

Solution

  • I had some trouble trying to recreate your problem with PyDictionary, it's dependent on a library for translation that no longer installs from PyPI.

    Fortunately a fork is available, PyMultiDictionary, that installs for me, so I've created a minimal example. You will likely want to handle the processing of the dictionary.meaning() function call with more care. If you change the dictionary, then the response format will change. One of your issues seems to be that you're not passing strings to the font.render() function.

    import random
    import pygame
    
    from PyMultiDictionary import MultiDictionary
    dictionary = MultiDictionary()
    
    def get_meaning(word):
        """Return the first meaning of a word"""
        # strip non-ascii
        word = word.encode('ascii',errors='ignore').decode()
        print(f"  Searching for {word} … ", end="")
        word_type, word_meaning, word_wikipedia = dictionary.meaning("en", word)
        print(f"found {word_meaning}")
        if word_meaning == "":
            word_meaning = "No definition found"
        return word_meaning
    
    def blit_text(surface, text, pos, font, color=pygame.Color('black')):
        """Multi-line text blit from https://stackoverflow.com/a/42015712/2280890"""
        words = [word.split(' ') for word in text.splitlines()]
        # 2D array where each row is a list of words.
        space = font.size(' ')[0]  # The width of a space.
        max_width, max_height = surface.get_size()
        x, y = pos
        for line in words:
            for word in line:
                word_surface = font.render(word, True, color)
                word_width, word_height = word_surface.get_size()
                if x + word_width >= max_width:
                    x = pos[0]  # Reset the x.
                    y += word_height  # Start on new row.
                surface.blit(word_surface, (x, y))
                x += word_width + space
            x = pos[0]  # Reset the x.
            y += word_height  # Start on new row.
    
    
    word_list = ["bunch", "Φof", "wordsΩ", "b✖areδ", "!#@$"]
    
    pygame.init()
    # grab the first installed font
    sys_font = pygame.font.SysFont(pygame.font.get_fonts()[0], 20)
    
    WIDTH, HEIGHT = 640, 480
    window = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Random Meanings")
    clock = pygame.time.Clock()
    txt_pos = (60,60)
    
    word = random.choice(word_list)
    meaning = get_meaning(word)
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
                elif event.key == pygame.K_SPACE:
                    word = random.choice(word_list)
                    meaning = get_meaning(word)
        # update state
        txt = f"{word}:\n{meaning}"
        # draw background
        window.fill(pygame.Color("white"))
        # draw text
        blit_text(window, txt, txt_pos, sys_font)
        # update screen
        pygame.display.flip()
        clock.tick(30)
    pygame.quit()
    

    This will display a window like:

    Meaning of bunch

    The simplistic special character removal performed is through conversion to ASCII as suggested in the comment and the multi-line text drawing is from this excellent answer.