обертка curses в многопоточности не восстанавливает экран после выхода - PullRequest
0 голосов
/ 10 апреля 2020

Я пытаюсь разработать многопоточную программу с интерфейсом TUI. В основном у меня есть основной L oop, решающий, что делать, и некоторые задачи (такие как TUI или чтение данных из очереди и их обработка) выполняются в отдельном потоке. Мой TUI использует curses и является производным от потока классом, который выглядит следующим образом (для ясности я удалил несущественный код):

import threading
from time import sleep
import curses
import logging
from curses.textpad import Textbox, rectangle
from datetime import datetime
import re
from curses import panel
import os
import sys


class GenericTUI(threading.Thread):

    def __init__(self, logger=logging.getLogger()):
        threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=True)
        self.keyPressedList = list()
        self.alive = True
        self._myStdscr = None
        self.title = ""
        self.logger = logger
        self.lock = threading.Lock()


    def run(self):
            curses.wrapper(self.main)
            curses.nocbreak()
            curses.echo()
            curses.noraw()
            sys.exit(0)


    def main(self,stdscr):
        self._myStdscr = stdscr
        self._myStdscr.nodelay(True)
        self._myStdscr.keypad(True)
        self._myStdscr.box()
        while self.alive:
            sleep(0.4)
            try : 
                key =  self._myStdscr.getkey()
                if re.match('[A-Z_\+\-\*/]', key):
                    self.keyPressedList.append(key)

            except Exception as e:
               ## ignoring no key presssed 
               pass

            try :
                with self.lock :
                    self._myStdscr.clear()
                    self._myStdscr.addstr(1, 2, str(datetime.now())+" "+ sys.argv[0] +" "+self.title )
                    ### printing other things 
                    self._myStdscr.refresh()
            except Exception as e:
                self.logger.error(e, exc_info=True)
                continue

        self._myStdscr.clear()
        self._myStdscr.keypad(0)


    def getKeyPressed(self):
        if self.keyPressedList :
            return self.keyPressedList.pop()
        else :
            return None

    def stop(self):
        self.alive = False


    def updateTitle(self,title):
        with self.lock : self.title = title


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('title %s'%str(datetime.now() ))
        k = testGUI.getKeyPressed()
        if k is not None:
            if k=='Q' :
                alive = False
            else :
                testGUI.addMessage('unknown key %s'%k , maj=True)
        sleep(0.1)

основной l oop моей программы, создающей экземпляр и запускающей объект genericTUI и получить от него нажатие клавиши или установить значение для отображения.

Но когда я закрываю программу, мой терминал находится в смешном состоянии, даже если я использовал функцию-оболочку curses или попытался выполнить сброс вручную с помощью curses.nocbreak () и другие.

Я не могу понять, что я сделал не так? Я ошибаюсь, используя проклятия внутри потока?

1 Ответ

0 голосов
/ 10 апреля 2020

Я нашел ответ, но его размещение в разделе комментариев затрудняет чтение. Поэтому я также написал это здесь: программа-обработчик curses не любит поток в режиме демона:

, поэтому следующий код работает нормально и восстанавливает терминал в правильном состоянии:


class GenericTUI(threading.Thread):

    def __init__(self, logger=logging.getLogger()):
        threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=False)
        self.keyPressedList = list()

и в функции остановки помогает добавление curses.endwin ():

    def stop(self):
        curses.endwin()
        self.alive = False

надеюсь, что это поможет другим

...