Table.py :  » GUI » PmwContribD » PmwContribD-r2_0_2 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » PmwContribD 
PmwContribD » PmwContribD r2_0_2 » Table.py
#!/usr/bin/env python
#
# $Id: Table.py,v 1.5 2002/04/30 11:09:11 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

"""A Table widget for displaying tabular data.

"""

__rcs_info__ = {
    #
    #  Creation Information
    #
    'module_name'  : '$RCSfile: Table.py,v $',
    'rcs_id'       : '$Id: Table.py,v 1.5 2002/04/30 11:09:11 doughellmann Exp $',
    'creator'      : 'Doug Hellmann <doug@hellfly.net>',
    'project'      : 'PmwContribD',
    'created'      : 'Sat, 05-May-2001 13:26:12 EDT',

    #
    #  Current Information
    #
    'author'       : '$Author: doughellmann $',
    'version'      : '$Revision: 1.5 $',
    'date'         : '$Date: 2002/04/30 11:09:11 $',
}

#
# Import system modules
#
import Tkinter, Canvas, tkFont
import UserList
import string
import math


#
# Import Local modules
#
from ShadowBox import ShadowBox

class BorderLessCanvasGroup(Canvas.Group):
    """A subclass of Canvas.Group which knows how to do bbox.
    """
    def __init__(self, canvas, width=1):
        self.width = width
        Canvas.Group.__init__(self, canvas)
    
    def bbox(self):
        "Returns the bounding box of the items in the group."
        objs = self.canvas.find_withtag(self.tag)
        #ulx,uly,lrx,lry = 0,0, 0,0
        ulx,uly,lrx,lry = 100000,100000, 0,0
        for item in objs:
            x1,y1,x2,y2 = self.canvas.bbox(item)
            x1 = x1 + self.width
            y1 = y1 + self.width
            x2 = x2 - self.width
            y2 = y2 - self.width
            if x1 < ulx: ulx = x1
            if x2 > lrx: lrx = x2
            if y1 < uly: uly = y1
            if y2 > lry: lry = y2
        return ( (ulx,uly), (lrx,lry) )


#
# Module
#

