Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

cursesでCUIプログラミング

こんにちわ、みちゃこの大ファンなあらびきプログラマーmopemopeです。
最近はbpythonや,urwidなど何気にCUIが流行り始めてるっぽいです。
せっかくなのでtwistedを絡めてみます。

#-*- coding:utf-8 -*-

import curses, time, traceback, sys
import curses.wrapper
import locale

from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory
from twisted.python import log

class CursesStdIO:

    def fileno(self):
        return 0

    def doRead(self):
        pass

    def logPrefix(self):
        return "CursesClient"


class Screen(CursesStdIO):

    def __init__(self, stdscr):
        self.timer = 0
        self.statusText = "てすと -"
        self.searchText = ''
        self.stdscr = stdscr

        self.stdscr.nodelay(1)
        curses.cbreak()
        self.stdscr.keypad(1)
        curses.curs_set(0)

        self.rows, self.cols = self.stdscr.getmaxyx()
        self.lines = []

        curses.start_color()

        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
        curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK)

        self.paintStatus(self.statusText)

    def connectionLost(self, reason):
        self.close()

    def addLine(self, text):

        self.lines.append(text)
        self.redisplayLines()

    def redisplayLines(self):

        self.stdscr.clear()
        self.paintStatus(self.statusText)
        i = 0
        index = len(self.lines) - 1
        while i < (self.rows - 3) and index >= 0:
            self.stdscr.addstr(self.rows - 3 - i, 0, self.lines[index], 
                               curses.color_pair(2))
            i = i + 1
            index = index - 1

        self.stdscr.refresh()
    
    def paintStatus(self, text):
        if len(text) > self.cols: raise TextTooLongError
        self.stdscr.addstr(self.rows-2,0,text + ' ' * (self.cols-len(text)), 
                           curses.color_pair(1))
        self.stdscr.move(self.rows-1, self.cols-1)

    def doRead(self):
        curses.noecho()
        str = self.stdscr.getkey()

        if 'BACKSPACE' in str:
            self.searchText = self.searchText[:-1]

        elif str == '\n':
            self.addLine(self.searchText)
            self.searchText = ''

        else:
            if len(self.searchText) == self.cols-2: return
            self.searchText = self.searchText + str

        self.stdscr.addstr(self.rows-1, 0, 
                           self.searchText + (' ' * (
                           self.cols-len(self.searchText)-2)))
        self.stdscr.move(self.rows-1, len(self.searchText))
        self.paintStatus(self.statusText + ' %d' % len(self.searchText))
        self.stdscr.refresh()

    def close(self):

        curses.nocbreak()
        self.stdscr.keypad(0)
        curses.echo()
        curses.endwin()

if __name__ == '__main__':
    locale.setlocale(locale.LC_ALL, "")
    stdscr = curses.initscr()
    screen = Screen(stdscr) 
    stdscr.refresh()
    reactor.addReader(screen)
    reactor.run()
    screen.close()

とまあこんな感じでreaderとして突っ込めばdoReadを呼んでくれます。
簡単にtwistedを絡められますね。これでバックエンドをtwistedにしたものが書けますね。
ちなみにXMPP関連はtwisted/wordsに実装されてるので興味のあるひとは見てみるといいかも。