📄 util.py
字号:
#!/usr/bin/python# -*- coding: utf-8 -*-## Urwid utility functions# Copyright (C) 2004-2006 Ian Ward## This library is free software; you can redistribute it and/or# modify it under the terms of the GNU Lesser General Public# License as published by the Free Software Foundation; either# version 2.1 of the License, or (at your option) any later version.## This library is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU# Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public# License along with this library; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA## Urwid web site: http://excess.org/urwid/from __future__ import nested_scopesimport escapeimport encodingsimport weakreftry: import str_utilexcept ImportError: import old_str_util as str_util# bring str_util functions into our namespacecalc_text_pos = str_util.calc_text_poscalc_width = str_util.calc_widthis_wide_char = str_util.is_wide_charmove_next_char = str_util.move_next_charmove_prev_char = str_util.move_prev_charwithin_double_byte = str_util.within_double_bytetry: enumerateexcept: enumerate = lambda x: zip(range(len(x)),x) # old python# Try to determine if using a supported double-byte encodingimport localetry: try: locale.setlocale( locale.LC_ALL, "" ) except locale.Error: pass detected_encoding = locale.getlocale()[1] if not detected_encoding: detected_encoding = ""except ValueError, e: # with invalid LANG value python will throw ValueError if e.args and e.args[0].startswith("unknown locale"): detected_encoding = "" else: raise_target_encoding = None_use_dec_special = Truedef set_encoding( encoding ): """ Set the byte encoding to assume when processing strings and the encoding to use when converting unicode strings. """ encoding = encoding.lower() global _target_encoding, _use_dec_special if encoding in ( 'utf-8', 'utf8', 'utf' ): str_util.set_byte_encoding("utf8") _use_dec_special = False elif encoding in ( 'euc-jp' # JISX 0208 only , 'euc-kr', 'euc-cn', 'euc-tw' # CNS 11643 plain 1 only , 'gb2312', 'gbk', 'big5', 'cn-gb', 'uhc' # these shouldn't happen, should they? , 'eucjp', 'euckr', 'euccn', 'euctw', 'cncb' ): str_util.set_byte_encoding("wide") _use_dec_special = True else: str_util.set_byte_encoding("narrow") _use_dec_special = True # if encoding is valid for conversion from unicode, remember it _target_encoding = 'ascii' try: if encoding: u"".encode(encoding) _target_encoding = encoding except LookupError: passdef get_encoding_mode(): """ Get the mode Urwid is using when processing text strings. Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings or 'utf8' for UTF-8 encodings. """ return str_util.get_byte_encoding()def apply_target_encoding( s ): """ Return (encoded byte string, character set rle). """ if _use_dec_special and type(s) == type(u""): # first convert drawing characters try: s = s.translate( escape.DEC_SPECIAL_CHARMAP ) except NotImplementedError: # python < 2.4 needs to do this the hard way.. for c, alt in zip(escape.DEC_SPECIAL_CHARS, escape.ALT_DEC_SPECIAL_CHARS): s = s.replace( c, escape.SO+alt+escape.SI ) if type(s) == type(u""): s = s.replace( escape.SI+escape.SO, u"" ) # remove redundant shifts s = s.encode( _target_encoding ) sis = s.split( escape.SO ) sis0 = sis[0].replace( escape.SI, "" ) sout = [] cout = [] if sis0: sout.append( sis0 ) cout.append( (None,len(sis0)) ) if len(sis)==1: return sis0, cout for sn in sis[1:]: sl = sn.split( escape.SI, 1 ) if len(sl) == 1: sin = sl[0] sout.append(sin) rle_append_modify(cout, (escape.DEC_TAG, len(sin))) continue sin, son = sl son = son.replace( escape.SI, "" ) if sin: sout.append(sin) rle_append_modify(cout, (escape.DEC_TAG, len(sin))) if son: sout.append(son) rle_append_modify(cout, (None, len(son))) return "".join(sout), cout ####################################################################### Try to set the encoding using the one detected by the locale moduleset_encoding( detected_encoding )######################################################################def supports_unicode(): """ Return True if python is able to convert non-ascii unicode strings to the current encoding. """ return _target_encoding and _target_encoding != 'ascii'class TextLayout: def supports_align_mode(self, align): """Return True if align is a supported align mode.""" return True def supports_wrap_mode(self, wrap): """Return True if wrap is a supported wrap mode.""" return True def layout(self, text, width, align, wrap ): """ Return a layout structure for text. text -- string in current encoding or unicode string width -- number of screen columns available align -- align mode for text wrap -- wrap mode for text Layout structure is a list of line layouts, one per output line. Line layouts are lists than may contain the following tuples: ( column width of text segment, start offset, end offset ) ( number of space characters to insert, offset or None) ( column width of insert text, offset, "insert text" ) The offset in the last two tuples is used to determine the attribute used for the inserted spaces or text respectively. The attribute used will be the same as the attribute at that text offset. If the offset is None when inserting spaces then no attribute will be used. """ assert 0, ("This function must be overridden by a real" " text layout class. (see StandardTextLayout)") return [[]]class StandardTextLayout(TextLayout): def __init__(self):#, tab_stops=(), tab_stop_every=8): pass #""" #tab_stops -- list of screen column indexes for tab stops #tab_stop_every -- repeated interval for following tab stops #""" #assert tab_stop_every is None or type(tab_stop_every)==type(0) #if not tab_stops and tab_stop_every: # self.tab_stops = (tab_stop_every,) #self.tab_stops = tab_stops #self.tab_stop_every = tab_stop_every def supports_align_mode(self, align): """Return True if align is 'left', 'center' or 'right'.""" return align in ('left', 'center', 'right') def supports_wrap_mode(self, wrap): """Return True if wrap is 'any', 'space' or 'clip'.""" return wrap in ('any', 'space', 'clip') def layout(self, text, width, align, wrap ): """Return a layout structure for text.""" segs = self.calculate_text_segments( text, width, wrap ) return self.align_layout( text, width, segs, wrap, align ) def pack(self, maxcol, layout): """ Return a minimal maxcol value that would result in the same number of lines for layout. layout must be a layout structure returned by self.layout(). """ maxwidth = 0 assert layout, "huh? empty layout?: "+`layout` for l in layout: lw = line_width(l) if lw >= maxcol: return maxcol maxwidth = max(maxwidth, lw) return maxwidth def align_layout( self, text, width, segs, wrap, align ): """Convert the layout segs to an aligned layout.""" out = [] for l in segs: sc = line_width(l) if sc == width or align=='left': out.append(l) continue if align == 'right': out.append([(width-sc, None)] + l) continue assert align == 'center' out.append([((width-sc+1)/2, None)] + l) return out def calculate_text_segments( self, text, width, wrap ): """ Calculate the segments of text to display given width screen columns to display them. text - text to display width - number of available screen columns wrap - wrapping mode used Returns a layout structure without aligmnent applied. """ b = [] p = 0 if wrap == 'clip': # no wrapping to calculate, so it's easy. while p<=len(text): n_cr = text.find("\n", p) if n_cr == -1: n_cr = len(text) sc = calc_width(text, p, n_cr) l = [(0,n_cr)] if p!=n_cr: l = [(sc, p, n_cr)] + l b.append(l) p = n_cr+1 return b while p<=len(text): # look for next eligible line break n_cr = text.find("\n", p) if n_cr == -1: n_cr = len(text) sc = calc_width(text, p, n_cr) if sc == 0: # removed character hint b.append([(0,n_cr)]) p = n_cr+1 continue if sc <= width: # this segment fits b.append([(sc,p,n_cr), # removed character hint (0,n_cr)]) p = n_cr+1 continue pos, sc = calc_text_pos( text, p, n_cr, width ) # FIXME: handle pathological width=1 double-byte case if wrap == 'any': b.append([(sc,p,pos)]) p = pos continue assert wrap == 'space' if text[pos] == " ": # perfect space wrap b.append([(sc,p,pos), # removed character hint (0,pos)]) p = pos+1 continue if is_wide_char(text, pos): # perfect next wide b.append([(sc,p,pos)]) p = pos continue prev = pos while prev > p: prev = move_prev_char(text, p, prev) if text[prev] == " ": sc = calc_width(text,p,prev) l = [(0,prev)] if p!=prev: l = [(sc,p,prev)] + l b.append(l) p = prev+1 break if is_wide_char(text,prev): # wrap after wide char next = move_next_char(text, prev, pos) sc = calc_width(text,p,next) b.append([(sc,p,next)]) p = next break else: # unwrap previous line space if possible to # fit more text (we're breaking a word anyway) if b and (len(b[-1]) == 2 or ( len(b[-1])==1 and len(b[-1][0])==2 )): # look for removed space above if len(b[-1]) == 1: [(h_sc, h_off)] = b[-1] p_sc = 0 p_off = p_end = h_off else: [(p_sc, p_off, p_end), (h_sc, h_off)] = b[-1] if (p_sc < width and h_sc==0 and text[h_off] == " "): # combine with previous line del b[-1] p = p_off pos, sc = calc_text_pos( text, p, n_cr, width ) b.append([(sc,p,pos)]) # check for trailing " " or "\n" p = pos if p < len(text) and ( text[p] in (" ","\n")): # removed character hint b[-1].append((0,p)) p += 1 continue # force any char wrap b.append([(sc,p,pos)]) p = pos return b####################################### default layout object to usedefault_layout = StandardTextLayout()###################################### class LayoutSegment: def __init__(self, seg): """Create object from line layout segment structure""" assert type(seg) == type(()), `seg` assert len(seg) in (2,3), `seg` self.sc, self.offs = seg[:2] assert type(self.sc) == type(0), `self.sc` if len(seg)==3: assert type(self.offs) == type(0), `self.offs` assert self.sc > 0, `seg` t = seg[2] if type(t) == type(""): self.text = t self.end = None else: assert type(t) == type(0), `t` self.text = None self.end = t else: assert len(seg) == 2, `seg` if self.offs is not None: assert self.sc >= 0, `seg` assert type(self.offs)==type(0) self.text = self.end = None def subseg(self, text, start, end): """ Return a "sub-segment" list containing segment structures that make up a portion of this segment. A list is returned to handle cases where wide characters need to be replaced with a space character at either edge so two or three segments will be returned. """ if start < 0: start = 0 if end > self.sc: end = self.sc if start >= end: return [] # completely gone if self.text: # use text stored in segment (self.text) spos, epos, pad_left, pad_right = calc_trim_text( self.text, 0, len(self.text), start, end ) return [ (end-start, self.offs, " "*pad_left + self.text[spos:epos] + " "*pad_right) ] elif self.end: # use text passed as parameter (text) spos, epos, pad_left, pad_right = calc_trim_text( text, self.offs, self.end, start, end ) l = [] if pad_left: l.append((1,spos-1)) l.append((end-start-pad_left-pad_right, spos, epos)) if pad_right: l.append((1,epos)) return l else: # simple padding adjustment return [(end-start,self.offs)]def line_width( segs ): """ Return the screen column width of one line of a text layout structure. This function ignores any existing shift applied to the line, represended by an (amount, None) tuple at the start of the line. """ sc = 0 seglist = segs if segs and len(segs[0])==2 and segs[0][1]==None: seglist = segs[1:] for s in seglist: sc += s[0] return scdef shift_line( segs, amount ): """ Return a shifted line from a layout structure to the left or right. segs -- line of a layout structure amount -- screen columns to shift right (+ve) or left (-ve) """ assert type(amount)==type(0), `amount` if segs and len(segs[0])==2 and segs[0][1]==None: # existing shift amount += segs[0][0] if amount: return [(amount,None)]+segs[1:] return segs[1:] if amount: return [(amount,None)]+segs return segs def trim_line( segs, text, start, end ): """ Return a trimmed line of a text layout structure. text -- text to which this layout structre applies start -- starting screen column end -- ending screen column
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -