Sinisterly

Full Version: [Question] Python Curses: Multithreading Input | SOLVED
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Okay, I am making a stopwatch that by pressing the up key it adds more and more watch's, right now I only have it running on one.
When the user inputs the up arrow key, another stopwatch is added, this will just be done as an array and for loop.
The issue is, when using Curses, it locks the screen. No other inputs or anything can be interpreted.

I will put a comment on what doesn't work.

Code:
from time import sleep
from curses import wrapper

import curses
import threading


class StopWatch(threading.Thread):
   seconds = 0
   minutes = 0
   hours = 0

   def run(self):
       while True:
           sleep(1)
           StopWatch.seconds = StopWatch.seconds + 1
           if StopWatch.seconds == 60:
               StopWatch.minutes = StopWatch.minutes + 1
               StopWatch.seconds = 1
           if StopWatch.minutes == 60:
               StopWatch.hours = StopWatch.hours + 1
               StopWatch.minutes = 1

# The input here does not work
class GetInput(threading.Thread):
   def run(self):
       while True:
           c = main.stdscr.getch()
           if c == 27:
               break
       exit(main.stdscr)


def main(stdscr):
   curses.echo()
   StopWatch().start()
   GetInput().start()
   while True:
       sleep(0.10)
       stdscr.clear()
       strToDisp = '{} Seconds | {} Minutes | {} Hours'
       strToDisp = strToDisp.format(StopWatch.seconds,
                                    StopWatch.minutes,
                                    StopWatch.hours)
       stdscr.addstr(0, 0, strToDisp)
       stdscr.refresh()
   exit(stdscr)


def exit(stdscr):
   curses.nocbreak()
   stdscr.keypad(False)
   curses.echo()
   curses.endwin()

if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   wrapper(main)
(04-13-2017, 08:19 PM)ProfessorChill Wrote: [ -> ]Okay, I am making a stopwatch that by pressing the up key it adds more and more watch's, right now I only have it running on one.
When the user inputs the up arrow key, another stopwatch is added, this will just be done as an array and for loop.
The issue is, when using Curses, it locks the screen. No other inputs or anything can be interpreted.

I will put a comment on what doesn't work.

Code:
from time import sleep
from curses import wrapper

import curses
import threading


class StopWatch(threading.Thread):
   seconds = 0
   minutes = 0
   hours = 0

   def run(self):
       while True:
           sleep(1)
           StopWatch.seconds = StopWatch.seconds + 1
           if StopWatch.seconds == 60:
               StopWatch.minutes = StopWatch.minutes + 1
               StopWatch.seconds = 1
           if StopWatch.minutes == 60:
               StopWatch.hours = StopWatch.hours + 1
               StopWatch.minutes = 1

# The input here does not work
class GetInput(threading.Thread):
   def run(self):
       while True:
           c = main.stdscr.getch()
           if c == 27:
               break
       exit(main.stdscr)


def main(stdscr):
   curses.echo()
   StopWatch().start()
   GetInput().start()
   while True:
       sleep(0.10)
       stdscr.clear()
       strToDisp = '{} Seconds | {} Minutes | {} Hours'
       strToDisp = strToDisp.format(StopWatch.seconds,
                                    StopWatch.minutes,
                                    StopWatch.hours)
       stdscr.addstr(0, 0, strToDisp)
       stdscr.refresh()
   exit(stdscr)


def exit(stdscr):
   curses.nocbreak()
   stdscr.keypad(False)
   curses.echo()
   curses.endwin()

if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   wrapper(main)

Try stdscr.nodelay()
This will make getch non-blocking.
I think @vhsx might be right. Good job!
Let us know if it works, @ProfessorChill.

Quote:The curses library itself offers only very simple input mechanisms. Python’s support adds a text-input widget that makes up some of the lack.

The most common way to get input to a window is to use its getch() method. getch() pauses and waits for the user to hit a key, displaying it if echo() has been called earlier. You can optionally specify a coordinate to which the cursor should be moved before pausing.

It’s possible to change this behavior with the method nodelay(). After nodelay(1), getch() for the window becomes non-blocking and returns curses.ERR (a value of -1) when no input is ready. There’s also a halfdelay() function, which can be used to (in effect) set a timer on each getch(); if no input becomes available within a specified delay (measured in tenths of a second), curses raises an exception.

The getch() method returns an integer; if it’s between 0 and 255, it represents the ASCII code of the key pressed. Values greater than 255 are special keys such as Page Up, Home, or the cursor keys. You can compare the value returned to constants such as curses.KEY_PPAGE, curses.KEY_HOME, or curses.KEY_LEFT. Usually the main loop of your program will look something like this:
https://docs.python.org/2/howto/curses.html#user-input

Spoiler:
@ProfessorChill, I saw your question yesterday, but I didn't reply because I didn't know the answer without looking further. Curses (ncurses) has always been a thorn in my side. It's like they try to make it horrible on purpose. xD
(04-14-2017, 05:48 PM)vhsx Wrote: [ -> ]
(04-13-2017, 08:19 PM)ProfessorChill Wrote: [ -> ]Okay, I am making a stopwatch that by pressing the up key it adds more and more watch's, right now I only have it running on one.
When the user inputs the up arrow key, another stopwatch is added, this will just be done as an array and for loop.
The issue is, when using Curses, it locks the screen. No other inputs or anything can be interpreted.