class Column:
    """A single Column in a Table.
    """
    def __init__(
        self, 
        title, 
        datasource, 
        width, 
        rowheight,
        column=0,
        titletextcolor='black', 
        titlebackground='grey',
        celltextcolor='black',
        cellbackground='white',
        celloutline='black',
        titlealignment='center',
        cellalignment='sw',
        cellpadding=2,
        icellpadding=1,
        font=('Helvetica', '-12'),
        *args, **kw):

        #
        self.titlecell = None
        # where we get our data
        self._datasource = datasource
        # how we display the data
        self.title = title
        
        # this column number
        self.column = column

        # auto-calculate width if we have not been given it
        self.font = font
        if width == 0:
            self.width = self.auto_width()
        else:
            self.width = width
        self.rowheight = rowheight
        self.titletextcolor=titletextcolor
        self.titlebackground=titlebackground
        self.celltextcolor=celltextcolor
        self.cellbackground=cellbackground
        self.celloutline=celloutline
        self.titlealignment = titlealignment
        self.cellalignment = cellalignment
        self.cellpadding = cellpadding
        self.icellpadding = icellpadding
        self.visibleCells = {}
        return

    def auto_width(self):
        """Calculate the width of this column automatically.

        The algorithm assumes that 'm' is the longest character.  The
        screen width of the 'm' character in the current font is used
        to estimate the screen width of all of the row values, and the
        longest estimate is used as the default width.
        """
        f = tkFont.Font(font=self.font)
        w = f.measure('m')

        # now find longest string in data
        maxwidth = len(self.title)
        for row in range(-1, len(self._datasource)):
            maxwidth = max(maxwidth, len(self.getRowStr(row)))

        return maxwidth * w        

    def __str__(self):
        return str(self.title)
    
    def getRowStr(self, row):
        """Get the string to display for a specified row.
        """
        if row == -1:
            return self.title
        return str(self._datasource(row, self.column))
  
    def numRows(self):
        "How many rows does the column have?"
        return len(self._datasource)

    def __len__(self):
        return self.numRows()

    def columnWidth(self):
        "How wide is the column on the screen?"
        return self.width

    def hideCanvasObjects(self, rowRange, offScreenFlag):
        """Hide the objects related to the rows in the specified range.

        If the offScreenFlag is true, also hide the title cell for the
        column.
        """
        #print '"%s".hideCanvasObjects(%s, %s)' % (self,
        #                                        rowRange,
        #                                        offScreenFlag)
        if offScreenFlag:
            self.titlecell.delete()
            self.titlecell = None
        for cellNum in rowRange:
            try:
                cell = self.visibleCells[cellNum]
                if cell:
                    cell.delete()
                    self.visibleCells[cellNum] = None
            except KeyError:
                pass
        return

    def showCanvasObjects(self, canvas, leftEdgeX, startRow, endRow):
        """Show the objects related to the rows in the specified range.

        This is the primary "paint" method.
        """
        #print '"%s".showCanvasObjects leftX = %d, rows: %d-%d)' % \
        #      (self, leftEdgeX, startRow, endRow)
        #
        # Deal with the title cell.  It should never
        # scroll off of the screen, and might not
        # exist when we start.
        #
        if not self.titlecell:
            # self.createCanvasObjects(canvas)
            self.titlecell = self.createTitleCell(canvas, self.title)
        leftEdgeX = leftEdgeX + self.cellpadding
        bbox = self.titlecell.bbox()
        deltaX = (leftEdgeX - bbox[0][0])
        deltaY = (0 - bbox[0][1])
        self.titlecell.move( deltaX, deltaY )
        #print 'new title cell bbox is ', self.titlecell.bbox()
        #
        # Now handle the regular cells.  Determine
        # the initial yOffset based on the height
        # of the title cell.  
        #
        yOffset = self.cellpadding + self.rowheight + self.cellpadding
        for row in range(startRow, endRow):
            #
            # Check for an existing cell representation
            #
            try:
                cell = self.visibleCells[row]
            except KeyError:
                cell = None
            #
            # If there is no cell, create it.
            #
            if not cell:
                try:
                    cell = self.createCell(canvas, row, leftEdgeX, yOffset)
                except IndexError:
                    #
                    # The data source will cause an IndexError
                    # when we try to go too far through the
                    # rows.
                    #
                    break
                self.visibleCells[row] = cell
                #
                # Set the yOffset so that the next cell
                # will be just below this one
                #
                ## yOffset = cell.bbox()[1][1] + self.cellpadding
                yOffset += self.rowheight + self.cellpadding
            else:
                #
                # If there was a cell, move it into
                # position.
                #
                bbox = cell.bbox()
                ulx = bbox[0][0]
                uly = bbox[0][1]
                #deltaX = leftEdgeX - ulx
                deltaX = leftEdgeX - ulx
                deltaY = yOffset - uly

                if deltaX or deltaY:
                    cell.move(deltaX, deltaY)
                #
                # Set the yOffset so that the next cell
                # will be just below this one
                #
                yOffset = yOffset + self.rowheight + self.cellpadding
        return

    def alignmentPoint(self, alignment, bbox):
        """Find the point on the 'bbox' around which we are aligning an object.

        The 'alignment' value is a string such as would be passed as
        an alignment argument to 'pack' or another geometry manager.

        If 'alignment' includes

          's' - (South) -- Use the bottom of the 'bbox' for the Y coordinate.

          'n' - (North) -- Use the top of the 'bbox' for the Y coordinate.

          'w' - (West) -- Use the left side of the 'bbox' for the X coordinate.

          'e' - (East) -- Use the right side of the 'bbox' for the X coordinate.

        If no instruction is found for one of the coordinates, the
        default behavior is to use the center of the 'bbox' for that
        axis.
        """
        ulx = bbox[0][0] + self.icellpadding
        uly = bbox[0][1] + self.icellpadding
        lrx = bbox[1][0] - self.icellpadding
        lry = bbox[1][1] - self.icellpadding
        xcenter = (ulx + lrx)/2
        ycenter = (uly + lry)/2

        #print 'getting alignment point for %s in ' % alignment, bbox
        
        if alignment == 'center':
            return (xcenter, ycenter)

        x = xcenter
        y = ycenter
        
        if 's' in alignment:
            y = lry
        elif 'n' in alignment:
            y = uly
        else:
            y = ycenter

        if 'w' in alignment:
            x = ulx
        elif 'e' in alignment:
            x = lrx
        else:
            x = xcenter
            
        return (x,y)

    def createCell(self, canvas, row, ulx, uly):
        """
        Create the cell containing the information in
        the specified row of the table.
        """
        width = self.width
        height = self.rowheight
        #
        # Create the outline rectangle for
        # the cell
        #
        text=self.getRowStr(row)
        newCell = Canvas.Rectangle(
            canvas,
            ulx, uly,
            ulx + width, uly + height,
            fill=self.cellbackground,
            #outline=self.cellbackground,
            outline=self.celloutline,
            width=1,
            #width=2,
            #width=0,
            )
        #
        # Create the text for the cell
        #
        point = self.alignmentPoint(self.cellalignment, newCell.bbox())
        newCellText = Canvas.CanvasText(
            canvas,
            point[0], point[1],
            text=text,
            fill=self.celltextcolor,
            anchor=self.cellalignment,
            )
        #
        # Create a group through which both
        # cell objects can be managed
        #
        newCellGroup = BorderLessCanvasGroup(canvas)
        newCell.addtag(newCellGroup)
        newCellText.addtag(newCellGroup)
        return newCellGroup

    def createTitleCell(self, canvas, label):
        """Create the cell for the column title.
        """
        titleCell = ShadowBox(
            canvas,
            ulx=0,
            uly=0,
            width=self.width,
            #height=self.rowheight + (self.cellpadding * 2),
            height=self.rowheight,
            background=self.titlebackground,
            )
        point = self.alignmentPoint('center', titleCell.bbox())
        titleText = Canvas.CanvasText(
            canvas,
            point[0], point[1],
            text=label,
            fill=self.titletextcolor,
            )
        titleCell.add_object(titleText)
        return titleCell

    # def createCanvasObjects(self, canvas):
    #     """
    #     Create the on-screen objects to display
    #     this column.
    #     """
    #     self.titlecell = self.createTitleCell(canvas, self.title)
    #     return


class RowLabelColumn(Column):
    """Column for holding the row labels.
    """
    def __init__(self, *args, **kw):
        apply(Column.__init__, (self,) + args, kw)
        
    def createTitleCell(self, canvas, label):
        titleCell = ShadowBox(
            canvas,
            ulx=0,
            uly=0,
            width=self.width,
            height=self.rowheight,
            background=self.titlebackground,
            )

        return titleCell
    
    def createCell(self, canvas, row, ulx, uly):
        newCell = Column.createTitleCell(self, canvas, str(row+1))
        (x1,y1),(x2,y2) = newCell.bbox()
        newCell.move( ulx - x1, uly - y1 )
        return newCell
  
class ColumnDataSource(UserList.UserList):
    """A data source for a Column.
    """
    def __call__(self, row, col=0):
        #return self.data[row]
        return self[row]


class Table(Tkinter.Frame):
    """A widget for displaying tabular data.
    """
    def __init__(
        self, 
        parent,
        cellpadding=2,
        rowheight=15,
        xfreeze=1,
        font=('Helvetica', '-12'),
        rowlabels=1,
        keybindings=1,
        *args, **kw):
        """Construct a Table.

        Arguments

          parent -- Parent Tk widget.

          cellpadding=2 -- Space in pixels between cells.

          rowheight=15 -- Height in pixels of rows.

          xfreeze=1 -- Number of columns from the left edge to hold
                       locked on the screen.  These columns do not
                       scroll horizontally.

          font=('Helvetica', '-12') -- Font specification for contents of cells.

          rowlabels=1 -- Boolean controlling whether rows are labeled
                       as columns are.

          keybindings=1 -- Boolean controlling whether default
                       keybindings are applied.

        """
        #
        # Get our specific parameters
        #
        self.cellpadding = cellpadding
        self.rowheight = rowheight
        #
        # Handle the rest of the init
        #
        apply(Tkinter.Frame.__init__, (self, parent) + args, kw)
        #
        # Create some member data variables
        #
        self.rightColumn = 0
        self.topRow = 0
        self.bottomRow = 0
        self.numRows = 1
        self._columns = []
        self.xfreeze = max(xfreeze, rowlabels)
        self.leftColumn = self.xfreeze
        self.font = font
        self.rowlabels = rowlabels
        self.createwidgets()
        if keybindings:
            self._bind(self.canvas)
        return
    
    def createwidgets(self):
        "Create the widget components of the Table."
        #
        # Scrollbars
        #
        self.horizSB = Tkinter.Scrollbar(
            self,
            orient='horizontal',
            command=self._xview,
            )
        self.horizSB.pack(
            side=Tkinter.BOTTOM,
            expand=Tkinter.NO,
            fill=Tkinter.X,
            )
        self.vertSB = Tkinter.Scrollbar(
            self,
            orient='vertical',
            command=self._yview,
            )
        self.vertSB.pack(
            side=Tkinter.RIGHT,
            expand=Tkinter.NO,
            fill=Tkinter.Y,
            )
        #
        # Canvas drawing area
        #
        self.canvas = Tkinter.Canvas(
            self,
            background='white',
            xscrollcommand=self.horizSB.set,
            yscrollcommand=self.vertSB.set,
            )
        self.canvas.pack(
            side=Tkinter.TOP,
            anchor=Tkinter.W,
            expand=Tkinter.YES,
            fill=Tkinter.BOTH,
            )
        self.canvas.bind('<Configure>', self.configureEventHandler)
        
        self.titleSlopRectangle = None
        self.cellSlopRectangle = None
        return

    def _bind(self, widget):
        "Apply default keybindings to allow convenient movement with keyboard."
        widget.focus_set()
        widget.bind('<Next>', self.scrolldownpage)
        widget.bind('<Prior>', self.scrolluppage)
        widget.bind('<Down>', self.scrolldownline)
        widget.bind('<Up>', self.scrollupline)
        
        widget.bind('<Home>', self.scrollbegin)
        widget.bind('<End>', self.scrollend)
        
        widget.bind('<Right>', self.scrollright)
        widget.bind('<Left>', self.scrollleft)
        widget.bind('<Control-Right>', self.scrollrightpage)
        widget.bind('<Control-Left>', self.scrollleftpage)
        return

    def scrolldownpage(self, event):
        "Handle page down event."
        self._yview('scroll', '1', 'page')
        return
    
    def scrolluppage(self, event):
        "Handle page up event."
        self._yview('scroll', '-1', 'page')
        return

    def scrolldownline(self, event):
        "Handle scroll down event."
        self._yview('scroll', '1', 'units')
        return
        
    def scrollupline(self, event):
        "Handle scroll up event."
        self._yview('scroll', '-1', 'units')
        return

    def scrollright(self, event):
        "Handle scroll right event."
        self._xview('scroll', '1', 'units')
        return
        
    def scrollleft(self, event):
        "Handle scroll left event."
        self._xview('scroll', '-1', 'units')
        return

    def scrollrightpage(self, event):
        "Handle page right event."
        self._xview('scroll', '1', 'page')
        return
        
    def scrollleftpage(self, event):
        "Handle page left event."
        self._xview('scroll', '-1', 'page')
        return

    def scrollend(self, event):
        "Handle 'end' event."
        self._yview('moveto', '1.0')
        return
        
    def scrollbegin(self, event):
        "Handle 'begin' event."
        self._yview('moveto', '0.0')
        return

    def addcolumn(self, column):
        """Add the column to the list of columns managed by this Table.
        """
        self._columns.append(column)
        self.numRows = max([self.numRows, column.numRows()])
        return
    
    def configureEventHandler(self, event=None):
        """Event handler for configure event on the canvas.
        """
        rightColumn = self._findRightColumnFromLeft(self.leftColumn,1)
        leftColumn = self._findLeftColumnFromRight(rightColumn)
        rightColumn = self._findRightColumnFromLeft(self.leftColumn)
        bottomRow = self._findBottomRowFromTop(self.topRow)
        topRow = self._findTopRowFromBottom(bottomRow)

        self.setVisibleArea(
            leftColumn, rightColumn,
            topRow, bottomRow
            )
        self.canvas.after_idle(self.updateScrollbars)
        return

    def clearScreenBeforeMove(self, newLeft, newRight, newTop, newBottom):
        """Clear anything that used to be in the display range but isn't any more.
        """
        # print '\nclearScreenBeforeMove(%d, %d, %d, %d)' % (newLeft,
        #                                                    newRight,
        #                                                    newTop,
        #                                                    newBottom)
        # print '   current settings: (%d, %d, %d, %d)' % (
        #     self.leftColumn, self.rightColumn,
        #     self.topRow, self.bottomRow)

        if ( (self.leftColumn >= newLeft) and 
             (self.rightColumn <= newRight) and
             (self.topRow >= newTop) and
             (self.bottomRow <= newBottom)
             ):
            # no changes needed
            # print 'no clearing needed'
            return
        #
        # Figure out the column situation
        #
        colRange = None
        rowRange = None
        if ( (newLeft <= self.leftColumn) and (newRight < self.rightColumn) ):
            #print '\tmoved left'
            # moved view window to the left
            # so we need to clear off the right edge
            colRange = xrange(newRight, self.rightColumn)
            xcolRange = xrange(self.leftColumn, newRight)
        elif ( (newRight >= self.rightColumn) and (newLeft > self.leftColumn) ):
            #print '\tmoved right'
            # moved view window to the right
            # so we need to clear off the left edge
            colRange = xrange(self.leftColumn, newLeft)
            xcolRange = xrange(newRight, self.rightColumn)
        
        #
        # Figure out the row situation
        #
        if ( (newTop <= self.topRow) and (newBottom < self.bottomRow) ):
            #print '\tmoved up'
            # moved view window up
            # so we need to clear off the bottom
            rowRange = xrange(newBottom, self.bottomRow)
        elif ( (newBottom >= self.bottomRow) and (newTop > self.topRow) ):
            #print '\tmoved down'
            # moved view window DOWN
            # so we need to clear off the top
            rowRange = xrange(self.topRow, newTop)
        
        #
        # Clear stuff from the screen
        #

        # Watch out in case both x and y have changed
        if colRange != None and rowRange != None:
            # erase all rows in colRange
            self._hideCanvasObjects(xrange(self.topRow, self.bottomRow),
                                    colRange, newLeft, newRight)

            # set colRange so code below will result in all
            # cells in xcolRange, rowRange will be erased
            colRange = xcolRange

        # now None means all columns/rows
        if rowRange == None:
            rowRange = xrange(self.topRow, self.bottomRow)
        if colRange == None:
            colRange = xrange(self.leftColumn, self.rightColumn)

        # erase what is left to erase
        self._hideCanvasObjects(rowRange, colRange, newLeft, newRight)

        # do any frozen columns
        for columnNum in range(self.xfreeze):
            column = self._columns[columnNum]
            self._columns[columnNum].hideCanvasObjects(rowRange, 0)

        return

    def _hideCanvasObjects(self, rowRange, colRange, newLeft, newRight):
        """Hide the objects related to the columns and rows in the specified range.
        """
        # print 'erasing rows/cols:', rowRange, colRange
        for columnNum in colRange:
            column = self._columns[columnNum]
            if (columnNum < newLeft) or (columnNum >= newRight):
                offScreen = 1
            else:
                offScreen = 0
            self._columns[columnNum].hideCanvasObjects(rowRange, offScreen)
        return

    def refresh(self):
        """Refresh screen.

        For example, after sorting the datasource.
        """
        rowRange = xrange(self.topRow, self.bottomRow)
        colRange = xrange(self.leftColumn, self.rightColumn)
        self._hideCanvasObjects(rowRange, colRange,
                                self.leftColumn, self.rightColumn)
        self.setVisibleArea(self.leftColumn, self.rightColumn,
                            self.topRow, self.bottomRow)
        return

    def setVisibleArea(self, left, right, top, bottom):
        """Update which columns are displayed.
        """
        #
        # Clear what just moved off of the screen
        #
        self.clearScreenBeforeMove(left, right, top, bottom)
        #
        # Update the internal state
        #
        self.topRow = top
        self.bottomRow = bottom
        self.leftColumn = left
        self.rightColumn = right
        #
        # Update the scrollbars
        #
        self.updateScrollbars()
        #
        # Update the canvas
        #
        offsetX = self.cellpadding

        # do any frozen columns
        for columnNum in range(self.xfreeze):
            column = self._columns[columnNum]
            column.showCanvasObjects(self.canvas, offsetX, top, bottom)
            offsetX = offsetX + column.width + self.cellpadding

        # now do non-frozen columns
        for columnNum in range(left, right):
            column = self._columns[columnNum]
            column.showCanvasObjects(self.canvas, offsetX, top, bottom)
            offsetX = offsetX + column.width + self.cellpadding
        #slop for the title
        if not self.titleSlopRectangle:
            self.titleSlopRectangle = Canvas.Rectangle(
                self.canvas,
                0, 0,
                10, 10,
                fill='grey',
                outline='grey',
                )
        bbox = self._columns[left].titlecell.bbox()
        slopHeight = bbox[1][1] - bbox[0][1] - 1
        self.titleSlopRectangle.coords( 
            ( (offsetX, 0), 
              (self.canvas.winfo_width(),
               slopHeight)
              )
            )
        self.titleSlopRectangle.tkraise()
        # slop for the cells
        if not self.cellSlopRectangle:
            self.cellSlopRectangle = Canvas.Rectangle(
                self.canvas,
                0, 0,
                10, 10,
                fill='white',
                outline='white'
                )
        bbox = self.titleSlopRectangle.bbox()
        self.cellSlopRectangle.coords (
            ( (bbox[0][0],bbox[1][1]),
              (bbox[1][0],self.canvas.winfo_height())
              )
            )
        return

    def updateScrollbars(self):
        """
        Compute the values for start and stop of the scrollbars
        and call their set methods to set them correctly.
        """
        self.updateHScrollbar()
        self.updateVScrollbar()
        return

    def updateVScrollbar(self):
        """Update the settings of the vertical scrollbar.
        """
        bottomRow = self._findBottomRowFromTop(self.topRow)

        if bottomRow == 0:
            top = 0
            bottom = 0
        else:
            top = self.topRow * 1.0 / (self.numRows + 1)
            bottom = bottomRow * 1.0 / (self.numRows + 1)
        self.vertSB.set( top, bottom )
        return

    def updateHScrollbar(self):
        """Update the settings of the horizontal scrollbar.
        """
        rightColumn = self._findRightColumnFromLeft(self.leftColumn, 1)
        numColumns = len(self._columns) - self.xfreeze
        if rightColumn == 0:
            left = 0
            right = 0
        else:
            left = (self.leftColumn - self.xfreeze) * 1.0 / numColumns
            right = (rightColumn - self.xfreeze) * 1.0 / numColumns
        self.horizSB.set( left, right )
        return

    def _findBottomRowFromTop(self, topRow):
        "Determine the last visible row on the screen, given the first."
        height = self.canvas.winfo_height()
        visibleRows = math.floor(height / (self.rowheight +
                                          self.cellpadding))
        lastRow = min(self.numRows+1, topRow + visibleRows)
        return lastRow

    def _findTopRowFromBottom(self, bottomRow):
        "Determine the first visible row on the screen, given the last."
        height = self.canvas.winfo_height()
        visibleRows = math.ceil(height / (self.rowheight +
                                          self.cellpadding))
        firstRow = max(0, bottomRow - visibleRows)
        return firstRow

    def _findRightColumnFromLeft(self, leftColumn, fullonly=0):
        """
        Given a left column number, determine the
        right-most column which can be displayed if
        that column is the left-most column on the
        screen.
        """
        if not self._columns:
            rightColumn = 0
        else:
            width = self.canvas.winfo_width()
            for x in range(self.xfreeze):
                column = self._columns[x]
                width = width - (column.columnWidth() +
                                 self.cellpadding)
            rightColumn = leftColumn
            columnWidths = 0
            while (
                (columnWidths <= width) and 
                (rightColumn < len(self._columns))
                ):
                column = self._columns[rightColumn]
                widthIncr = column.columnWidth()
                #print 'widthincr', widthIncr
                #print 'columnWidths', columnWidths
                #print 'spacing', self.cellpadding
                columnWidths = (columnWidths + 
                                widthIncr +
                                self.cellpadding)
                rightColumn = rightColumn + 1
                
            if columnWidths > width and fullonly:
                rightColumn = max(0, rightColumn-1)

        return rightColumn

    def _findLeftColumnFromRight(self, rightColumn):
        """
        Given a right column number, determine the
        left-most column which can be displayed if
        that column is the right-most on the screen.
        """
        if not self._columns:
            return 0
        else:
            width = self.canvas.winfo_width()
            for x in range(self.xfreeze):
                column = self._columns[x]
                width = width - (column.columnWidth() +
                                 self.cellpadding)

            leftColumn = rightColumn - 1
            columnWidths = 0
            while (
                (columnWidths <= width) and 
                (leftColumn >= self.xfreeze)
                ):
                column = self._columns[leftColumn]
                widthIncr = column.columnWidth()
                columnWidths = (columnWidths + 
                                   widthIncr +
                                   self.cellpadding)
                leftColumn = leftColumn - 1
            leftColumn = leftColumn + 1
            if columnWidths > width:
                leftColumn = leftColumn + 1

            if leftColumn < self.xfreeze:
                leftColumn = self.xfreeze
            return leftColumn

    def _xview(self, mode, value, units=None):
        """Callback for horizontal scrollbar.
        """
        # print 'xview', mode, value, units
        if mode == 'moveto':
            # fraction of number of columns
            value = max(0, min(string.atof(value), 1.0))
            newLeft = (self.xfreeze + 
                       int( math.floor( value * (len(self._columns) - self.xfreeze))))
            # Do not let the indicator move too far to the right
            newRight = self._findRightColumnFromLeft(newLeft, 1)
            while (newLeft >= self.xfreeze and
                   newRight == self._findRightColumnFromLeft(newLeft, 1) ):
                newLeft = newLeft - 1
            newLeft = newLeft + 1
      
        elif units == 'units':
            # increment one column at a time
            moveBy = string.atoi(value)
            if (self.leftColumn == self.xfreeze) and (moveBy < 0):
                return
            currentRight = self._findRightColumnFromLeft(self.leftColumn, 1)
            if (currentRight == len(self._columns)) and (moveBy > 0):
                return
            newLeft = self.leftColumn + moveBy

        elif mode == 'scroll':
            # move one page at a time
            moveBy = string.atoi(value)
            import sys
            if moveBy > 0:
                newLeft = self._findRightColumnFromLeft(self.leftColumn)
                newLeft = max(self.xfreeze, newLeft-1)
                newRight = self._findRightColumnFromLeft(newLeft, 1)

                while ( newRight == self._findRightColumnFromLeft(newLeft, 1) ):
                    newLeft = newLeft - 1
                newLeft = newLeft + 1
            else:
                newLeft = self._findLeftColumnFromRight(self.leftColumn)
        else:
            print 'WHAT?'
            
        # Do some range/optimization checking
        if newLeft < self.xfreeze:
            newLeft = self.xfreeze
        if newLeft == self.leftColumn:
            return

        # get right column including partial
        newRight = self._findRightColumnFromLeft(newLeft)

        # Change the left column and fix up the scrollbar
        self.setVisibleArea(newLeft, self._findRightColumnFromLeft(newLeft),
                            self.topRow, self.bottomRow)
        return

    def _yview(self, mode, value, units=None):
        """Callback for vertical scrollbar.
        """
        if mode == 'moveto':
            # fraction of number of columns
            newTop = int( math.floor( string.atof(value) * 
                                      self.numRows))
            if newTop < 0: newTop = 0
            newBottom = self._findBottomRowFromTop(newTop)
            newTop = self._findTopRowFromBottom(newBottom)
      
        elif units == 'units':
            # increment one column at a time
            moveBy = string.atoi(value)
            if (self.topRow == 0) and (moveBy < 0):
                return
            currentBottom = self._findBottomRowFromTop(self.topRow)
            if (currentBottom == self.numRows+1) and (moveBy > 0):
                return
            newTop = self.topRow + moveBy

        else:
            # move one page at a time
            moveBy = string.atoi(value)
            if moveBy > 0:
                newTop = self._findBottomRowFromTop(self.topRow)
                newBottom = self._findBottomRowFromTop(newTop)
                newTop = self._findTopRowFromBottom(newBottom)
            else:
                newTop = self._findTopRowFromBottom(self.topRow)
                if newTop < 0: newTop = 0

        # Do some range/optimization checking
        if newTop == self.topRow:
            return

        # Change the left column and fix up the scrollbar
        self.setVisibleArea(self.leftColumn, self.rightColumn,
                            newTop, self._findBottomRowFromTop(newTop))
        return
    
    def load(self, source, titles=None):
        """Load Table with data from source.

        Source should implement the following methods::
        
          __call__(self, row=0, col=0)  # return data for row/col

          __len__(self) # return number of rows in the datasource

          columns(self) # return number of columns in the datasource
        
        """

        # insert column for rowlabels if we need them
        if self.rowlabels:
            self.addcolumn( RowLabelColumn(
                title='',
                datasource=[],
                width=50,
                rowheight=self.rowheight,
                cellalignment='se',
                cellpadding=self.cellpadding))
    
        # add a column for each title
        for i in range(source.columns()):
            if titles == None:
                title='Col %d' % (1+i)
            else:
                title=titles[i]
                
            self.addcolumn( Column(
                title=title,
                datasource=source,
                column=i,
                width=0,
                font=self.font,
                rowheight=self.rowheight,
                cellalignment='se',
                cellpadding=self.cellpadding))
        return
    


