⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 listbox.py

📁 Urwid is a Python library for making text console applications. It has many features including fluid
💻 PY
📖 第 1 页 / 共 3 页
字号:
#!/usr/bin/python## Urwid listbox class#    Copyright (C) 2004-2007  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 util import *from canvas import *from widget import *class ListWalkerError(Exception):	passclass ListWalker(object):	__metaclass__ = MetaSignals		signals = ["modified"]	def __hash__(self): return id(self)	def _modified(self):		Signals.emit(self, "modified")	class PollingListWalker(object):  # NOT ListWalker subclass	def __init__(self, contents):		"""		contents -- list to poll for changes		"""		self.contents = contents		if not type(contents) == type([]) and not hasattr( 			contents, '__getitem__' ):			raise ListWalkerError, "SimpleListWalker expecting list like object, got: "+`contents`		self.focus = 0		def _clamp_focus(self):		if self.focus >= len(self.contents):			self.focus = len(self.contents)-1		def get_focus(self):		"""Return (focus widget, focus position)."""		if len(self.contents) == 0: return None, None		self._clamp_focus()		return self.contents[self.focus], self.focus	def set_focus(self, position):		"""Set focus position."""		assert type(position) == type(1)		self.focus = position	def get_next(self, start_from):		"""		Return (widget after start_from, position after start_from).		"""		pos = start_from + 1		if len(self.contents) <= pos: return None, None		return self.contents[pos],pos	def get_prev(self, start_from):		"""		Return (widget before start_from, position before start_from).		"""		pos = start_from - 1		if pos < 0: return None, None		return self.contents[pos],posclass SimpleListWalker(MonitoredList, ListWalker):	def __init__(self, contents):		"""		contents -- list to copy into this object		Changes made to this object (when it is treated as a list) are		detected automatically and will cause ListBox objects using		this list walker to be updated.		"""		if not type(contents) == type([]) and not hasattr(contents, '__getitem__'):			raise ListWalkerError, "SimpleListWalker expecting list like object, got: "+`contents`		MonitoredList.__init__(self, contents)		self.focus = 0	def __hash__(self): return id(self)		def _get_contents(self):		"""		Return self.		Provides compatibility with old SimpleListWalker class.		"""		return self	contents = property(_get_contents)	def _modified(self):		if self.focus >= len(self):			self.focus = max(0, len(self)-1)		ListWalker._modified(self)		def set_modified_callback(self, callback):		"""		This function inherited from MonitoredList is not 		implemented in SimleListWalker.				Use Signals.connect(list_walker, "modified", ...) instead.		"""		raise NotImplementedError('Use Signals.connect('			'list_walker, "modified", ...) instead.')		def get_focus(self):		"""Return (focus widget, focus position)."""		if len(self) == 0: return None, None		return self[self.focus], self.focus	def set_focus(self, position):		"""Set focus position."""		assert type(position) == type(1)		self.focus = position		self._modified()	def get_next(self, start_from):		"""		Return (widget after start_from, position after start_from).		"""		pos = start_from + 1		if len(self) <= pos: return None, None		return self[pos],pos	def get_prev(self, start_from):		"""		Return (widget before start_from, position before start_from).		"""		pos = start_from - 1		if pos < 0: return None, None		return self[pos],pos		class ListBoxError(Exception):	passclass ListBox(BoxWidget):	def __init__(self, body):		"""		body -- a ListWalker-like object that contains			widgets to be displayed inside the list box		"""		if hasattr(body,'get_focus'):			self.body = body		else:			self.body = PollingListWalker(body)		try:			Signals.connect(self.body, "modified", self._invalidate)		except NameError:			# our list walker has no modified signal so we must not			# cache our canvases because we don't know when our			# content has changed			self.render = nocache_widget_render_instance(self)		# offset_rows is the number of rows between the top of the view		# and the top of the focused item		self.offset_rows = 0		# inset_fraction is used when the focused widget is off the 		# top of the view.  it is the fraction of the widget cut off 		# at the top.  (numerator, denominator)		self.inset_fraction = (0,1)		# pref_col is the preferred column for the cursor when moving		# between widgets that use the cursor (edit boxes etc.)		self.pref_col = 'left'		# variable for delayed focus change used by set_focus		self.set_focus_pending = 'first selectable'				# variable for delayed valign change used by set_focus_valign		self.set_focus_valign_pending = None				def calculate_visible(self, (maxcol, maxrow), focus=False ):		""" Return (middle,top,bottom) or None,None,None.		middle -- ( row offset(when +ve) or inset(when -ve),			focus widget, focus position, focus rows, 			cursor coords or None )		top -- ( # lines to trim off top, 			list of (widget, position, rows) tuples above focus			in order from bottom to top )		bottom -- ( # lines to trim off bottom, 			list of (widget, position, rows) tuples below focus			in order from top to bottom )		"""		# 0. set the focus if a change is pending		if self.set_focus_pending or self.set_focus_valign_pending:			self._set_focus_complete( (maxcol, maxrow), focus )		# 1. start with the focus widget		focus_widget, focus_pos = self.body.get_focus()		if focus_widget is None: #list box is empty?			return None,None,None		top_pos = bottom_pos = focus_pos				offset_rows, inset_rows = self.get_focus_offset_inset(			(maxcol,maxrow))		#    force at least one line of focus to be visible		if maxrow and offset_rows >= maxrow:			offset_rows = maxrow -1				#    adjust position so cursor remains visible		cursor = None		if maxrow and focus_widget.selectable() and focus:			if hasattr(focus_widget,'get_cursor_coords'):				cursor=focus_widget.get_cursor_coords((maxcol,))				if cursor is not None:			cx, cy = cursor			effective_cy = cy + offset_rows - inset_rows						if effective_cy < 0: # cursor above top?				inset_rows = cy			elif effective_cy >= maxrow: # cursor below bottom?				offset_rows = maxrow - cy -1				#    set trim_top by focus trimmimg		trim_top = inset_rows		focus_rows = focus_widget.rows((maxcol,),True)				# 2. collect the widgets above the focus		pos = focus_pos		fill_lines = offset_rows		fill_above = []		top_pos = pos		while fill_lines > 0:			prev, pos = self.body.get_prev( pos )			if prev is None: # run out of widgets above?				offset_rows -= fill_lines				break			top_pos = pos				p_rows = prev.rows( (maxcol,) )			fill_above.append( (prev, pos, p_rows) )			if p_rows > fill_lines: # crosses top edge?				trim_top = p_rows-fill_lines				break			fill_lines -= p_rows				trim_bottom = focus_rows + offset_rows - inset_rows - maxrow		if trim_bottom < 0: trim_bottom = 0		# 3. collect the widgets below the focus		pos = focus_pos		fill_lines = maxrow - focus_rows - offset_rows + inset_rows		fill_below = []		while fill_lines > 0:			next, pos = self.body.get_next( pos )			if next is None: # run out of widgets below?				break			bottom_pos = pos							n_rows = next.rows( (maxcol,) )			fill_below.append( (next, pos, n_rows) )			if n_rows > fill_lines: # crosses bottom edge?				trim_bottom = n_rows-fill_lines				fill_lines -= n_rows				break			fill_lines -= n_rows		# 4. fill from top again if necessary & possible		fill_lines = max(0, fill_lines)				if fill_lines >0 and trim_top >0:			if fill_lines <= trim_top:				trim_top -= fill_lines				offset_rows += fill_lines				fill_lines = 0			else:				fill_lines -= trim_top				offset_rows += trim_top				trim_top = 0		pos = top_pos		while fill_lines > 0:			prev, pos = self.body.get_prev( pos )			if prev is None:				break			p_rows = prev.rows( (maxcol,) )			fill_above.append( (prev, pos, p_rows) )			if p_rows > fill_lines: # more than required				trim_top = p_rows-fill_lines				offset_rows += fill_lines				break			fill_lines -= p_rows			offset_rows += p_rows				# 5. return the interesting bits		return ((offset_rows - inset_rows, focus_widget, 				focus_pos, focus_rows, cursor ),			(trim_top, fill_above), (trim_bottom, fill_below))		def render(self, (maxcol, maxrow), focus=False ):		"""		Render listbox and return canvas.		"""		middle, top, bottom = self.calculate_visible( 			(maxcol, maxrow), focus=focus)		if middle is None:			return SolidCanvas(" ", maxcol, maxrow)				_ignore, focus_widget, focus_pos, focus_rows, cursor = middle		trim_top, fill_above = top		trim_bottom, fill_below = bottom		combinelist = []		rows = 0		fill_above.reverse() # fill_above is in bottom-up order		for widget,w_pos,w_rows in fill_above:			canvas = widget.render((maxcol,))			if w_rows != canvas.rows():				raise ListBoxError, "Widget %s at position %s within listbox calculated %d rows but rendered %d!"% (`widget`,`w_pos`,w_rows, canvas.rows())			rows += w_rows			combinelist.append((canvas, w_pos, False))				focus_canvas = focus_widget.render((maxcol,), focus=focus)		if focus_canvas.rows() != focus_rows:			raise ListBoxError, "Focus Widget %s at position %s within listbox calculated %d rows but rendered %d!"% (`focus_widget`,`focus_pos`,focus_rows, focus_canvas.rows())		c_cursor = focus_canvas.cursor		if cursor != c_cursor:			raise ListBoxError, "Focus Widget %s at position %s within listbox calculated cursor coords %s but rendered cursor coords %s!" %(`focus_widget`,`focus_pos`,`cursor`,`c_cursor`)					rows += focus_rows		combinelist.append((focus_canvas, focus_pos, True))				for widget,w_pos,w_rows in fill_below:			canvas = widget.render((maxcol,))			if w_rows != canvas.rows():				raise ListBoxError, "Widget %s at position %s within listbox calculated %d rows but rendered %d!"% (`widget`,`w_pos`,w_rows, canvas.rows())			rows += w_rows			combinelist.append((canvas, w_pos, False))				final_canvas = CanvasCombine(combinelist)				if trim_top:				final_canvas.trim(trim_top)			rows -= trim_top		if trim_bottom:				final_canvas.trim_end(trim_bottom)			rows -= trim_bottom				assert rows <= maxrow, "Listbox contents too long!  Probably urwid's fault (please report): %s" % `top,middle,bottom`				if rows < maxrow:			bottom_pos = focus_pos			if fill_below: bottom_pos = fill_below[-1][1]			assert trim_bottom==0 and self.body.get_next(bottom_pos) == (None,None), "Listbox contents too short!  Probably urwid's fault (please report): %s" % `top,middle,bottom`			final_canvas.pad_trim_top_bottom(0, maxrow - rows)		return final_canvas	def set_focus_valign(self, valign):		"""Set the focus widget's display offset and inset.		valign -- one of:			'top', 'middle', 'bottom'			('fixed top', rows)			('fixed bottom', rows)			('relative', percentage 0=top 100=bottom)		"""		vt,va,ht,ha=decompose_valign_height(valign,None,ListBoxError)		self.set_focus_valign_pending = vt,va	def set_focus(self, position, coming_from=None):		"""		Set the focus position and try to keep the old focus in view.		position -- a position compatible with self.body.set_focus		coming_from -- set to 'above' or 'below' if you know that		               old position is above or below the new position.		"""		assert coming_from in ('above', 'below', None)		focus_widget, focus_pos = self.body.get_focus()				self.set_focus_pending = coming_from, focus_widget, focus_pos		self.body.set_focus( position )	def get_focus(self):		"""		Return a (focus widget, focus position) tuple.		"""		return self.body.get_focus()	def _set_focus_valign_complete(self, (maxcol, maxrow), focus):		"""		Finish setting the offset and inset now that we have have a 		maxcol & maxrow.		"""		vt,va = self.set_focus_valign_pending		self.set_focus_valign_pending = None		self.set_focus_pending = None		focus_widget, focus_pos = self.body.get_focus()		if focus_widget is None:			return				rows = focus_widget.rows((maxcol,), focus)		rtop, rbot = calculate_filler( vt, va, 'fixed', rows, 			None, maxrow )		self.shift_focus((maxcol, maxrow), rtop)			def _set_focus_first_selectable(self, (maxcol, maxrow), focus):		"""		Choose the first visible, selectable widget below the		current focus as the focus widget.		"""		self.set_focus_valign_pending = None		self.set_focus_pending = None		middle, top, bottom = self.calculate_visible( 			(maxcol, maxrow), focus=focus)		if middle is None:			return				row_offset, focus_widget, focus_pos, focus_rows, cursor = middle

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -