Getting Rid Of Control Character With Ncurses And Threaded Class
I'm trying to develop some kind of terminal user interface in python3 with threading and ncurse and some weird characters appear. Based on the answer in this post: Threading with P
Solution 1:
I answer my own question, but I think it will be hepfull for others : The trick is in the ncurses wrapper which have is own thread . So If i want the lock to work I have to put it in the main loop . Below is the code modified :
import threading
from time import sleep
import curses
import logging
from curses.textpad import Textbox, rectangle
from datetime import datetime
import re
class GenericTUI(threading.Thread):
def __init__(self,textmode=False, messageBoxSize=10, logger=logging.getLogger()):
threading.Thread.__init__(self)
self.keyPressedList = list()
self.alive = True
self.myStdscr = None
self.title = ""
self.messageList = list()
self.messageBoxSize = messageBoxSize
self.subTitle = ""
self.priceInfo = ""
self.progInfo = ""
self.textMode = textmode
self.logger = logger
self.lock = threading.Lock()
self.refreshFlag = True
def run(self):
if self.textMode :
with open('/tmp/genericTUI.command','w+') as f:
# command file in text mode
pass
while self.alive :
print("Program :"+ self.title)
print("sub"+self.subTitle)
print("Prices : "+self.priceInfo)
print("ProgInfos :"+self.progInfo)
for m in self.messageList :
print(m)
with open('/tmp/genericTUI.command','r+') as f:
c = f.read(1)
if c:
self.keyPressedList.append(c)
f.truncate(0)
sleep(5)
else :
curses.wrapper(self.main)
def main(self,stdscr):
# color definition
if curses.has_colors():
curses.start_color()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_RED)
curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_YELLOW)
curses.init_pair(4, curses.COLOR_BLACK, curses.COLOR_BLUE)
curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_GREEN)
## NE SURTOUT PAS METTRE keypad(false) GENERE DES CARACTERES AU HASARD .. DANGEREUX
self.myStdscr = stdscr
self.myStdscr.nodelay(True)
self.myStdscr.keypad(True)
self.myStdscr.box()
counter = 0
while self.alive:
try :
key = self.myStdscr.getkey()
#workaround to avoid multi thread escape character
if re.match('[A-Z_\+\-\*/]', key) :
self.keyPressedList.append(key)
except Exception as e:
## le nodelay rend l interface reactive mais ,le getkey genere un noinput error
## si pas de touche pressée d ou le pass
pass
'''
screen update in the mail loop
to be able to thread lock :
https://stackoverflow.com/questions/46773577/threading-with-python-curses-giving-me-weird-characters
'''
# determine la taille de l ecran
max_y, max_x = self.myStdscr.getmaxyx()
# reecriture
if self.refreshFlag:
try :
with self.lock :
self.myStdscr.clear()
for x in range(max_x):
self.myStdscr.addch(0,x,curses.ACS_HLINE)
self.myStdscr.addstr(1, 2, "Program :"+ self.title, curses.color_pair(1) )
self.myStdscr.addstr(2, 2, self.subTitle)
for x in range(max_x):
self.myStdscr.addch(3,x,curses.ACS_HLINE)
self.myStdscr.addstr(4, 2, "Prices : "+self.priceInfo, curses.color_pair(2))
for x in range(max_x):
self.myStdscr.addch(5,x,curses.ACS_HLINE)
self.myStdscr.addstr(6, 2, "ProgInfos :"+self.progInfo, curses.color_pair(5))
for x in range(max_x):
self.myStdscr.addch(7,x,curses.ACS_HLINE)
indent =0
for m in self.messageList :
self.myStdscr.addstr(8+indent, 3,m)
indent+=1
for y in range(max_y):
self.myStdscr.addch(y,0,curses.ACS_VLINE)
self.myStdscr.refresh()
except Exception as e:
self.logger.error(repr(e))
self.myStdscr.clear()
finally:
self.refreshFlag = False
sleep(0.1)
counter +=1
def getKeyPressed(self):
if self.keyPressedList :
return self.keyPressedList.pop()
else :
return None
def stop(self):
self.alive = False
def updatePriceInfo(self,priceDict,maj=False):
result = " ".join(str(key) +":"+ str(value)+"|" for key, value in priceDict.items())
self.priceInfo = result
self.refreshFlag = maj
def updateTitle(self,title, maj=False):
self.title = str(title)
self.refreshFlag = maj
def updateSubTitle(self,subtitleDict, maj=False):
result = " ".join(str(key) +":"+ str(value)+"|" for key, value in subtitleDict.items())
self.subTitle = str(result)
self.refreshFlag = maj
def updateProgInfo(self,messDict, maj=False):
result = " ".join(str(key) +":"+ str(value)+"|" for key, value in messDict.items())
self.progInfo = result
self.refreshFlag = maj
def addMessage(self,mess, maj=False):
self.messageList.append(repr(mess))
if len(self.messageList) > self.messageBoxSize : self.messageList.pop(0)
self.refreshFlag = maj
def getValue(self, mess="Enter Value: (hit Ctrl-G to send)"):
with self.lock :
self.myStdscr.addstr(0, 0, mess)
editwin = curses.newwin(1,7, 2,1)
rectangle(self.myStdscr, 1,0, 1+1+1, 1+7+1)
box = Textbox(editwin)
box.stripspaces = True
self.myStdscr.refresh()
# Let the user edit until Ctrl-G is struck.
box.edit()
# Get resulting contents
return(box.gather().strip())
if __name__ == "__main__":
## the main is used for some test when the lib is called directly
testGUI = GenericTUI()
alive = True
testGUI.logger.addHandler(logging.StreamHandler())
testGUI.logger.setLevel(logging.DEBUG)
testGUI.start()
while alive :
testGUI.updateTitle('time %s'%str(datetime.now() ))
k = testGUI.getKeyPressed()
if k is not None:
if k=='Q' :
alive = False
elif k=='M' :
mess = testGUI.getValue()
testGUI.addMessage(mess,maj=True)
else :
testGUI.addMessage('unknown key %s'%k , maj=True)
sleep(0.1)
testGUI.stop()
```
you may also notice there is another lock in the getvalue function to avoid the main thread to erase the textbox .
Hope this help others.
Post a Comment for "Getting Rid Of Control Character With Ncurses And Threaded Class"