if __name__ == '__main__':
    TESTROWS=50

    import GuiAppD

    class TestDataSource(ColumnDataSource):
        def __init__(self, base=0):
            ColumnDataSource.__init__(self)
            for i in range(TESTROWS):
                self.data.append(str(base+i))
            return
  
    class TestApp(GuiAppD.GuiAppD):
        def createInterface(self):
            pad=1
            rowheight=15
            self.table = Table(self.interior(), cellpadding=pad, xfreeze=1)
            self.table.addcolumn( RowLabelColumn(
                title='',
                datasource=TestDataSource(),
                width=50,
                rowheight=rowheight,
                cellalignment='se',
                cellpadding=pad,
                ))
            for i in range(10):
                self.table.addcolumn( Column(
                    title='Col %d' % i, 
                    datasource=TestDataSource(base=10**i),
                    #width=50 + (10 * i),
                    width=0,
                    rowheight=rowheight,
                    cellalignment='se',
                    cellpadding=pad,
                    ) )
                self.table.pack(fill=Tkinter.BOTH,
                                expand=Tkinter.YES)
                
            return

    class DataSource:
        def __init__(self, data, formats=None):
            self.data = data
            self.formats = formats
            self.guess_len = 0

        def __call__(self, row=0, col=0):
            if self.formats != None:
                return self.formats[col](self.data[row][col])
            else:
                return repr(self.data[row][col])

        def __len__(self):
            return len(self.data)

        def columns(self):
            return len(self.data[0])
    
        def sort(self):
            self.data.sort()

        def reverse(self):
            self.data.reverse()
        
    class TestApp2(GuiAppD.GuiAppD):
        usebuttonbox=1
        def createInterface(self):
            pad=1
            rowheight=15
            self.table = Table(self.interior(), cellpadding=pad,
                               rowlabels=1, rowheight=15, font='fixed')


            data = []

            for i in range(TESTROWS):
                row = []
                for j in range(10):
                    row.append(i + 10**j)
                data.append(row)

            self.source = DataSource(data)

            self.table.load(self.source)
            self.table.pack(fill=Tkinter.BOTH,
                            expand=Tkinter.YES)

            self.buttonAdd('Sort',
                           'sort data',
                           'sort data', command=self.sort)
            self.buttonAdd('Reverse',
                           'reverse data',
                           'reverse data', command=self.reverse)
            return

        def sort(self):
            self.source.sort()
            self.table.refresh()

        def reverse(self):
            self.source.reverse()
            self.table.refresh()


    

    TestApp2().run()

    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.