📄 canvas.py
字号:
""" def __init__(self): Canvas.__init__(self, None) def content(self, trim_left, trim_top, cols, rows, attr): """ return (cols, rows) of spaces with def_attr attribute. """ line = [(attr, None, " "*cols)] for i in range(rows): yield line def cols(self): raise NotImplementedError("BlankCanvas doesn't know its own size!") def rows(self): raise NotImplementedError("BlankCanvas doesn't know its own size!") def content_delta(self): raise NotImplementedError("BlankCanvas doesn't know its own size!") blank_canvas = BlankCanvas()class SolidCanvas(Canvas): """ A canvas filled completely with a single character. """ def __init__(self, fill_char, cols, rows): Canvas.__init__(self) end, col = calc_text_pos(fill_char, 0, len(fill_char), 1) assert col == 1, "Invalid fill_char: %r" % fill_char self._text, cs = apply_target_encoding(fill_char[:end]) self._cs = cs[0][0] self.size = cols, rows self.cursor = None def cols(self): return self.size[0] def rows(self): return self.size[1] def content(self, trim_left=0, trim_top=0, cols=None, rows=None, attr=None): if cols is None: cols = self.size[0] if rows is None: rows = self.size[1] line = [(attr, self._cs, self._text*cols)] for i in range(rows): yield line def content_delta(self, other): """ Return the differences between other and this canvas. """ if other is self: return [self.cols()]*self.rows() return self.content() class CompositeCanvas(Canvas): """ class for storing a combination of canvases """ def __init__(self, canv=None): """ canv -- a Canvas object to wrap this CompositeCanvas around. if canv is a CompositeCanvas, make a copy of its contents """ # a "shard" is a (num_rows, list of cviews) tuple, one for # each cview starting in this shard # a "cview" is a tuple that defines a view of a canvas: # (trim_left, trim_top, cols, rows, def_attr, canv) # a "shard tail" is a list of tuples: # (col_gap, done_rows, content_iter, cview) # tuples that define the unfinished cviews that are part of # shards following the first shard. Canvas.__init__(self) if canv is None: self.shards = [] self.children = [] else: if hasattr(canv, "shards"): self.shards = canv.shards else: self.shards = [(canv.rows(), [ (0, 0, canv.cols(), canv.rows(), None, canv)])] self.children = [(0, 0, canv, None)] self.coords.update(canv.coords) for shortcut in canv.shortcuts: self.shortcuts[shortcut] = "wrap" def rows(self): return sum([r for r,cv in self.shards]) def cols(self): if not self.shards: return 0 return sum([cv[2] for cv in self.shards[0][1]]) def content(self): """ Return the canvas content as a list of rows where each row is a list of (attr, cs, text) tuples. """ shard_tail = [] for num_rows, cviews in self.shards: # combine shard and shard tail sbody = shard_body(cviews, shard_tail) # output rows for i in range(num_rows): yield shard_body_row(sbody) # prepare next shard tail shard_tail = shard_body_tail(num_rows, sbody) def content_delta(self, other): """ Return the differences between other and this canvas. """ if not hasattr(other, 'shards'): for row in self.content(): yield row return shard_tail = [] for num_rows, cviews in shards_delta( self.shards, other.shards): # combine shard and shard tail sbody = shard_body(cviews, shard_tail) # output rows row = [] for i in range(num_rows): # if whole shard is unchanged, don't keep # calling shard_body_row if len(row) != 1 or type(row[0]) != type(0): row = shard_body_row(sbody) yield row # prepare next shard tail shard_tail = shard_body_tail(num_rows, sbody) def trim(self, top, count=None): """Trim lines from the top and/or bottom of canvas. top -- number of lines to remove from top count -- number of lines to keep, or None for all the rest """ assert top >= 0, "invalid trim amount %d!"%top assert top < self.rows(), "cannot trim %d lines from %d!"%( top, self.rows()) if self.widget_info: raise self._finalized_error if top: self.shards = shards_trim_top(self.shards, top) if count == 0: self.shards = [] elif count is not None: self.shards = shards_trim_rows(self.shards, count) self.coords = self.translate_coords(0, -top) def trim_end(self, end): """Trim lines from the bottom of the canvas. end -- number of lines to remove from the end """ assert end > 0, "invalid trim amount %d!"%end assert end <= self.rows(), "cannot trim %d lines from %d!"%( end, self.rows()) if self.widget_info: raise self._finalized_error self.shards = shards_trim_rows(self.shards, self.rows() - end) def pad_trim_left_right(self, left, right): """ Pad or trim this canvas on the left and right values > 0 indicate screen columns to pad values < 0 indicate screen columns to trim """ if self.widget_info: raise self._finalized_error shards = self.shards if left < 0 or right < 0: trim_left = max(0, -left) cols = self.cols() - trim_left - max(0, -right) shards = shards_trim_sides(shards, trim_left, cols) rows = self.rows() if left > 0 or right > 0: top_rows, top_cviews = shards[0] if left > 0: new_top_cviews = ( [(0,0,left,rows,None,blank_canvas)] + top_cviews) else: new_top_cviews = top_cviews[:] #copy if right > 0: new_top_cviews.append( (0,0,right,rows,None,blank_canvas)) shards = [(top_rows, new_top_cviews)] + shards[1:] self.coords = self.translate_coords(left, 0) self.shards = shards def pad_trim_top_bottom(self, top, bottom): """ Pad or trim this canvas on the top and bottom. """ if self.widget_info: raise self._finalized_error orig_shards = self.shards if top < 0 or bottom < 0: trim_top = max(0, -top) rows = self.rows() - trim_top - max(0, -bottom) self.trim(trim_top, rows) cols = self.cols() if top > 0: self.shards = [(top, [(0,0,cols,top,None,blank_canvas)])] + \ self.shards self.coords = self.translate_coords(0, top) if bottom > 0: if orig_shards is self.shards: self.shards = self.shards[:] self.shards.append((bottom, [(0,0,cols,bottom,None,blank_canvas)])) def overlay(self, other, left, top ): """Overlay other onto this canvas.""" if self.widget_info: raise self._finalized_error width = other.cols() height = other.rows() right = self.cols() - left - width bottom = self.rows() - top - height assert right >= 0, "top canvas of overlay not the size expected!" + `other.cols(),left,right,width` assert bottom >= 0, "top canvas of overlay not the size expected!" + `other.rows(),top,bottom,height` shards = self.shards top_shards = [] side_shards = self.shards bottom_shards = [] if top: side_shards = shards_trim_top(shards, top) top_shards = shards_trim_rows(shards, top) if bottom: bottom_shards = shards_trim_top(side_shards, height) side_shards = shards_trim_rows(side_shards, height) left_shards = [] right_shards = [] if left: left_shards = [shards_trim_sides(side_shards, 0, left)] if right: right_shards = [shards_trim_sides(side_shards, left + width, right)] if not self.rows(): middle_shards = [] elif left or right: middle_shards = shards_join(left_shards + [other.shards] + right_shards) else: middle_shards = other.shards self.shards = top_shards + middle_shards + bottom_shards self.coords.update(other.translate_coords(left, top)) def fill_attr(self, a): """ Apply attribute a to all areas of this canvas with default attribute currently set to None, leaving other attributes intact.""" if self.widget_info: raise self._finalized_error shards = [] for num_rows, original_cviews in self.shards: new_cviews = [] for cv in original_cviews: if cv[4] is None: new_cviews.append(cv[:4] + (a,) + cv[5:]) else: new_cviews.append(cv) shards.append((num_rows, new_cviews)) self.shards = shards def set_depends(self, widget_list): """ Explicitly specify the list of widgets that this canvas depends on. If any of these widgets change this canvas will have to be updated. """ if self.widget_info: raise self._finalized_error self.depends_on = widget_listdef shard_body_row(sbody): """ Return one row, advancing the iterators in sbody. ** MODIFIES sbody by calling next() on its iterators ** """ row = [] for done_rows, content_iter, cview in sbody: if content_iter: row.extend(content_iter.next()) else: # need to skip this unchanged canvas if row and type(row[-1]) == type(0): row[-1] = row[-1] + cview[2] else: row.append(cview[2]) return rowdef shard_body_tail(num_rows, sbody): """ Return a new shard tail that follows this shard body. """ shard_tail = [] col_gap = 0 done_rows = 0 for done_rows, content_iter, cview in sbody: cols, rows = cview[2:4] done_rows += num_rows if done_rows == rows: col_gap += cols continue shard_tail.append((col_gap, done_rows, content_iter, cview)) col_gap = 0 return shard_taildef shards_delta(shards, other_shards): """ Yield shards1 with cviews that are the same as shards2 having canv = None. """ other_shards_iter = iter(other_shards) other_num_rows = other_cviews = None done = other_done = 0 for num_rows, cviews in shards: if other_num_rows is None: other_num_rows, other_cviews = other_shards_iter.next() while other_done < done: other_done += other_num_rows other_num_rows, other_cviews = other_shards_iter.next() if other_done > done: yield (num_rows, cviews) done += num_rows continue # top-aligned shards, compare each cview yield (num_rows, shard_cviews_delta(cviews, other_cviews)) other_done += other_num_rows other_num_rows = None done += num_rowsdef shard_cviews_delta(cviews, other_cviews): """ """ other_cviews_iter = iter(other_cviews) other_cv = None cols = other_cols = 0 for cv in cviews: if other_cv is None: other_cv = other_cviews_iter.next() while other_cols < cols: other_cols += other_cv[2] other_cv = other_cviews_iter.next() if other_cols > cols: yield cv cols += cv[2] continue
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -