I'm currently coding an app U.I with python/curses and I was wondering if it is possible to ask the user to press keys (cbreak mode) to hide or show some panels or windows while the U.I is continuously updating.
I read the official python docs about curses and made some tries but even with the use of the cbreak mode and the non-blocking input mode (nodelay) activated I am unable to make it work properly (I succeed in getting the user input but at the expense of blocking the U.I that is not what I want).
So my question is simple, is it possible ? And if yes, how ?
I may have mis-read the docs but I haven't found any alternative docs or example about it.
I thought about making the app multi-threaded but I didn't see how this can help me in this case.
Thank you for your help, advices or pointer to a detailed doc.
EDIT :
I finally ended up with the following multi-threaded code but it's not satisfying. The U.I is feeded as it has to be but after refreshing the display is borked.
I also do not understand why the curses.panel.hidden() returns False while the considered panel is hidden. It seems that refreshing the window associated with the panel unhide the panel or something like that. I'm really lost at this point !
import threading
import curses, curses.panel
import random
import time
gui = None
class ui:
def __init__(self):
self.feeder = feeder(self)
self.stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
curses.curs_set(0)
self.stdscr.keypad(1)
self.win1 = curses.newwin(10, 50, 0, 0)
self.win1.border(0)
self.pan1 = curses.panel.new_panel(self.win1)
self.win2 = curses.newwin(10, 50, 0, 0)
self.win2.border(0)
self.pan2 = curses.panel.new_panel(self.win2)
self.win3 = curses.newwin(10, 50, 12, 0)
self.win3.border(0)
self.pan3 = curses.panel.new_panel(self.win3)
self.win1.addstr(1, 1, "Window 1")
self.win2.addstr(1, 1, "Window 2")
self.win3.addstr(1, 1, "Press 's' to switch windows or 'q' to quit.")
self.pan1.hide()
self.win1.refresh()
curses.panel.update_panels()
self.win2.refresh()
self.feeder.start()
def ask(self):
while True:
self.win3.addstr(5,1, "Hidden = win1: "+str(self.pan1.hidden())+\
"win2:"+str(self.pan2.hidden()), 0)
self.win3.refresh()
k = self.win3.getkey()
if k == 's':
if self.pan1.hidden():
self.pan2.hide()
self.pan1.show()
self.win1.refresh()
self.win3.addstr(2, 1, "Pan1 restored")
else:
self.pan1.hide()
self.pan2.show()
self.win2.refresh()
self.win3.addstr(2, 1, "Pan2 restored")
self.win3.addstr(5,1, "Hidden = win1: "+\
str(self.pan1.hidden())+\
" win2:"+str(self.pan2.hidden()), 0)
elif k == 'q':
break
self.quit_ui()
def quit_ui(self):
self.feeder.stop()
curses.nocbreak()
self.stdscr.keypad(0)
curses.curs_set(1)
curses.echo()
curses.endwin()
exit(0)
def display_data(self, window, data):
window.addstr(3, 1, data, 0)
class feeder(threading.Thread):
# Fake U.I feeder
def __init__(self, ui):
super(feeder, self).__init__()
self.running = False
self.ui = ui
self.count = 0
def stop(self):
self.running = False
def run(self):
self.running = True
self.feed()
def feed(self):
while self.running:
self.ui.win1.addstr(3, 1, str(self.count)+\
": "+str(int(round(random.random()*9999))))
self.ui.win1.addstr(4, 1, str(self.running))
self.ui.win2.addstr(3, 1, str(self.count)+\
": "+str(int(round(random.random()*9999))))
self.ui.win2.addstr(4, 1, str(self.running))
time.sleep(0.5)
self.count += 1
if __name__ == "__main__":
gui = ui()
gui.ask()
I finally succeed in make it work by reading one byte from sys.stdin in a double while loop and then avoiding the use of another thread. The following code may not work on MS Windows and as I'm not a professional developer it may be un-optimized or throw un-catched errors but it is just a draft made to make me understand how things work (though, comment are welcome). Special thanks to Paul Griffiths who guided me to sys.stdin.