首页 > 解决方案 > 多线程中的诅咒包装器在退出后不会恢复屏幕

问题描述

我正在尝试开发一个带有 TUI 接口的多线程程序。基本上我有一个主循环来决定要做什么,一些任务(如 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)

我的程序的主循环实例化并启动一个 genericTUI 对象并从中获取按键或设置要显示的值。

但是当我退出程序时,即使我使用了 curses 包装函数或尝试使用 curses.nocbreak() 等手动重置,我的终端也处于一个有趣的状态。

我想不通我做错了什么?我在线程中使用诅咒是错误的吗?

标签: python-3.xmultithreadingpython-curses

解决方案


我找到了答案,但把它放在评论部分会让人难以阅读。所以我也把它放在这里:curses wapper 不喜欢守护模式下的线程:

因此以下代码可以正常工作并将终端恢复为正确状态:


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

希望它可以帮助其他人


推荐阅读