📄 util.py
字号:
""" l = [] x = 0 for seg in segs: sc = seg[0] if start or sc < 0: if start >= sc: start -= sc x += sc continue s = LayoutSegment(seg) if x+sc >= end: # can all be done at once return s.subseg( text, start, end-x ) l += s.subseg( text, start, sc ) start = 0 x += sc continue if x >= end: break if x+sc > end: s = LayoutSegment(seg) l += s.subseg( text, 0, end-x ) break l.append( seg ) return ldef calc_line_pos( text, line_layout, pref_col ): """ Calculate the closest linear position to pref_col given a line layout structure. Returns None if no position found. """ closest_sc = None closest_pos = None current_sc = 0 if pref_col == 'left': for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: return s.offs return elif pref_col == 'right': for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: closest_pos = s s = closest_pos if s is None: return if s.end is None: return s.offs return calc_text_pos( text, s.offs, s.end, s.sc-1)[0] for seg in line_layout: s = LayoutSegment(seg) if s.offs is not None: if s.end is not None: if (current_sc <= pref_col and pref_col < current_sc + s.sc): # exact match within this segment return calc_text_pos( text, s.offs, s.end, pref_col - current_sc )[0] elif current_sc <= pref_col: closest_sc = current_sc + s.sc - 1 closest_pos = s if closest_sc is None or ( abs(pref_col-current_sc) < abs(pref_col-closest_sc) ): # this screen column is closer closest_sc = current_sc closest_pos = s.offs if current_sc > closest_sc: # we're moving past break current_sc += s.sc if closest_pos is None or type(closest_pos) == type(0): return closest_pos # return the last positions in the segment "closest_pos" s = closest_pos return calc_text_pos( text, s.offs, s.end, s.sc-1)[0]def calc_pos( text, layout, pref_col, row ): """ Calculate the closest linear position to pref_col and row given a layout structure. """ if row < 0 or row >= len(layout): raise Exception("calculate_pos: out of layout row range") pos = calc_line_pos( text, layout[row], pref_col ) if pos is not None: return pos rows_above = range(row-1,-1,-1) rows_below = range(row+1,len(layout)) while rows_above and rows_below: if rows_above: r = rows_above.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos if rows_below: r = rows_below.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos return 0def calc_coords( text, layout, pos, clamp=1 ): """ Calculate the coordinates closest to position pos in text with layout. text -- raw string or unicode string layout -- layout structure applied to text pos -- integer position into text clamp -- ignored right now """ closest = None y = 0 for line_layout in layout: x = 0 for seg in line_layout: s = LayoutSegment(seg) if s.offs is None: x += s.sc continue if s.offs == pos: return x,y if s.end is not None and s.offs<=pos and s.end>pos: x += calc_width( text, s.offs, pos ) return x,y distance = abs(s.offs - pos) if s.end is not None and s.end<pos: distance = pos - (s.end-1) if closest is None or distance < closest[0]: closest = distance, (x,y) x += s.sc y += 1 if closest: return closest[1] return 0,0def calc_trim_text( text, start_offs, end_offs, start_col, end_col ): """ Calculate the result of trimming text. start_offs -- offset into text to treat as screen column 0 end_offs -- offset into text to treat as the end of the line start_col -- screen column to trim at the left end_col -- screen column to trim at the right Returns (start, end, pad_left, pad_right), where: start -- resulting start offset end -- resulting end offset pad_left -- 0 for no pad or 1 for one space to be added pad_right -- 0 for no pad or 1 for one space to be added """ l = [] spos = start_offs pad_left = pad_right = 0 if start_col > 0: spos, sc = calc_text_pos( text, spos, end_offs, start_col ) if sc < start_col: pad_left = 1 spos, sc = calc_text_pos( text, start_offs, end_offs, start_col+1 ) run = end_col - start_col - pad_left pos, sc = calc_text_pos( text, spos, end_offs, run ) if sc < run: pad_right = 1 return ( spos, pos, pad_left, pad_right )def trim_text_attr_cs( text, attr, cs, start_col, end_col ): """ Return ( trimmed text, trimmed attr, trimmed cs ). """ spos, epos, pad_left, pad_right = calc_trim_text( text, 0, len(text), start_col, end_col ) attrtr = rle_subseg( attr, spos, epos ) cstr = rle_subseg( cs, spos, epos ) if pad_left: al = rle_get_at( attr, spos-1 ) rle_append_beginning_modify( attrtr, (al, 1) ) rle_append_beginning_modify( cstr, (None, 1) ) if pad_right: al = rle_get_at( attr, epos ) rle_append_modify( attrtr, (al, 1) ) rle_append_modify( cstr, (None, 1) ) return " "*pad_left + text[spos:epos] + " "*pad_right, attrtr, cstr def rle_get_at( rle, pos ): """ Return the attribute at offset pos. """ x = 0 if pos < 0: return None for a, run in rle: if x+run > pos: return a x += run return Nonedef rle_subseg( rle, start, end ): """Return a sub segment of an rle list.""" l = [] x = 0 for a, run in rle: if start: if start >= run: start -= run x += run continue x += start run -= start start = 0 if x >= end: break if x+run > end: run = end-x x += run l.append( (a, run) ) return ldef rle_len( rle ): """ Return the number of characters covered by a run length encoded attribute list. """ run = 0 for v in rle: assert type(v) == type(()), `rle` a, r = v run += r return rundef rle_append_beginning_modify( rle, (a, r) ): """ Append (a, r) to BEGINNING of rle. Merge with first run when possible MODIFIES rle parameter contents. Returns None. """ if not rle: rle[:] = [(a, r)] else: al, run = rle[0] if a == al: rle[0] = (a,run+r) else: rle[0:0] = [(al, r)] def rle_append_modify( rle, (a, r) ): """ Append (a,r) to the rle list rle. Merge with last run when possible. MODIFIES rle parameter contents. Returns None. """ if not rle or rle[-1][0] != a: rle.append( (a,r) ) return la,lr = rle[-1] rle[-1] = (a, lr+r)def rle_join_modify( rle, rle2 ): """ Append attribute list rle2 to rle. Merge last run of rle with first run of rle2 when possible. MODIFIES attr parameter contents. Returns None. """ if not rle2: return rle_append_modify(rle, rle2[0]) rle += rle2[1:] def rle_product( rle1, rle2 ): """ Merge the runs of rle1 and rle2 like this: eg. rle1 = [ ("a", 10), ("b", 5) ] rle2 = [ ("Q", 5), ("P", 10) ] rle_product: [ (("a","Q"), 5), (("a","P"), 5), (("b","P"), 5) ] rle1 and rle2 are assumed to cover the same total run. """ i1 = i2 = 1 # rle1, rle2 indexes if not rle1 or not rle2: return [] a1, r1 = rle1[0] a2, r2 = rle2[0] l = [] while r1 and r2: r = min(r1, r2) rle_append_modify( l, ((a1,a2),r) ) r1 -= r if r1 == 0 and i1< len(rle1): a1, r1 = rle1[i1] i1 += 1 r2 -= r if r2 == 0 and i2< len(rle2): a2, r2 = rle2[i2] i2 += 1 return l def rle_factor( rle ): """ Inverse of rle_product. """ rle1 = [] rle2 = [] for (a1, a2), r in rle: rle_append_modify( rle1, (a1, r) ) rle_append_modify( rle2, (a2, r) ) return rle1, rle2class TagMarkupException( Exception ): passdef decompose_tagmarkup( tm ): """Return (text string, attribute list) for tagmarkup passed.""" tl, al = _tagmarkup_recurse( tm, None ) text = "".join(tl) if al and al[-1][0] is None: del al[-1] return text, al def _tagmarkup_recurse( tm, attr ): """Return (text list, attribute list) for tagmarkup passed. tm -- tagmarkup attr -- current attribute or None""" if type(tm) == type("") or type(tm) == type( u"" ): # text return [tm], [(attr, len(tm))] if type(tm) == type([]): # for lists recurse to process each subelement rtl = [] ral = [] for element in tm: tl, al = _tagmarkup_recurse( element, attr ) if ral: # merge attributes when possible last_attr, last_run = ral[-1] top_attr, top_run = al[0] if last_attr == top_attr: ral[-1] = (top_attr, last_run + top_run) del al[-1] rtl += tl ral += al return rtl, ral if type(tm) == type(()): # tuples mark a new attribute boundary if len(tm) != 2: raise TagMarkupException, "Tuples must be in the form (attribute, tagmarkup): %s" % `tm` attr, element = tm return _tagmarkup_recurse( element, attr ) raise TagMarkupException, "Invalid markup element: %s" % `tm`def is_mouse_event( ev ): return type(ev) == type(()) and len(ev)==4 and ev[0].find("mouse")>=0def is_mouse_press( ev ): return ev.find("press")>=0class MetaSuper(type): """adding .__super""" def __init__(cls, name, bases, d): super(MetaSuper, cls).__init__(name, bases, d) if hasattr(cls, "_%s__super" % name): raise AttributeError, "Class has same name as one of its super classes" setattr(cls, "_%s__super" % name, super(cls))class MetaSignals(type): """ register the list of signals in the class varable signals, including signals in superclasses. """ def __init__(cls, name, bases, d): signals = d.get("signals", []) for superclass in cls.__bases__: signals.extend(getattr(superclass, 'signals', [])) signals = dict([(x,None) for x in signals]).keys() d["signals"] = signals Signals.register(cls, signals) super(MetaSignals, cls).__init__(name, bases, d)class Signals(object): _connections = weakref.WeakKeyDictionary() _supported = {} def register(cls, sig_cls, signals): cls._supported[sig_cls] = signals register = classmethod(register) def connect(cls, obj, name, callback, user_arg=None): sig_cls = obj.__class__ if not name in cls._supported.get(sig_cls, []): raise NameError, "No such signal %r for object %r" % \ (name, obj) d = cls._connections.setdefault(obj, {}) d.setdefault(name, []).append((callback, user_arg)) connect = classmethod(connect) def disconnect(cls, obj, name, callback, user_arg=None): d = cls._connections.get(obj, {}) if name not in d: return if (callback, user_arg) not in d[name]: return d[name].remove((callback, user_arg)) disconnect = classmethod(disconnect) def emit(cls, obj, name, *args): result = False d = cls._connections.get(obj, {}) for callback, user_arg in d.get(name, []): args_copy = args if user_arg is not None: args_copy = args + [user_arg] result |= bool(callback(*args_copy)) return result emit = classmethod(emit) def _call_modified(fn): def call_modified_wrapper(self, *args): rval = fn(self, *args) self._modified() return rval return call_modified_wrapperclass MonitoredList(list): def _modified(self): pass def set_modified_callback(self, callback): self._modified = callback def __repr__(self): return "%s.%s(%s)" % (self.__class__.__module__, self.__class__.__name__, list.__repr__(self)) __add__ = _call_modified(list.__add__) __delitem__ = _call_modified(list.__delitem__) __delslice__ = _call_modified(list.__delslice__) __iadd__ = _call_modified(list.__iadd__) __imul__ = _call_modified(list.__imul__) __rmul__ = _call_modified(list.__rmul__) __setitem__ = _call_modified(list.__setitem__) __setslice__ = _call_modified(list.__setslice__) append = _call_modified(list.append) extend = _call_modified(list.extend) insert = _call_modified(list.insert) pop = _call_modified(list.pop) remove = _call_modified(list.remove) reverse = _call_modified(list.reverse) sort = _call_modified(list.sort)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -