nLess /lib/PEG_GrammarExplorer/PEG_GrammarExplorer/PegSamples/python_2_5_2/input/simple_tabview.py

Language Python Lines 235
MD5 Hash 74e27ba9020bfffeed9c6e6194fc9551 Estimated Cost $3,634 (why?)
Repository git://github.com/chrisjowen/nLess.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#!/usr/local/bin/python

# tabview.py -- View a tab-delimited file in a spreadsheet-like display.
# Contributed by A.M. Kuchling <amk@amk.ca>
#
# The tab-delimited file is displayed on screen.  The highlighted
# position is shown in the top-left corner of the screen; below it are
# shown the contents of that cell.
#
#  Movement keys are:
#    Cursor keys: Move the highlighted cell, scrolling if required.
#    Q or q     : Quit
#    TAB        : Page right a screen
#    Home       : Move to the start of this line
#    End        : Move to the end of this line
#    PgUp/PgDn  : Move a page up or down
#    Insert     : Memorize this position
#    Delete     : Return to memorized position (if any)
#
# TODO : 
#    A 'G' for Goto: enter a cell like AA260 and move there
#    A key to re-read the tab-delimited file
#
# Possible projects:
#    Allow editing of cells, and then saving the modified data
#    Add formula evaluation, and you've got a simple spreadsheet
# program.  (Actually, you should allow displaying both via curses and
# via a Tk widget.)  
#

import curses, re, string

def yx2str(y,x):
    "Convert a coordinate pair like 1,26 to AA2"
    if x<26: s=chr(65+x)
    else:
	x=x-26
	s=chr(65+ (x/26) ) + chr(65+ (x%26) )
    s=s+str(y+1)
    return s

coord_pat = re.compile('^(?P<x>[a-zA-Z]{1,2})(?P<y>\d+)$')

def str2yx(s):
    "Convert a string like A1 to a coordinate pair like 0,0"
    match = coord_pat.match(s)
    if not match: return None
    y,x = match.group('y', 'x')
    x = string.upper(x)
    if len(x)==1: x=ord(x)-65
    else:
	x= (ord(x[0])-65)*26 + ord(x[1])-65 + 26
    return string.atoi(y)-1, x

assert yx2str(0,0) == 'A1'
assert yx2str(1,26) == 'AA2'
assert str2yx('AA2') == (1,26)
assert str2yx('B2') == (1,1)
	
class TabFile:
    def __init__(self, scr, filename, column_width=20):
	self.scr=scr ; self.filename = filename
	self.column_width = column_width
	f=open(filename, 'r')
	self.data = []
	while (1):
	    L=f.readline()
	    if L=="": break
	    self.data.append( string.split(L, '\t') )
#	    if len(self.data)>6: break # XXX
	self.x, self.y = 0,0
	self.win_x, self.win_y = 0,0
	self.max_y, self.max_x = self.scr.getmaxyx()
	self.num_columns = int(self.max_x/self.column_width)
	self.scr.clear()	
	self.display()

    def move_to_end(self):
	"""Move the highlighted location to the end of the current line."""

	# This is a method because I didn't want to have the code to 
	# handle the End key be aware of the internals of the TabFile object.
	yp=self.y+self.win_y ; xp=self.x+self.win_x
	if len(self.data)<=yp: end=0
	else: end=len(self.data[yp])-1
	
	# If the end column is on-screen, just change the
	# .x value appropriately.
	if self.win_x <= end < self.win_x + self.num_columns:
	    self.x = end - self.win_x
	else:
	    if end<self.num_columns:
		self.win_x = 0 ; self.x = end
	    else:
		self.x = self.num_columns-1
		self.win_x = end-self.x
	        
	
    def display(self):
	"""Refresh the current display"""
	
	self.scr.addstr(0,0, 
			yx2str(self.y + self.win_y, self.x+self.win_x)+'    ',
			curses.A_REVERSE)

	for y in range(0, self.max_y-3):
	    self.scr.move(y+2,0) ; self.scr.clrtoeol()
	    for x in range(0, int(self.max_x / self.column_width) ):
		self.scr.attrset(curses.A_NORMAL)
		yp=y+self.win_y ; xp=x+self.win_x
		if len(self.data)<=yp: s=""
		elif len(self.data[yp])<=xp: s=""
		else: s=self.data[yp][xp]
		s = string.ljust(s, 15)[0:15]
		if x==self.x and y==self.y: self.scr.attrset(curses.A_STANDOUT)
		self.scr.addstr(y+2, x*self.column_width, s)

	yp=self.y+self.win_y ; xp=self.x+self.win_x
	if len(self.data)<=yp: s=""
	elif len(self.data[yp])<=xp: s=""
	else: s=self.data[yp][xp]

	self.scr.move(1,0) ; self.scr.clrtoeol()
	self.scr.addstr(s[0:self.max_x])
	self.scr.refresh()

def main(stdscr):
    import string, curses, sys

    if len(sys.argv)==1:
	print 'Usage: tabview.py <filename>'
	return
    filename=sys.argv[1]

    # Clear the screen and display the menu of keys
    stdscr.clear()
    file = TabFile(stdscr, filename)
    
    # Main loop:
    while (1):
	stdscr.move(file.y+2, file.x*file.column_width)     # Move the cursor
	c=stdscr.getch()		# Get a keystroke
	if 0<c<256:
	    c=chr(c)
	    # Q or q exits
	    if c in 'Qq': break  
	    # Tab pages one screen to the right
	    elif c=='\t':
		file.win_x = file.win_x + file.num_columns
		file.display()
	    else: pass                  # Ignore incorrect keys

	# Cursor keys
	elif c==curses.key_up:
	    if file.y == 0:
		if file.win_y>0: file.win_y = file.win_y - 1
	    else: file.y=file.y-1
	    file.display()
	elif c==curses.key_down:
	    if file.y < file.max_y-3 -1: file.y=file.y+1
	    else: file.win_y = file.win_y+1
	    file.display()
	elif c==curses.key_left:
	    if file.x == 0:
		if file.win_x>0: file.win_x = file.win_x - 1
	    else: file.x=file.x-1
	    file.display()
	elif c==curses.key_right:
	    if file.x < int(file.max_x/file.column_width)-1: file.x=file.x+1
	    else: file.win_x = file.win_x+1
	    file.display()

	# Home key moves to the start of this line
	elif c==curses.key_home:
	    file.win_x = file.x = 0
	    file.display()
	# End key moves to the end of this line
	elif c==curses.key_end:
	    file.move_to_end()
	    file.display()

	# PageUp moves up a page
	elif c==curses.key_ppage:
	    file.win_y = file.win_y - (file.max_y - 2)
	    if file.win_y<0: file.win_y = 0
	    file.display()
	# PageDn moves down a page
	elif c==curses.key_npage:
	    file.win_y = file.win_y + (file.max_y - 2)
	    if file.win_y<0: file.win_y = 0
	    file.display()
	
	# Insert memorizes the current position
	elif c==curses.key_ic:
	    file.save_y, file.save_x = file.y + file.win_y, file.x + file.win_x
	# Delete restores a saved position
	elif c==curses.key_dc:
	    if hasattr(file, 'save_y'):
		file.x = file.y = 0
		file.win_y, file.win_x = file.save_y, file.save_x
		file.display()
        else: 
	    stdscr.addstr(0,50, curses.keyname(c)+ ' pressed')
	    stdscr.refresh()
	    pass			# Ignore incorrect keys

if __name__=='__main__':
    import curses, traceback
    try:
	# Initialize curses
	stdscr=curses.initscr()
	# Turn off echoing of keys, and enter cbreak mode,
	# where no buffering is performed on keyboard input
	curses.noecho() ; curses.cbreak()

	# In keypad mode, escape sequences for special keys
	# (like the cursor keys) will be interpreted and
	# a special value like curses.key_left will be returned
	stdscr.keypad(1)
	main(stdscr)			# Enter the main loop
	# Set everything back to normal
	stdscr.keypad(0)
	curses.echo() ; curses.nocbreak()
	curses.endwin()			# Terminate curses
    except:
        # In the event of an error, restore the terminal
	# to a sane state.
	stdscr.keypad(0)
	curses.echo() ; curses.nocbreak()
	curses.endwin()
	traceback.print_exc()		# Print the exception
Back to Top