Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting stdout in Python into PyGame

I have a pretty strange use-case here, I am trying to put together a handful of simple programs for my students that will help them learn python. To get it working I have a PyGame window embedded in a TKinter frame, and I need to redirect stdout to change something in the PyGame window. I have the redirecting working, if I redirect it to a file it works fine, but if I try to change the text it doesn't work. I hard-coded a string into the PyGame text changing code and that works, but it won't work with the redirected text for some reason.

The redirecting class:

class PrintTest:
    def __init__(self, file, game):
        self.f = file
        self.game = game
    
    def write(self, t):
        f.write(t)

        self.game.text = game.font.render(t, True, self.game.text_colors[1], self.game.text_colors[2])
        self.game.textRect = self.game.text.get_rect()

        self.game.textRect.center = (300, 300)

    def flush(self):
        pass

the game class:

class Game:
    def __init__(self, root):
        self.root = root
        embed = tk.Frame(root, width=600, height=600)
        embed.pack(side=tk.LEFT)

        os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
        if platform.system == "Windows":
            os.environ['SDL_VIDEODRIVER'] = 'windib'

        self.text_colors = [(255,255,255),
                            (0,255,0),
                            (0,0,128)]

        # initialize a pygame display
        pygame.init()
        self.screen = pygame.display.set_mode((600, 600))
        self.screen.fill(pygame.Color('red'))
        self.clock = pygame.time.Clock()

        self.font = pygame.font.Font('freesansbold.ttf', 32)
        self.text = self.font.render('Hello, world!', True, self.text_colors[1], self.text_colors[2])
        self.textRect = self.text.get_rect()

        self.textRect.center = (300, 300)

        # TK Creation
        helv = font.Font(family='Helvetica', size=18, weight='bold')
        self.button = tk.Button(root, 
                                text="Change text",
                                font=helv,
                                command=self.change_text).pack()

        self.user_input = tk.StringVar()
        self.textbox = ttk.Entry(root, width=15, textvariable=self.user_input)
        self.textbox.pack()
        # ---------------------------------------------------------------

    def change_text(self):
        print(self.user_input.get())

    def run(self):
        # Pygame loop
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

            self.screen.fill(pygame.Color('red'))    

            self.screen.blit(self.text, self.textRect)

            pygame.display.update()

            try:
                self.root.update()
            except tk.TclError:
                running = False   
        pygame.quit()

and I set up the stdout like this:

try:
    root = tk.Tk()
    root.geometry('1000x600')

    game = Game(root)

    f = open('text.txt', 'w')
    sl = PrintTest(f, game)
    sys.stdout = sl

    game.run()
except Exception:
    traceback.print_exc()
    pygame.quit()

When I run this as it is, if I type hello in the box, hello is printed to the file, but a null character is put into the pygame box. I don't really know PyGame well enough to know if it is an issue on that end or a redirecting issue. Any help would be greatly appreciated!

(In case you are wondering what the use-case is here, I am going to have them 'finish' some programs to make something happen in PyGame. So if they type print('Hello Friend!') into the given field, it will redirect that to be dialog for someone in the PyGame box. It may not work in the long run, but I gotta get past this to really figure that out)

Edit:

So the problem is that the write function is being called twice for some reason when I click the button, it is calling print on the typed in string then again on an empty string. Still not sure how to fix it, but at least I found the problem

like image 284
Jacob D Avatar asked Aug 19 '20 01:08

Jacob D


People also ask

What does Pygame flip do?

To flip the image we need to use pygame. transform. flip(Surface, xbool, ybool) method which is called to flip the image in vertical direction or horizontal direction according to our needs.


1 Answers

Okay, so I found the problem.

It looks as though print sends two things to stdout, the string to print and the ending character. So it was overwriting the string I wanted printed with a newline character. I changed my PrintTest class to accommodate for this:

class PrintTest:
    def __init__(self, file, game):
        self.f = file
        self.game = game

    def write(self, t):
        if t != '\n':
            self.f.write(t)
            self.game.text, self.game.textRect = game.font.render(t, self.game.text_colors[2])

            self.game.textRect.center = (300, 300)

    def flush(self):
        pass
like image 51
Jacob D Avatar answered Oct 18 '22 04:10

Jacob D