I will put a comment on what doesn't work.

Code:
from time import sleep
from curses import wrapper

import curses
import threading


class StopWatch(threading.Thread):
   seconds = 0
   minutes = 0
   hours = 0

   def run(self):
       while True:
           sleep(1)
           StopWatch.seconds = StopWatch.seconds + 1
           if StopWatch.seconds == 60:
               StopWatch.minutes = StopWatch.minutes + 1
               StopWatch.seconds = 1
           if StopWatch.minutes == 60:
               StopWatch.hours = StopWatch.hours + 1
               StopWatch.minutes = 1

# The input here does not work
class GetInput(threading.Thread):
   def run(self):
       while True:
           c = main.stdscr.getch()
           if c == 27:
               break
       exit(main.stdscr)


def main(stdscr):
   curses.echo()
   StopWatch().start()
   GetInput().start()
   while True:
       sleep(0.10)
       stdscr.clear()
       strToDisp = '{} Seconds | {} Minutes | {} Hours'
       strToDisp = strToDisp.format(StopWatch.seconds,
                                    StopWatch.minutes,
                                    StopWatch.hours)
       stdscr.addstr(0, 0, strToDisp)
       stdscr.refresh()
   exit(stdscr)


def exit(stdscr):
   curses.nocbreak()
   stdscr.keypad(False)
   curses.echo()
   curses.endwin()

if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   wrapper(main)

Try stdscr.nodelay()
This will make getch non-blocking.

(04-14-2017, 06:32 PM)m0dem Wrote: [ -> ]I think @vhsx might be right.  Good job!
Let us know if it works, @ProfessorChill.

Quote:The curses library itself offers only very simple input mechanisms. Python’s support adds a text-input widget that makes up some of the lack.

The most common way to get input to a window is to use its getch() method. getch() pauses and waits for the user to hit a key, displaying it if echo() has been called earlier. You can optionally specify a coordinate to which the cursor should be moved before pausing.

It’s possible to change this behavior with the method nodelay(). After nodelay(1), getch() for the window becomes non-blocking and returns curses.ERR (a value of -1) when no input is ready. There’s also a halfdelay() function, which can be used to (in effect) set a timer on each getch(); if no input becomes available within a specified delay (measured in tenths of a second), curses raises an exception.

The getch() method returns an integer; if it’s between 0 and 255, it represents the ASCII code of the key pressed. Values greater than 255 are special keys such as Page Up, Home, or the cursor keys. You can compare the value returned to constants such as curses.KEY_PPAGE, curses.KEY_HOME, or curses.KEY_LEFT. Usually the main loop of your program will look something like this:
https://docs.python.org/2/howto/curses.html#user-input

Spoiler:
@ProfessorChill, I saw your question yesterday, but I didn't reply because I didn't know the answer without looking further.  Curses (ncurses) has always been a thorn in my side.  It's like they try to make it horrible on purpose.  xD

Hey, sorry about the delay! I will let you know when I do try what @vhsx mentioned!
Thank you guys for the help Smile
I'll be sure to post this on github after I post it if anyone is interested (even though it's a stupid little project :p)

And thank you a ton @m0dem for the research you went into! It has actually helped quite a bit. I am however using python 3 but I think the page I was linked to is still relevant regardless.
UPDATE: Thank you @vhsx! It worked perfectly after that!
At
Code:
if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   stdscr.nodelay(1) <--- It needed a 1 for true and 0 for false
   wrapper(main)

https://github.com/ProfessorChill/CLI-StopWatch
On the python website, if you're ever on a different version, there's a drop down to select a different version.
Also, in your code... the wrapper function handles the teardown of curses for you so you don't need to do all of that. The wrapper also intializes much of that stuff, so if you want to reduce redundancies in your code, you can take a lot of that out.
(04-14-2017, 08:46 PM)ProfessorChill Wrote: [ -> ]UPDATE: Thank you @vhsx! It worked perfectly after that!
At
Code:
if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   stdscr.nodelay(1) <--- It needed a 1 for true and 0 for false
   wrapper(main)

https://github.com/ProfessorChill/CLI-StopWatch

Glad to hear it!
(04-15-2017, 12:03 AM)vhsx Wrote: [ -> ]On the python website, if you're ever on a different version, there's a drop down to select a different version.
Also, in your code... the wrapper function handles the teardown of curses for you so you don't need to do all of that. The wrapper also intializes much of that stuff, so if you want to reduce redundancies in your code, you can take a lot of that out.

(04-15-2017, 12:04 AM)vhsx Wrote: [ -> ]
(04-14-2017, 08:46 PM)ProfessorChill Wrote: [ -> ]UPDATE: Thank you @vhsx! It worked perfectly after that!
At
Code:
if __name__ == '__main__':
   stdscr = curses.initscr()
   curses.noecho()
   curses.cbreak()
   stdscr.keypad(True)
   stdscr.nodelay(1) <--- It needed a 1 for true and 0 for false
   wrapper(main)

https://github.com/ProfessorChill/CLI-StopWatch

Glad to hear it!

Man, and I thought I read the documentation pretty well, hahaha.
To be honest I didn't see where it said wrapper handles the exit as well but thank you for mentioning that and I will include that in the github post.... When I find out how to update it :p
EDIT: I found out how to update it ^_^\
Great! I have no idea what this website is but your post came up during a google search. It was an easy enough question to answer.

Cheers!