Search code examples
pythonterminalcurses

How can I made a string that is updated by a thread reflect the changes on Python's curses?


I am planning to implement the curses library into an existing Python script for a client. The script will be run purely through SSH.

I am currently attempting to simulate some of the output that my script would generate.

In my 'testing-the-waters' script I have 3 variables: x, y, z.

I have a thread running alongside the curses loop that increments x, y, and z every x seconds. In the loop I am simply printing the three variables to the terminal screen.

The problem: The variables do not update until I provide some kind of input. How can I make the terminal string update the values automagically?

I am testing this on a Terminal on Kubuntu. I tried Urwid and ran into a similar problem.

import curses
import time
from threading import Thread

x, y, z = 0, 0, 0
go = True


def increment_ints():
    global x, y, z
    while go:
        x += 1
        y += 2
        z += 3
        time.sleep(3)


def main(screen):
    global go
    curses.initscr()
    screen.clear()
    while go:
        screen.addstr(0, 0, f"x: {x}, y = {y}, z = {z}")
        c = screen.getch()
        if c == ord('q'):
            go = False


if __name__ == '__main__':
    t = Thread(target=update_ints)
    t.setDaemon(True)
    t.start()
    curses.wrapper(main)

Expected: The values of x, y, and z are displayed and reflect the increments without input.

Actual results: The values of x, y, and z remain 1, 2, and 3 respectively and updates only when I press a key.

-----------Edit: This works as expected:

import curses
import time
from threading import Thread

x, y, z = 0, 0, 0
go = True
def update_ints():
    global x, y, z
    x += 1
    y += 2
    z += 3


def main(screen):
    global go
    curses.initscr()
    screen.clear()
    while go:
        update_ints()
        screen.addstr(0, 0, f"x: {x}, y = {y}, z = {z}")
        c = screen.getch()
        if c == ord('q'):
            go = False
        time.sleep(3)


if __name__ == '__main__':
    curses.wrapper(main)

But I will need the values to be updated from a thread.


Solution

  • The issue was that c = screen.getch() was blocking the loop and preventing the values from being updated.

    Removing...

    c = screen.getch()
    if c == ord('q'):
       go = False
    

    ... produced the intended results.

    Thank you NEGR KITAEC