[PyKDE] ANNOUNCE: SIP/PyQt/PyKDE v0.13pre5

Pete Ware ware at cis.ohio-state.edu
Thu Aug 31 14:58:22 BST 2000


Here is something I wrote to do a "Spreadsheet" like thing.  I really 
haven't
cleaned it up beyond just getting it to work for my specific needs.

Typing "Return" allows you to edit a cell.  Type "Esc" stops editting 
without saving changes to that cell.

--pete
-------------- next part --------------
#! /bin/env python
#				-*- Python -*-
import sys
import types
import UserList

import qt

class Table (qt.QScrollView):
	"""Implement a spreadsheet like widget.
	Individual cells can be editted and arrow keys work for navigation."""

	def __init__ (self, rows, cols, parent = None, name = ''):
		qt.QScrollView.__init__ (self, parent, name,  0x00800000 | qt.Qt.WidgetFlags.WNorthWestGravity)
		self.setResizePolicy (self.ResizePolicy.Manual)
		#self.num_rows = rows	# avoid, if possible
		#self.num_cols = cols
		self.contents = map (lambda arg: (None, []), range (0,rows))
		self.row_cur = 0
		self.col_cur = 0
		self.editor = None
		self.key_last = qt.Qt.Key.Key_Right
		self.update_func = None	# called when user changes a cell's contents
		self.doupdates = 1
		self.selectlist = []	# List of rows that are selected
		self.sort_col = None
		self.sort_increasing = 1

		#
		# Create the left and top headers
		#
		self.header_left = qt.QHeader (rows, self)
		left = self.header_left
		left.setOrientation (qt.Qt.Orientation.Vertical)
		left.setTracking (1)
		left.setMovingEnabled (0)

		self.header_top = qt.QHeader (cols, self)
		top = self.header_top
		top.setOrientation (qt.Qt.Orientation.Horizontal)
		top.setTracking (1)
		top.setMovingEnabled (0)

		self.setMargins (30, self.fontMetrics().height() + 4, 0, 0)

		self.enableClipper (1)
		self.viewport().setBackgroundMode (qt.QWidget.BackgroundMode.PaletteBase)
		#self.contents.resize (self.)

		self.connect (self.horizontalScrollBar (), qt.SIGNAL ('valueChanged (int)'),
			      self.header_top, qt.SLOT ('setOffset(int)'))
		self.connect (self.header_top, qt.SIGNAL ('clicked(int)'), self.sort)
		self.connect (self.header_left, qt.SIGNAL ('clicked(int)'), self.select_toggle)
		self.connect (self.header_left, qt.SIGNAL ('clicked(int)'), self.select_notify)
		self.connect (self.verticalScrollBar (), qt.SIGNAL ('valueChanged (int)'),
			      self.header_left, qt.SLOT ('setOffset(int)'))
		self.connect (self.header_top, qt.SIGNAL ('sizeChange (int, int, int)'),
			      self.col_width_changed)
		self.connect (self.header_left, qt.SIGNAL ('sizeChange (int, int, int)'),
			      self.row_height_changed)

	#
	#------------------------------------------------------------
	# The following re-implement methods from qt.QTableView and qt.QWidget
	#------------------------------------------------------------
	#
	
	def contentsMousePressEvent (self, event):
		# get rid of the editor
		self.editor_save ()
		self.editor_cancel ()
		row_old = self.row_cur
		col_old = self.col_cur

		self.row_cur = self.row_at (event.pos().y())
		self.col_cur = self.col_at (event.pos().x())

		if self.row_cur == -1:
			self.row_cur = row_old
		if self.col_cur == -1:
			self.col_cur = col_old

		# if it is a new cell, repaint it
		if self.row_cur != row_old or self.col_cur != col_old:
			self.update_cell (row_old, col_old)
			self.update_cell (self.row_cur, self.col_cur)
			rh = self.row_height (self.row_cur)
			cw = self.col_width (self.col_cur)
			self.ensureVisible (self.col_pos (self.col_cur) + cw/2, self.row_pos (self.row_cur) + rh/2, cw/2, rh/2)
		return None

	def contentsMouseMoveEvent (self, event):
		self.contentsMousePressEvent (event)

	def focusInEvent (self, event):
		self.update_cell (self.row_cur, self.col_cur)

	def focusOutEvent (self, event):
		self.update_cell (self.row_cur, self.col_cur)

	def resizeEvent (self, event):
		qt.QScrollView.resizeEvent (self, event)
		self.update_geometries ()

	def showEvent (self, event):
		qt.QScrollView.showEvent (self, event)
		(x, y, width, height) = self.cell_geometry (self.rows () - 1, self.cols () - 1)
		self.resizeContents (x + width, y + height)
		self.update_geometries ()

	def focusNextPrevChild (self, next):
		dofocus = 0
		if not next:
			next = -1
		if self.editor:
			#self.editor_save ()

			# If we are either in the top left or bottom right corner
			# Just move vertically.  Make the self.key_last be vertical
			if self.col_cur == 0 and self.row_cur == 0:
				self.key_last = qt.Qt.Key.Key_Down
				return self.move_vertical (1, 1)
			if self.col_cur + 1== self.cols () and self.row_cur + 1 == self.rows ():
				self.key_last = qt.Qt.Key.Key_Up
				return self.move_vertical (-1, 1)

			# Based on the previous direction, keep moving in that
			# same direction.  If we are at a boundary, than
			# "slide" along that boundary.
			if self.key_last == qt.Qt.Key.Key_Left:
				if next * -1 + self.col_cur < 0:
					return self.move_vertical (next * -1, 1)
				return self.move_horizontal (next * -1, 1)
			elif self.key_last == qt.Qt.Key.Key_Right:
				if next * 1 + self.col_cur >= self.cols ():
					return self.move_vertical (next * 1, 1)
				return self.move_horizontal (next * 1, 1)
			elif self.key_last == qt.Qt.Key.Key_Up:
				if next * - 1 + self.row_cur < 0:
					return self.move_horizontal (next * -1, 1)
				return self.move_vertical (next * -1, 1)
			else: #self.key_last == qt.Qt.Key.Key_Down:
				if next * 1 + self.row_cur >= self.rows ():
					return self.move_horizontal (next * 1, 1)
				return self.move_vertical (next * 1, 1)
		else:
			return qt.QScrollView.focusNextPrevChild (self, next > 0)

	def keyPressEvent (self, event):
		if self.editor:
			#
			# We will ignore most keys (except Escape) and
			# let the returnPressed() signal from QMultiLineEdit
			# tell us person wants to move on or the
			# focus change which is handled by
			# self.focusChangeEvent ()
			if event.key () == qt.Qt.Key.Key_Escape:
				self.editor_cancel ()
			return None
			#elif event.key () == qt.Qt.Key.Key_Return or event.key () == qt.Qt.Key.Key_Return:
			#	self.editor_save ()
			# FIX: If we wanted, we could handle Page_Up, etc here
			#return
		row_old = self.row_cur
		col_old = self.col_cur
		key = event.key ()
		if key == qt.Qt.Key.Key_Tab:
			key = self.key_last
		if key == qt.Qt.Key.Key_Left:
			self.move_horizontal (-1, 0)
			self.key_last = key
		elif key == qt.Qt.Key.Key_Right:
			self.move_horizontal (1, 0)
			self.key_last = key
		elif key == qt.Qt.Key.Key_Up:
			self.move_vertical (-1, 0)
			self.key_last = key
		elif key == qt.Qt.Key.Key_Down:
			self.move_vertical (1, 0)
			self.key_last = key
		# FIX: Hmm, If this is a Key_Return, than create an editor and start editting
		#      If the editor does not exist, then do not create one!
		#      Otherwise, start editing with this new text.  Do we really
		#	want to just start editing on any key?
		elif key == qt.Qt.Key.Key_Enter or key == qt.Qt.Key.Key_Return:
			self.editor_display ()
		#else: # if event.text ()[0].isPrint ():
		#	t = repr (event.text ())
		#	if not t:
		#		event.ignore ()
		#		return
		#	self.editor_display (repr (event.text ()))
		return None

	def drawContents (self, painter, cell_x, cell_y, cell_width, cell_height):
		"""Reimplement QScrollView.drawContents().  Draws all the cells within the rectangle defined by x,y,width, height."""
		row_first = self.row_at (cell_y)
		row_last = self.row_at (cell_y + cell_height)

		col_first = self.col_at (cell_x)
		col_last = self.col_at (cell_x + cell_width)

		if row_first == -1 or col_first == -1:
			self.paint_empty (painter, cell_x, cell_y, cell_width, cell_height)
			return

		if row_last == -1:
			row_last = self.rows ()
		if col_last == -1:
			col_last = self.cols ()

		# Step through the rows
		for i in range (row_first, row_last+1):
			row_pos = self.row_pos (i)
			row_height = self.row_height (i);
			# Look at each column in the row
			for j in range (col_first, col_last+1):
				col_pos = self.col_pos (j)
				col_width = self.col_width (j)

				# Translate painter and draw the cell
				painter.saveWorldMatrix ()
				painter.translate (col_pos, row_pos)
				self.cell_paint (painter, i, j, (col_pos, row_pos, col_width, row_height))
				painter.restoreWorldMatrix ()
		self.paint_empty (painter, cell_x, cell_y, cell_width, cell_height)
	

	#
	#------------------------------------------------------------
	# The following is the "public" interface to this class
	#------------------------------------------------------------
	#
	def cell_data (self, row):
		"Return the data the application associated with item displayed at ROW.  Note: this is the currently displayed row, not the original order."
		return self.contents[row][0]

	def cell_text (self, row, col):
		"Return the contents of cell ROW, COL as a (string, wasstring)."
		val = self.cell_val (row, col)
		wasstring = 1
		if not isinstance (val, types.StringType):
			if val != None:
				val = repr (val)
				wasstring = 0
			else:
				val = ''
		return (val, wasstring)

	def cell_val (self, row, col):
		"Return the value associated with cell ROW, COL.  Note this may not be a string value."
		if row >= len (self.contents):
			return None
		(data, vals) = self.contents[row]
		if col >= len (vals):
			#raise 'To long at (%d, %d)' % (row, col)
			val = None
		else:
			val = vals[col]
		return val

	def cell_row_set (self, row, values, data = []):
		"Set the contents of cells at ROW with each col being in VALUES[i].  At display time, a string representation of VALUES[i] is displayed.  DATA is passed back to the application when a particular row is being acted upon."
		if not (isinstance (values, types.ListType) or isinstance (values, UserList.UserList)):
			raise 'values is not a listType'
		if len (self.contents) <= row:
			self.contents = self.contents + map (lambda arg:  (None, []), range (len (self.contents), row+1))
		self.contents[row] = (data, values)
		for col in range (0, self.cols ()):
			self.column_resize (col)
		self.row_resize (row)
		# FIX: How to update the contents?

	def cell_set (self, row, col, val, notify = 1):
		"Set the contents of cell at ROW, COL to VAL."
		if len (self.contents) <= row:
			raise 'Row %d is longer than number or rows (%d)' % (row, len (self.contents))
		(data, rowdata) = self.contents[row]
		if col >= len (rowdata):
			raise 'Column %d is longer than number of columns (%d)' % (col, len (rowdata))
		rowdata[col] = val
		for c in range (0, self.cols ()):
			self.column_resize (c)
		self.row_resize (row)
		self.repaint (self.row_pos (row), self.col_pos (col), self.row_height (row), self.col_width (col), 1)
		self.update_cell (row, col)
		# FIX: How to update the contents?

	def header_top_set (self, col, title):
		"Set the header in COL to the string TITLE."
		self.header_top.setLabel (col, title)
		self.column_resize (col)

	def header_row_set (self, row, title):
		"Set the header in ROW to the string TITLE."
		self.header_left.setLabel (row, title)
		self.row_resize (row)

	#
	#------------------------------------------------------------
	# The following are used to implement this class.
	# They should be considered "internal".
	#------------------------------------------------------------
	#

	def rows (self):
		"Return the number of rows in this table."
		return self.header_left.count ()

	def row_at (self, y):
		"Return the row that is at pixel location Y."
		return min (self.header_left.sectionAt (y), self.rows ())

	def row_height (self, row):
		"Return the height, in pixels, of ROW."
		return self.header_left.sectionSize (row)

	def row_height_changed (self, row, os, ns):
		self.updateContents (0, self.row_pos (row), self.contentsWidth (), self.contentsHeight ())
		(width, height) = self.table_size ()
		h = self.contentsHeight ()
		self.resizeContents (width, height)
		if self.contentsHeight () < h:
			self.repaintContents (0, h, self.contentsWidth (), h - height)
			#self.repaintContents (0, self.contentsHeight (), self.contentsWidth (), h - height)
		# FIX: editor
		self.update_geometries ()

	def row_resize (self, row):
		if not self.doupdates:
			return
		info = self.fontMetrics ()
		h = info.height ()
		for i in range (0, self.cols ()):
			r = info.height ()
			h = max (r, h)
		self.header_left.resizeSection (row, h + 4)
			

	def row_pos (self, row):
		"Return the position, in pixels, of the top of ROW."
		return self.header_left.sectionPos (row)

	def cols (self):
		"Return the number of columns in this table."
		return self.header_top.count ()

	def col_at (self, x):
		"Return the column that is at pixel location X."
		return min (self.header_top.sectionAt (x), self.cols ())

	def col_header (self, col):
		"Return the header displayed about COL."
		return self.header_top.label (col)

	def col_pos (self, col):
		"Return the position, in pixels, of the COL."
		return self.header_top.sectionPos (col)

	def column_resize (self, col):
		if not self.doupdates:
			return
		cols = self.cols ()
		info = self.fontMetrics ()
		# Figure out how wide it should be plus enough for arrow
		# This is based on a peak at the qheader.cpp code.
		w = info.width (self.header_top.label (col)) + 16 + info.height ()/2
		for i in range (0, self.rows ()):
			r = info.width (self.cell_text (i, col)[0])
			w = max (r, w)
		if w + 8 != self.header_top.sectionSize (col):
			self.header_top.resizeSection (col, w + 8)
			self.updateContents (self.col_pos (col), 0, self.contentsWidth (), self.contentsHeight ())
			#self.col_width_changed (col, self.header_top.sectionSize (col), w+8)

	def col_width (self, col):
		"Return the width, in pixels, of COL."
		return self.header_top.sectionSize (col)

	def col_width_changed (self, col, os, ns):
		self.updateContents (self.col_pos (col), 0, self.contentsWidth (), self.contentsHeight ())
		(width, height) = self.table_size ()
		w = self.contentsWidth ()
		self.resizeContents (width, height)
		if self.contentsWidth () < w:
			self.repaintContents (width, 0, w - width + 1, self.contentsHeight (), 1)
		# FIX: editor
		self.update_geometries ()


	def table_size (self):
		"Return the size of the table in pixels as a tuple: (width, height)."
		width = self.col_pos (self.cols () - 1) + self.col_width (self.cols () - 1)
		height = self.row_pos (self.rows () - 1) + self.row_height (self.rows () - 1)
		return (width, height)

	def paint_empty (self, painter, x, y, width, height):
		"Clear a region."
		reg = qt.QRegion (qt.QRect (x, y, width, height))
		(tw, th) = self.table_size ()
		r = qt.QRegion (qt.QRect (qt.QPoint (0, 0), qt.QSize (tw, th)))
		reg = reg.subtract (r)
		painter.save ()
		painter.setClipRegion (reg)
		painter.fillRect (x, y, width, height, self.colorGroup().brush (qt.QColorGroup.ColorRole.Base))
		painter.restore ()

	def cell_paint (self, painter, row, col, rect):
		"In PAINTER, draw the cell number ROW, COL located in tuple RECT (x, y, width, height)."
		(x, y, width, height) = rect
		x2 = width - 1
		y2 = height - 1

		# Background
		if row in self.selectlist:
			fg_color = qt.QColorGroup.ColorRole.HighlightedText
			bg_color = qt.QColorGroup.ColorRole.Light
		else:
			fg_color = qt.QColorGroup.ColorRole.Text
			bg_color = qt.QColorGroup.ColorRole.Base
		painter.fillRect (0, 0, width, height, self.colorGroup().brush (bg_color))
		pen = qt.QPen (painter.pen ())
		painter.setPen (qt.Qt.gray)
		painter.drawLine (x2, 0, x2, y2)
		painter.drawLine (0, y2, x2, y2)
		#painter.setPen (fg_color)
		painter.setPen (pen)

		# If we are in the focus cell, draw indication
		if row == self.row_cur and col == self.col_cur:
			if self.hasFocus () or self.viewport().hasFocus ():
				painter.drawRect (0, 0, x2, y2)

		x = 0;
		#pix = qt.QPixmap pix( cellPixmap( row, col ) );
		#if ( !pix.isNull() ) {
		#    p->drawPixmap( 0, ( cr.height() - pix.height() ) / 2, pix );
		#    x = pix.width() + 2;
		#}

		(t, string) = self.cell_text (row, col)
		# Draw contents
		if string:
			flag = qt.Qt.AlignmentFlags.AlignLeft
		else:
			flag = qt.Qt.AlignmentFlags.AlignRight
		painter.drawText( x, 0, width - x, height, flag | qt.Qt.AlignmentFlags.AlignVCenter, t)

	def cell_geometry (self, row, col):
		"Return the cell location and size in pixels: (x, y, width, height)."
		return (self.col_pos (col), self.row_pos (row), self.col_width (col), self.row_height (row))
		
	def move_redraw (self, row_new, col_new):
		"Move the currently selected item including redrawing any frames."
		# If at same spot, we don't need to change anything
		if self.row_cur == row_new and self.col_cur == col_new:
			return
		row_old = self.row_cur
		col_old = self.col_cur
		self.update_cell (row_new, col_new)
		self.row_cur = row_new
		self.col_cur = col_new
		self.editor_position ()
		cw = self.col_width (self.col_cur)
		rh = self.row_height (self.row_cur)
		self.ensureVisible (self.col_pos (self.col_cur) + cw/2,
				    self.row_pos (self.row_cur) + rh/2,
				    cw/2, rh/2)
		self.update_cell (row_old, col_old)

	def move_horizontal (self, direction, focus):
		"""Move left if DIRECTION < 0, right if DIRECTION > 0.  If FOCUS is true and we are at the edge, then move to another widget.  Relies on self.move_redraw() to redraw."""
		dofocus = 0
		col_new = self.col_cur
		if direction < 0:
			if self.col_cur == 0:
				dofocus = 1
			else:
				col_new = self.col_cur - 1
		else:
			if self.col_cur == self.cols() - 1:
				dofocus = 1
			else:
				col_new = self.col_cur + 1
		if focus and dofocus:
			return qt.QScrollView.focusNextPrevChild (self, direction > 0)
		else:
			#self.editor_display ()
			#self.editor_position ()
			self.move_redraw (self.row_cur, col_new)
			if self.editor:
				self.editor.selectAll ()
			return 1

	def move_vertical (self, direction, focus):
		"""Move down if DIRECTION > 0, up if DIRECTION < 0.  If FOCUS is true and we are at the edge, then move to another widget.  Relies on self.move_redraw() to redraw."""
		dofocus = 0
		row_new = self.row_cur
		if direction < 0:
			if self.row_cur == 0:
				dofocus = 1
			else:
				row_new = self.row_cur - 1
		else:
			if self.row_cur == self.rows () - 1:
				dofocus = 1
			else:
				row_new = self.row_cur + 1
		if focus and dofocus:
			return qt.QScrollView.focusNextPrevChild (self, direction > 0)
		else:
			#self.editor_display ()
			#self.editor_position ()
			self.move_redraw (row_new, self.col_cur)
			if self.editor:
				self.editor.selectAll ()
			return 1

	def editor_display (self, new_text = ''):
		"""If it doesn't exist, creates a QLineEdit (self.editor).  Sets the text of the self.editor to this cell (self.cur_row, self.cur_col) plus TEXT.  Then positions cell."""
		if not self.editor:
			self.editor = qt.QLineEdit ('', self.viewport ())
			self.connect (self.editor, qt.SIGNAL ('returnPressed()'),
				      self.editor_done)
			self.editor.setFrame (0)
		self.editor_position ()
		self.editor.selectAll ()

	def editor_position (self, new_text = ''):
		if not self.editor:
			return
		t = self.cell_text (self.row_cur, self.col_cur)[0] + new_text
		self.editor.setText (t)
		self.moveChild (self.editor, self.col_pos (self.col_cur) + 1, self.row_pos (self.row_cur) + 1)
		self.editor.resize (self.col_width (self.col_cur) - 2, self.row_height (self.row_cur) - 2)
		self.editor.show ()	# FIX: Move this to when it is created?
		self.editor.setFocus ()
		
	def editor_cancel (self):
		"Get rid of the editor without saving any results."
		if not self.editor:
			return
		self.viewport().removeChild (self.editor)
		self.removeChild (self.editor)
		self.editor.hide ()
		del self.editor
		self.editor = None
		self.viewport().setFocus ()

	def editor_done (self):
		"Same as self.editor_save() except it moves on to the next field."
		if not self.editor:
			return
		self.editor_save ()
		self.focusNextPrevChild (1)

	def editor_save (self):
		"User is finished with this cell.  Save the results."
		if not self.editor:
			return
		val = repr (self.editor.text ())
		oldval = self.cell_val (self.row_cur, self.col_cur)
		if not isinstance (oldval, types.StringType):
			try:
				val = eval (val, {}, {})
			except (SyntaxError, OverflowError, NameError):
				pass
		self.cell_set (self.row_cur, self.col_cur, val)
		if self.update_func:
			apply (self.update_func, (self, self.row_cur, self.col_cur))
		#self.editor_cancel ()

	def select_toggle (self, row):
		try:
			del self.selectlist[self.selectlist.index (row)]
		except ValueError:
			self.selectlist.append (row)
		self.repaintContents (0, self.row_pos (row), self.contentsWidth (), self.row_height (row), 1)

	def select_clear (self):
		for i in self.selectlist[0:]:
			self.select_toggle (i)

	def select_notify (self, row):
		self.emit (qt.PYSIGNAL ('selectionChanged'), ())

	def select_data_get (self):
		return map (lambda i, data = self.contents: data[i][0], self.selectlist)

	def select_get (self):
		return self.selectlist[0:]

	def select_set (self, row):
		if row not in self.selectlist:
			self.select_toggle (row)

	def update_enable (self, start):
		old = self.doupdates
		if start and not self.doupdates:
			recalc = 1
		else:
			recalc = 0
		self.doupdates = start
		if not recalc:
			return old
		for row in range (0, self.rows ()):
			self.row_resize (row)
		for col in range (0, self.cols ()):
			self.column_resize (col)
		return old

	def update_cell (self, row, col):
		(x, y, width, height) = self.cell_geometry (row, col)
		r = qt.QRect (x, y, width, height)
		self.updateContents (x, y, width, height)

	def update_geometries (self):
		(width, height) = self.table_size ()
		top_offset = self.header_top.offset ()
		top_width = self.header_top.width ()
		if top_offset and width < top_offset + top_width:
			self.horizontalScrollBar.setValue (width - top_width)

		left_offset = self.header_left.offset ()
		left_height = self.header_left.height ()
		if left_offset and height < left_offset + left_height:
			self.verticalScrollBar().setValue (height + left_height)

		self.header_left.setGeometry (self.leftMargin () -30 + 2,
					      self.topMargin () + 2,
					      30, self.visibleHeight ())
		self.header_top.setGeometry (self.leftMargin () + 2, self.topMargin () + 2 - self.fontMetrics ().height() - 4,
					     self.visibleWidth (),
					     self.fontMetrics().height() + 4)
	def sort (self, col):
		self.header_top.setSortIndicator (-1, 0)
		if self.sort_col == col:
			self.sort_increasing = not self.sort_increasing
		else:
			self.sort_increasing = 1
		self.header_top.setSortIndicator (col, self.sort_increasing)
		self.sort_col = col
		l = []
		row = 0
		for (data, values) in self.contents:
			l.append ((values[col], row, data, values))
			row = row + 1
		l.sort ()
		if not self.sort_increasing:
			l.reverse ()
		newcontents = []
		newselectlist = []
		i = 0
		for (field, row, data, values) in l:
			if row in self.selectlist:
				newselectlist.append (i)
			newcontents.append ((data, values))
			i = i + 1
		self.contents = newcontents
		self.selectlist = newselectlist
		self.viewport().update ()
		self.emit (qt.PYSIGNAL ('sort'), (str (self.col_header (col)),))
		

w = None
def test ():
	global w
	app = qt.QApplication (sys.argv)
	cols = 10
	rows = 30
	w = Table (rows, cols, None, 'Table')
	w.update_enable (0)
	for i in range (0, cols):
		w.header_top_set (i, '%d' % i)
		#top.setLabel (i, 'Header %d' % i)
		#top.resizeSection (i, 100)

	for i in range (0, rows):
		w.header_row_set (i, '%d' % i)
		#left.setLabel (i, str (i))
		#left.resizeSection (i, 20)
	for row in range (0, rows):
		vals = map (lambda i, row = row, cols = cols: row * cols + i, range (0,cols)) 
		w.cell_row_set (row, vals)
	w.update_enable (1)
	app.setMainWidget (w)
	w.show ()

	return app

if __name__ == '__main__':
	print 'starting app...',
	app = test ()
	print 'done'
	app.exec_loop ()


More information about the PyQt mailing list