3
# Copyright (c) 2009 Twisted Matrix Laboratories.
4
# See LICENSE for details.
7
This is an example of integrating curses with the twisted underlying
8
select loop. Most of what is in this is insignificant -- the main piece
9
of interest is the 'CursesStdIO' class.
11
This class acts as file-descriptor 0, and is scheduled with the twisted
12
select loop via reactor.addReader (once the curses class extends it
13
of course). When there is input waiting doRead is called, and any
14
input-oriented curses calls (ie. getch()) should be executed within this
17
Remember to call nodelay(1) in curses, to make getch() non-blocking.
21
import curses, time, traceback, sys
25
from twisted.internet import reactor
26
from twisted.internet.protocol import ClientFactory
27
from twisted.words.protocols.irc import IRCClient
28
from twisted.python import log
30
class TextTooLongError(Exception):
34
"""fake fd to be registered as a reader with the twisted reactor.
35
Curses classes needing input should extend this"""
38
""" We want to select on FD 0 """
42
"""called when input is ready"""
44
def logPrefix(self): return 'CursesClient'
49
""" A protocol object for IRC """
51
nickname = "testcurses"
53
def __init__(self, screenObj):
54
# screenObj should be 'stdscr' or a curses window/pad object
55
self.screenObj = screenObj
56
# for testing (hacky way around initial bad design for this example) :)
57
self.screenObj.irc = self
59
def lineReceived(self, line):
60
""" When receiving a line, add it to the output buffer """
61
self.screenObj.addLine(line)
63
def connectionMade(self):
64
IRCClient.connectionMade(self)
65
self.screenObj.addLine("* CONNECTED")
67
def clientConnectionLost(self, connection, reason):
71
class IRCFactory(ClientFactory):
74
Factory used for creating IRC protocol objects
79
def __init__(self, screenObj):
80
self.irc = self.protocol(screenObj)
82
def buildProtocol(self, addr=None):
85
def clientConnectionLost(self, conn, reason):
89
class Screen(CursesStdIO):
90
def __init__(self, stdscr):
92
self.statusText = "TEST CURSES APP -"
96
# set screen attributes
97
self.stdscr.nodelay(1) # this is used to make input calls non-blocking
100
curses.curs_set(0) # no annoying mouse cursor
102
self.rows, self.cols = self.stdscr.getmaxyx()
107
# create color pair's 1 and 2
108
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
109
curses.init_pair(2, curses.COLOR_CYAN, curses.COLOR_BLACK)
111
self.paintStatus(self.statusText)
113
def connectionLost(self, reason):
116
def addLine(self, text):
117
""" add a line to the internal list of lines"""
119
self.lines.append(text)
120
self.redisplayLines()
122
def redisplayLines(self):
123
""" method for redisplaying lines
124
based on internal list of lines """
127
self.paintStatus(self.statusText)
129
index = len(self.lines) - 1
130
while i < (self.rows - 3) and index >= 0:
131
self.stdscr.addstr(self.rows - 3 - i, 0, self.lines[index],
132
curses.color_pair(2))
135
self.stdscr.refresh()
137
def paintStatus(self, text):
138
if len(text) > self.cols: raise TextTooLongError
139
self.stdscr.addstr(self.rows-2,0,text + ' ' * (self.cols-len(text)),
140
curses.color_pair(1))
141
# move cursor to input line
142
self.stdscr.move(self.rows-1, self.cols-1)
145
""" Input is ready! """
147
self.timer = self.timer + 1
148
c = self.stdscr.getch() # read a character
150
if c == curses.KEY_BACKSPACE:
151
self.searchText = self.searchText[:-1]
153
elif c == curses.KEY_ENTER or c == 10:
154
self.addLine(self.searchText)
156
try: self.irc.sendLine(self.searchText)
158
self.stdscr.refresh()
162
if len(self.searchText) == self.cols-2: return
163
self.searchText = self.searchText + chr(c)
165
self.stdscr.addstr(self.rows-1, 0,
166
self.searchText + (' ' * (
167
self.cols-len(self.searchText)-2)))
168
self.stdscr.move(self.rows-1, len(self.searchText))
169
self.paintStatus(self.statusText + ' %d' % len(self.searchText))
170
self.stdscr.refresh()
176
self.stdscr.keypad(0)
180
if __name__ == '__main__':
181
stdscr = curses.initscr() # initialize curses
182
screen = Screen(stdscr) # create Screen object
184
ircFactory = IRCFactory(screen)
185
reactor.addReader(screen) # add screen object as a reader to the reactor
186
reactor.connectTCP("irc.freenode.net",6667,ircFactory) # connect to IRC
187
reactor.run() # have fun!