Module tkintertable.Tables

Implements the core TableCanvas class. Created Oct 2008 Copyright (C) Damien Farrell

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Source code
#!/usr/bin/env python
"""
    Implements the core TableCanvas class.
    Created Oct 2008
    Copyright (C) Damien Farrell

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""

from __future__ import absolute_import, division, print_function
try:
    from tkinter import *
    from tkinter.ttk import *
except:
    from Tkinter import *
    from ttk import *
if (sys.version_info > (3, 0)):
    from tkinter import filedialog, messagebox, simpledialog
    from tkinter import font
    import tkinter.colorchooser as tkColorChooser
else:
    import tkFileDialog as filedialog
    import tkSimpleDialog as simpledialog
    import tkMessageBox as messagebox
    import tkFont as font
    import tkColorChooser

from .TableModels import TableModel
from .TableFormula import Formula
from .Prefs import Preferences
from .Dialogs import *

import math, time
import os, types
import copy
import platform

class TableCanvas(Canvas):
    """A tkinter class for providing table functionality"""

    def __init__(self, parent=None, model=None, data=None, read_only=False,
                 width=None, height=None,
                 rows=10, cols=5, **kwargs):
        Canvas.__init__(self, parent, bg='white',
                         width=width, height=height,
                         relief=GROOVE,
                         scrollregion=(0,0,300,200))
        self.parentframe = parent
        #get platform into a variable
        self.ostyp = self.checkOSType()
        self.platform = platform.system()
        self.width = width
        self.height = height
        self.set_defaults()

        self.currentpage = None
        self.navFrame = None
        self.currentrow = 0
        self.currentcol = 0
        self.reverseorder = 0
        self.startrow = self.endrow = None
        self.startcol = self.endcol = None
        self.allrows = False       #for selected all rows without setting multiplerowlist
        self.multiplerowlist=[]
        self.multiplecollist=[]
        self.col_positions=[]       #record current column grid positions
        self.mode = 'normal'
        self.read_only = read_only
        self.filtered = False

        self.loadPrefs()
        #set any options passed in kwargs to overwrite defaults and prefs
        for key in kwargs:
            self.__dict__[key] = kwargs[key]

        if data is not None:
            self.model = TableModel()
            self.model.importDict(data)
        elif model is not None:
            self.model = model
        else:
            self.model = TableModel(rows=rows,columns=cols)

        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()
        self.tablewidth = (self.cellwidth)*self.cols
        self.do_bindings()
        #initial sort order
        self.model.setSortOrder()

        #column specific actions, define for every column type in the model
        #when you add a column type you should edit this dict
        self.columnactions = {'text' : {"Edit":  'drawCellEntry' },
                              'number' : {"Edit": 'drawCellEntry' }}
        self.setFontSize()
        return

    def set_defaults(self):
        """Set default settings"""

        self.cellwidth=150
        self.maxcellwidth=200
        self.rowheight=20
        self.horizlines=1
        self.vertlines=1
        self.alternaterows=0
        self.autoresizecols = 0
        self.inset=2
        self.x_start=0
        self.y_start=1
        self.linewidth=1.0
        self.rowheaderwidth=40
        self.showkeynamesinheader=False
        self.thefont = ('Arial',12)
        self.cellbackgr = '#F7F7FA'
        self.entrybackgr = 'white'
        self.grid_color = '#ABB1AD'
        self.selectedcolor = 'yellow'
        self.rowselectedcolor = '#CCCCFF'
        self.multipleselectioncolor = '#ECD672'
        return

    def setFontSize(self):
        """Set font size to match font, we need to get rid of fontsize as
            a separate variable?"""

        if hasattr(self, 'thefont') and type(self.thefont) is tuple:
            self.fontsize = self.thefont[1]
        return

    def mouse_wheel(self, event):
        """Handle mouse wheel scroll for windows"""

        if event.num == 5 or event.delta == -120:
            event.widget.yview_scroll(1, UNITS)
            self.tablerowheader.yview_scroll(1, UNITS)
        if event.num == 4 or event.delta == 120:
            if self.canvasy(0) < 0:
                return
            event.widget.yview_scroll(-1, UNITS)
            self.tablerowheader.yview_scroll(-1, UNITS)
        self.redrawVisible()
        return

    def do_bindings(self):
        """Bind keys and mouse clicks, this can be overriden"""
        self.bind("<Button-1>",self.handle_left_click)
        self.bind("<Double-Button-1>",self.handle_double_click)
        self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
        self.bind("<Shift-Button-1>", self.handle_left_shift_click)

        self.bind("<ButtonRelease-1>", self.handle_left_release)
        if self.ostyp=='mac':
            #For mac we bind Shift, left-click to right click
            self.bind("<Button-2>", self.handle_right_click)
            self.bind('<Shift-Button-1>',self.handle_right_click)
        else:
            self.bind("<Button-3>", self.handle_right_click)

        self.bind('<B1-Motion>', self.handle_mouse_drag)
        self.bind('<Motion>', self.handle_motion)

        self.bind_all("<Control-x>", self.deleteRow)
        self.bind_all("<Control-n>", self.addRow)
        self.bind_all("<Delete>", self.clearData)
        self.bind_all("<Control-v>", self.paste)

        #if not hasattr(self,'parentapp'):
        #    self.parentapp = self.parentframe

        self.parentframe.master.bind_all("<Right>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Left>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Up>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Down>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<KP_8>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Return>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Tab>", self.handle_arrow_keys)
        #if 'windows' in self.platform:
        self.bind("<MouseWheel>", self.mouse_wheel)
        self.bind('<Button-4>', self.mouse_wheel)
        self.bind('<Button-5>', self.mouse_wheel)
        self.focus_set()
        return

    def getModel(self):
        """Get the current table model"""
        return self.model

    def setModel(self, model):
        """Set a new model - requires redraw to reflect changes"""
        self.model = model
        return

    def createfromDict(self, data):
        """Attempt to create a new model/table from a dict"""

        try:
            namefield=self.namefield
        except:
            namefield=data.keys()[0]
        self.model = TableModel()
        self.model.importDict(data, namefield=namefield)
        self.model.setSortOrder(0,reverse=self.reverseorder)
        return

    def createTableFrame(self, callback=None):
        self.show(callback)
        return

    def show(self, callback=None):
        """Adds column header and scrollbars and combines them with
           the current table adding all to the master frame provided in constructor.
           Table is then redrawn."""

        #Add the table and header to the frame
        self.tablerowheader = RowHeader(self.parentframe, self, width=self.rowheaderwidth)
        self.tablecolheader = ColumnHeader(self.parentframe, self)
        self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews)
        self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0)
        self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews)
        self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news')
        self['xscrollcommand'] = self.Xscrollbar.set
        self['yscrollcommand'] = self.Yscrollbar.set
        self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set
        self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set
        self.parentframe.rowconfigure(1,weight=1)
        self.parentframe.columnconfigure(1,weight=1)

        self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0)
        self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0)
        self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0)

        self.adjustColumnWidths()
        self.redrawTable(callback=callback)
        self.parentframe.bind("<Configure>", self.redrawVisible)
        self.tablecolheader.xview("moveto", 0)
        self.xview("moveto", 0)
        return

    def getVisibleRegion(self):
        """Get visible region of table to display"""

        x1, y1 = self.canvasx(0), self.canvasy(0)
        #w, h = self.winfo_width(), self.winfo_height()
        w,h= self.master.winfo_width(), self.master.winfo_height()
        if w <= 1.0 or h <= 1.0:
            w, h = self.master.winfo_width(), self.master.winfo_height()
        x2, y2 = self.canvasx(w), self.canvasy(h)
        return x1, y1, x2, y2

    def getRowPosition(self, y):
        """Get current row from canvas position"""

        h = self.rowheight
        y_start = self.y_start
        row = (int(y)-y_start)/h
        if row < 0:
            return 0
        if row > self.rows:
            row = self.rows
        return row

    def getColPosition(self, x):
        """Get current col from canvas position"""

        x_start = self.x_start
        w = self.cellwidth
        i=0
        col=0
        for c in self.col_positions:
            col = i
            if c+w>=x:
                break
            i+=1
        return col

    def getVisibleRows(self, y1, y2):
        """Get the visible row range"""

        start = int(self.getRowPosition(y1))
        end = int(self.getRowPosition(y2))+1
        if end > self.rows:
            end = self.rows
        return start, end

    def getVisibleCols(self, x1, x2):
        """Get the visible column range"""

        start = self.getColPosition(x1)
        end = self.getColPosition(x2)+1
        if end > self.cols:
            end = self.cols
        return start, end

    def redrawVisible(self, event=None, callback=None):
        """Redraw the visible portion of the canvas"""

        model = self.model
        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()

        self.tablewidth = (self.cellwidth) * self.cols
        self.configure(bg=self.cellbackgr)
        self.setColPositions()

        #are we drawing a filtered subset of the recs?
        if self.filtered == True and self.model.filteredrecs != None:
            self.rows = len(self.model.filteredrecs)
            self.delete('colrect')

        self.rowrange = range(0,self.rows)
        self.configure(scrollregion=(0,0, self.tablewidth+self.x_start,
                self.rowheight*self.rows+10))

        x1, y1, x2, y2 = self.getVisibleRegion()
        startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2)
        self.visiblerows = range(startvisiblerow, endvisiblerow)
        startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2)
        self.visiblecols = range(startvisiblecol, endvisiblecol)

        if self.cols == 0 or self.rows == 0:
            self.delete('entry')
            self.delete('rowrect')
            self.delete('currentrect')
            self.delete('gridline','text')
            self.tablerowheader.redraw()
            return

        self.drawGrid(startvisiblerow, endvisiblerow)
        align = self.align
        self.delete('fillrect')
        for row in self.visiblerows:
            if callback != None:
                callback()
            for col in self.visiblecols:
                colname = model.getColumnName(col)
                bgcolor = model.getColorAt(row,col, 'bg')
                fgcolor = model.getColorAt(row,col, 'fg')
                text = model.getValueAt(row,col)
                self.drawText(row, col, text, fgcolor, align)
                if bgcolor != None:
                    self.drawRect(row,col, color=bgcolor)

        #self.drawSelectedCol()
        self.tablecolheader.redraw()
        self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader)
        #self.setSelectedRow(self.currentrow)
        self.drawSelectedRow()
        self.drawSelectedRect(self.currentrow, self.currentcol)
        #print self.multiplerowlist

        if len(self.multiplerowlist)>1:
            self.tablerowheader.drawSelectedRows(self.multiplerowlist)
            self.drawMultipleRows(self.multiplerowlist)
            self.drawMultipleCells()
        return

    def redrawTable(self, event=None, callback=None):
        self.redrawVisible(event, callback)
        return

    def redraw(self, event=None, callback=None):
        self.redrawVisible(event, callback)
        return

    def redrawCell(self, row=None, col=None, recname=None, colname=None):
        """Redraw a specific cell only"""

        if row == None and recname != None:
            row = self.model.getRecordIndex(recname)
        if col == None and colname != None:
            col = self.model.getColumnIndex(colname)
        bgcolor = self.model.getColorAt(row,col, 'bg')
        fgcolor = self.model.getColorAt(row,col, 'fg')
        text = self.model.getValueAt(row,col)
        self.drawText(row, col, text, fgcolor)
        if bgcolor != None:
            self.drawRect(row,col, color=bgcolor)
        return

    def adjustColumnWidths(self):
        """Optimally adjust col widths to accomodate the longest entry
            in each column - usually only called  on first redraw"""

        #self.cols = self.model.getColumnCount()
        try:
            fontsize = self.thefont[1]
        except:
            fontsize = self.fontsize
        scale = 8.5 * float(fontsize)/12
        for col in range(self.cols):
            colname = self.model.getColumnName(col)
            if colname in self.model.columnwidths:
                w = self.model.columnwidths[colname]
            else:
                w = self.cellwidth
            maxlen = self.model.getlongestEntry(col)
            size = maxlen * scale
            if size < w:
                continue
            #print col, size, self.cellwidth
            if size >= self.maxcellwidth:
                size = self.maxcellwidth
            self.model.columnwidths[colname] = size + float(fontsize)/12*6
        return

    def autoResizeColumns(self):
        """Automatically set nice column widths and draw"""

        self.adjustColumnWidths()
        self.redrawTable()
        return

    def setColPositions(self):
        """Determine current column grid positions"""

        self.col_positions=[]
        w=self.cellwidth
        x_pos=self.x_start
        self.col_positions.append(x_pos)
        for col in range(self.cols):
            colname=self.model.getColumnName(col)
            if colname in self.model.columnwidths:
                x_pos=x_pos+self.model.columnwidths[colname]
            else:
                x_pos=x_pos+w
            self.col_positions.append(x_pos)
        self.tablewidth = self.col_positions[len(self.col_positions)-1]
        return

    def sortTable(self, columnIndex=0, columnName=None, reverse=0):
        """Set up sort order dict based on currently selected field"""

        self.model.setSortOrder(columnIndex, columnName, reverse)
        self.redrawTable()
        return

    def set_xviews(self,*args):
        """Set the xview of table and col header"""

        self.xview(*args)
        self.tablecolheader.xview(*args)
        self.redrawVisible()
        return

    def set_yviews(self,*args):
        """Set the xview of table and row header"""

        self.yview(*args)
        self.tablerowheader.yview(*args)
        self.redrawVisible()
        return

    def addRow(self, key=None, **kwargs):
        """Add new row"""

        key = self.model.addRow(key, **kwargs)
        self.redrawTable()
        self.setSelectedRow(self.model.getRecordIndex(key))
        return

    def addRows(self, num=None):
        """Add new rows"""

        if num == None:
            num = simpledialog.askinteger("Now many rows?",
                                            "Number of rows:",initialvalue=1,
                                             parent=self.parentframe)
        if not num:
            return
        keys = self.model.autoAddRows(num)
        self.redrawTable()
        self.setSelectedRow(self.model.getRecordIndex(keys[0]))
        return

    def addColumn(self, newname=None):
        """Add a new column"""

        if newname == None:

            coltypes = self.getModel().getDefaultTypes()
            d = MultipleValDialog(title='New Column',
                                    initialvalues=(coltypes, ''),
                                    labels=('Column Type','Name'),
                                    types=('list','string'),
                                    parent=self.parentframe)
            if d.result == None:
                return
            else:
                coltype = d.results[0]
                newname = d.results[1]

        if newname != None:
            if newname in self.getModel().columnNames:
                messagebox.showwarning("Name exists",
                                         "Name already exists!",
                                         parent=self.parentframe)
            else:
                self.model.addColumn(newname)
                self.parentframe.configure(width=self.width)
                self.redrawTable()
        return

    def deleteRow(self):
        """Delete a row"""

        if len(self.multiplerowlist)>1:
            n = messagebox.askyesno("Delete",
                                      "Delete Selected Records?",
                                      parent=self.parentframe)
            if n == True:
                rows = self.multiplerowlist
                self.model.deleteRows(rows)
                self.clearSelected()
                self.setSelectedRow(0)
                self.redrawTable()
        else:
            n = messagebox.askyesno("Delete",
                                      "Delete This Record?",
                                      parent=self.parentframe)
            if n:
                row = self.getSelectedRow()
                self.model.deleteRow(row)
                self.setSelectedRow(row-1)
                self.clearSelected()
                self.redrawTable()
        return

    def deleteColumn(self):
        """Delete currently selected column"""

        n =  messagebox.askyesno("Delete",
                                   "Delete This Column?",
                                   parent=self.parentframe)
        if n:
            col = self.getSelectedColumn()
            self.model.deleteColumn(col)
            self.currentcol = self.currentcol - 1
            self.redrawTable()
        return

    def deleteCells(self, rows, cols):
        """Clear the cell contents"""

        n =  messagebox.askyesno("Clear Confirm",
                                   "Clear this data?",
                                   parent=self.parentframe)
        if not n:
            return
        for col in cols:
            for row in rows:
                #absrow = self.get_AbsoluteRow(row)
                self.model.deleteCellRecord(row, col)
                self.redrawCell(row,col)
        return

    def clearData(self, evt=None):
        """Delete cells from gui event"""

        rows = self.multiplerowlist
        cols = self.multiplecollist
        self.deleteCells(rows, cols)
        return

    def autoAddColumns(self, numcols=None):
        """Automatically add x number of cols"""

        if numcols == None:
            numcols = simpledialog.askinteger("Auto add rows.",
                                                "How many empty columns?",
                                                parent=self.parentframe)
        self.model.auto_AddColumns(numcols)
        self.parentframe.configure(width=self.width)
        self.redrawTable()
        return

    def getRecordInfo(self, row):
        """Show the record for this row"""

        model = self.model
        #We need a custom dialog for allowing field entries here
        #absrow = self.get_AbsoluteRow(row)
        d = RecordViewDialog(title="Record Details",
                                  parent=self.parentframe, table=self, row=row)
        return

    def findValue(self, searchstring=None, findagain=None):
        """Return the row/col for the input value"""

        if searchstring == None:
            searchstring = simpledialog.askstring("Search table.",
                                               "Enter search value",
                                               parent=self.parentframe)
        found=0
        if findagain == None or not hasattr(self,'foundlist'):
            self.foundlist=[]
        if self.model!=None:
            for row in range(self.rows):
                for col in range(self.cols):
                    text = str(self.model.getValueAt(row,col))
                    if text=='' or text==None:
                        continue
                    cell=row,col
                    if findagain == 1 and cell in self.foundlist:
                        continue
                    if text.lower().find(searchstring.lower())!=-1:
                        print ('found in',row,col)
                        found=1
                        #highlight cell
                        self.delete('searchrect')
                        self.drawRect(row, col, color='red', tag='searchrect', delete=0)
                        self.lift('searchrect')
                        self.lift('celltext'+str(col)+'_'+str(row))
                        #add row/col to foundlist
                        self.foundlist.append(cell)
                        #need to scroll to centre the cell here..
                        x,y = self.getCanvasPos(row, col)
                        self.xview('moveto', x)
                        self.yview('moveto', y)
                        self.tablecolheader.xview('moveto', x)
                        self.tablerowheader.yview('moveto', y)
                        return row, col
        if found==0:
            self.delete('searchrect')
            print ('nothing found')
            return None

    def showAll(self):
        self.model.filteredrecs = None
        self.filtered = False
        self.redrawTable()
        return

    def doFilter(self, event=None):
        """Filter the table display by some column values.
        We simply pass the model search function to the the filtering
        class and that handles everything else.
        See filtering frame class for how searching is done.
        """
        if self.model==None:
            return
        names = self.filterframe.doFiltering(searchfunc=self.model.filterBy)
        #create a list of filtered recs
        self.model.filteredrecs = names
        self.filtered = True
        self.redrawTable()
        return

    def createFilteringBar(self, parent=None, fields=None):
        """Add a filter frame"""

        if parent == None:
            parent = Toplevel()
            parent.title('Filter Records')
            x,y,w,h = self.getGeometry(self.master)
            parent.geometry('+%s+%s' %(x,y+h))
        if fields == None:
            fields = self.model.columnNames
        from .Filtering import FilterFrame
        self.filterframe = FilterFrame(parent, fields,
                                       self.doFilter, self.closeFilterFrame)
        self.filterframe.pack()
        return parent

    def showFilteringBar(self):
        if not hasattr(self, 'filterwin') or self.filterwin == None:
            self.filterwin = self.createFilteringBar()
            self.filterwin.protocol("WM_DELETE_WINDOW", self.closeFilterFrame)
        else:
            self.filterwin.lift()
        return

    def closeFilterFrame(self):
        """Callback for closing filter frame"""
        self.filterwin.destroy()
        self.filterwin = None
        self.showAll()
        return

    def resizeColumn(self, col, width):
        """Resize a column by dragging"""

        #print 'resizing column', col
        #recalculate all col positions..
        colname=self.model.getColumnName(col)
        self.model.columnwidths[colname]=width
        self.setColPositions()
        self.redrawTable()
        self.drawSelectedCol(self.currentcol)
        return

    def get_currentRecord(self):
        """Get the currently selected record"""

        rec = self.model.getRecordAtRow(self.currentrow)
        return rec

    def get_currentColName(self):
        """Get the currently selected record name"""

        colname = self.mo(self.currentcol)
        return colname

    def get_currentRecordName(self):
        """Get the currently selected record name"""

        recname = self.model.getRecName(self.currentrow)
        return recname

    def get_selectedRecordNames(self):
        """Get a list of the current multiple selection, if any"""

        recnames=[]
        for row in self.multiplerowlist:
            recnames.append(self.model.getRecName(row))
        return recnames

    def get_currentRecCol(self):
        """Get the clicked rec and col names as a tuple"""

        recname = self.get_currentRecordName()
        colname = self.get_currentColName()
        return (recname, colname)

    def get_row_clicked(self, event):
        """get row where event on canvas occurs"""

        h=self.rowheight
        #get coord on canvas, not window, need this if scrolling
        y = int(self.canvasy(event.y))
        y_start=self.y_start
        rowc = int((int(y)-y_start)/h)
        #rowc = math.floor(rowc)
        #print 'event.y',event.y, 'y',y
        #print 'rowclicked', rowc
        return rowc

    def get_col_clicked(self,event):
        """get col where event on canvas occurs"""

        w=self.cellwidth
        x = int(self.canvasx(event.x))
        x_start=self.x_start
        #print self.col_positions
        for colpos in self.col_positions:
            try:
                nextpos=self.col_positions[self.col_positions.index(colpos)+1]
            except:
                nextpos=self.tablewidth
            if x > colpos and x <= nextpos:
                #print 'x=', x, 'colpos', colpos, self.col_positions.index(colpos)
                return self.col_positions.index(colpos)
            else:
                #print None
                pass
        #return colc

    def setSelectedRow(self, row):
        """Set currently selected row and reset multiple row list"""

        self.currentrow = row
        self.multiplerowlist = []
        self.multiplerowlist.append(row)
        return

    def setSelectedCol(self, col):
        """Set currently selected column"""

        self.currentcol = col
        self.multiplecollist = []
        self.multiplecollist.append(col)
        return

    def setSelectedCells(self, startrow, endrow, startcol, endcol):
        """Set a block of cells selected"""

        self.currentrow = startrow
        self.currentcol = startcol
        if startrow < 0 or startcol < 0:
            return
        if endrow > self.rows or endcol > self.cols:
            return
        for r in range(startrow, endrow):
            self.multiplerowlist.append(r)
        for c in range(startcol, endcol):
            self.multiplecollist.append(c)
        return

    def getSelectedRow(self):
        """Get currently selected row"""
        return self.currentrow

    def getSelectedColumn(self):
        """Get currently selected column"""
        return self.currentcol

    def select_All(self):
        """Select all rows and cells"""

        self.startrow = 0
        self.endrow = self.rows
        self.multiplerowlist = range(self.startrow,self.endrow)
        self.drawMultipleRows(self.multiplerowlist)
        self.startcol = 0
        self.endcol = self.cols
        self.multiplecollist = range(self.startcol, self.endcol)
        self.drawMultipleCells()
        return

    def getCellCoords(self, row, col):
        """Get x-y coordinates to drawing a cell in a given row/col"""
        colname=self.model.getColumnName(col)
        if colname in self.model.columnwidths:
            w=self.model.columnwidths[colname]
        else:
            w=self.cellwidth
        h=self.rowheight
        x_start=self.x_start
        y_start=self.y_start

        #get nearest rect co-ords for that row/col
        #x1=x_start+w*col
        x1=self.col_positions[col]
        y1=y_start+h*row
        x2=x1+w
        y2=y1+h
        return x1,y1,x2,y2

    def getCanvasPos(self, row, col):
        """Get the cell x-y coords as a fraction of canvas size"""
        if self.rows==0:
            return None, None
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        cx=float(x1)/self.tablewidth
        cy=float(y1)/(self.rows*self.rowheight)
        return cx, cy

    def isInsideTable(self,x,y):
        """Returns true if x-y coord is inside table bounds"""
        if self.x_start < x < self.tablewidth and self.y_start < y < self.rows*self.rowheight:
            return 1
        else:
            return 0
        return answer

    def setRowHeight(self, h):
        """Set the row height"""
        self.rowheight = h
        return

    def clearSelected(self):
        self.delete('rect')
        self.delete('entry')
        self.delete('tooltip')
        self.delete('searchrect')
        self.delete('colrect')
        self.delete('multicellrect')

        #self.delete('formulabox')
        return

    def gotoprevRow(self):
        """Programmatically set previous row - eg. for button events"""
        self.clearSelected()
        current = self.getSelectedRow()
        self.setSelectedRow(current-1)
        self.startrow = current-1
        self.endrow = current-1
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(self.currentrow)
        self.drawSelectedRect(self.currentrow, self.currentcol)
        self.drawSelectedRow()
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def gotonextRow(self):
        """Programmatically set next row - eg. for button events"""
        self.clearSelected()
        current = self.getSelectedRow()
        self.setSelectedRow(current+1)
        self.startrow = current+1
        self.endrow = current+1
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(self.currentrow)
        self.drawSelectedRect(self.currentrow, self.currentcol)
        self.drawSelectedRow()
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def handle_left_click(self, event):
        """Respond to a single press"""

        #which row and column is the click inside?
        self.clearSelected()
        self.allrows = False
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        self.focus_set()
        if self.mode == 'formula':
            self.handleFormulaClick(rowclicked, colclicked)
            return
        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        #ensure popup menus are removed if present
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        if hasattr(self.tablecolheader, 'rightmenu'):
            self.tablecolheader.rightmenu.destroy()

        self.startrow = rowclicked
        self.endrow = rowclicked
        self.startcol = colclicked
        self.endcol = colclicked
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(rowclicked)
        if rowclicked is None or colclicked is None:
            return
        if self.read_only is True:    
            return
        if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
            self.setSelectedRow(rowclicked)
            self.setSelectedCol(colclicked)
            self.drawSelectedRect(self.currentrow, self.currentcol)
            self.drawSelectedRow()
            self.tablerowheader.drawSelectedRows(rowclicked)
            coltype = self.model.getColumnType(colclicked)
            if coltype == 'text' or coltype == 'number':
                self.drawCellEntry(rowclicked, colclicked)
        return

    def handle_left_release(self,event):
        self.endrow = self.get_row_clicked(event)
        return

    def handle_left_ctrl_click(self, event):
        """Handle ctrl clicks for multiple row selections"""
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
            if rowclicked not in self.multiplerowlist:
                self.multiplerowlist.append(rowclicked)
            else:
                self.multiplerowlist.remove(rowclicked)
            self.drawMultipleRows(self.multiplerowlist)
            if colclicked not in self.multiplecollist:
                self.multiplecollist.append(colclicked)
            #print self.multiplecollist
            self.drawMultipleCells()
        return

    def handle_left_shift_click(self, event):
        """Handle shift click, for selecting multiple rows"""
        #Has same effect as mouse drag, so just use same method
        self.handle_mouse_drag(event)
        return

    def handle_mouse_drag(self, event):
        """Handle mouse moved with button held down, multiple selections"""

        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        rowover = self.get_row_clicked(event)
        colover = self.get_col_clicked(event)
        if colover == None or rowover == None:
            return

        if rowover >= self.rows or self.startrow > self.rows:
            return
        else:
            self.endrow = rowover
        #do columns
        if colover > self.cols or self.startcol > self.cols:
            return
        else:
            self.endcol = colover
            if self.startcol is None or self.endcol is None:
                return
            if self.endcol < self.startcol:
                self.multiplecollist = range(self.endcol, self.startcol+1)
            else:
                self.multiplecollist = range(self.startcol, self.endcol+1)
            #print self.multiplecollist
        #draw the selected rows
        if self.endrow != self.startrow:
            if self.endrow < self.startrow:
                self.multiplerowlist = range(self.endrow, self.startrow+1)
            else:
                self.multiplerowlist = range(self.startrow, self.endrow+1)
            self.drawMultipleRows(self.multiplerowlist)
            self.tablerowheader.drawSelectedRows(self.multiplerowlist)
            #draw selected cells outline using row and col lists
            #print self.multiplerowlist
            self.drawMultipleCells()
        else:
            self.multiplerowlist = []
            self.multiplerowlist.append(self.currentrow)
            if len(self.multiplecollist) >= 1:
                self.drawMultipleCells()
            self.delete('multiplesel')
        #print self.multiplerowlist
        return

    def handle_arrow_keys(self, event):
        """Handle arrow keys press"""
        #print event.keysym

        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        x,y = self.getCanvasPos(self.currentrow, 0)
        if x == None:
            return

        if event.keysym == 'Up':
            if self.currentrow == 0:
                return
            else:
                self.currentrow  = self.currentrow -1
        elif event.keysym == 'Down':
            if self.currentrow >= self.rows-1:
                return
            else:
                self.currentrow  = self.currentrow +1
        elif event.keysym == 'Right' or event.keysym == 'Tab':
            if self.currentcol >= self.cols-1:
                if self.currentrow < self.rows-1:
                    self.currentcol = 0
                    self.currentrow  = self.currentrow +1
                else:
                    return
            else:
                self.currentcol  = self.currentcol +1
        elif event.keysym == 'Left':
            if self.currentcol == 0:
                if self.currentrow == 0:
                    return
                else:
                    self.currentcol = self.cols-1
                    self.currentrow = self.currentrow - 1
            else:
                self.currentcol  = self.currentcol -1
        self.drawSelectedRect(self.currentrow, self.currentcol)
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.delete('entry')
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def handle_double_click(self, event):
        """Do double click stuff. Selected row/cols will already have
           been set with single click binding"""

        '''row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        model=self.getModel()
        cellvalue = model.getCellRecord(row, col)
        if Formula.isFormula(cellvalue):
            self.formula_Dialog(row, col, cellvalue)'''

        return

    def handle_right_click(self, event):
        """respond to a right click"""

        if self.read_only is True:
            return
        self.delete('tooltip')
        self.tablerowheader.clearSelected()
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        if colclicked == None:
            self.rightmenu = self.popupMenu(event, outside=1)
            return

        if (rowclicked in self.multiplerowlist or self.allrows == True) and colclicked in self.multiplecollist:
            self.rightmenu = self.popupMenu(event, rows=self.multiplerowlist, cols=self.multiplecollist)
        else:
            if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
                self.clearSelected()
                self.allrows = False
                self.setSelectedRow(rowclicked)
                self.setSelectedCol(colclicked)
                self.drawSelectedRect(self.currentrow, self.currentcol)
                self.drawSelectedRow()
            if self.isInsideTable(event.x,event.y) == 1:
                self.rightmenu = self.popupMenu(event,rows=self.multiplerowlist, cols=self.multiplecollist)
            else:
                self.rightmenu = self.popupMenu(event, outside=1)
        return

    def handle_motion(self, event):
        """Handle mouse motion on table"""

        self.delete('tooltip')
        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        if row == None or col == None:
            return
        if 0 <= row < self.rows and 0 <= col < self.cols:
            self.drawTooltip(row, col)

        return

    def gotonextCell(self, event):
        """Move highlighted cell to next cell in row or a new col"""
        #print 'next'
        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        self.currentcol=self.currentcol+1
        if self.currentcol >= self.cols-1:
            self.currentrow  = self.currentrow +1
            self.currentcol = self.currentcol+1
        self.drawSelectedRect(self.currentrow, self.currentcol)
        return

    def movetoSelectedRow(self, row=None, recname=None):
        """Move to selected row, updating table"""
        row=self.model.getRecordIndex(recname)
        self.setSelectedRow(row)
        self.drawSelectedRow()
        x,y = self.getCanvasPos(row, 0)
        self.yview('moveto', y-0.01)
        self.tablecolheader.yview('moveto', y)
        return

    def handleFormulaClick(self, row, col):
        """Do a dialog for cell formula entry"""

        model = self.getModel()
        cell = list(model.getRecColNames(row, col))
        #absrow = self.get_AbsoluteRow(row)
        self.formulaText.insert(END, str(cell))
        self.formulaText.focus_set()
        self.drawSelectedRect(row, col, color='red')
        return

    def formula_Dialog(self, row, col, currformula=None):
        """Formula dialog"""
        self.mode = 'formula'
        print (self.mode)
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=300
        h=h=self.rowheight * 3
        def close():
            if hasattr(self,'formulaWin'):
                self.delete('formulabox')
            self.mode = 'normal'
        def calculate():
            #get text area contents and do formula
            f = self.formulaText.get(1.0, END)
            f = f.strip('\n')
            self.model.setFormulaAt(f,row,col)
            value = self.model.doFormula(f)
            color = self.model.getColorAt(row,col,'fg')
            self.drawText(row, col, value, color)
            close()
            self.mode = 'normal'
            return
        def clear():
            self.formulaText.delete(1.0, END)

        self.formulaFrame = Frame(width=w,height=h,bd=3)
        self.formulaText = Text(self.formulaFrame, width=30, height=8, bg='white',relief=GROOVE)
        self.formulaText.pack(side=LEFT,padx=2,pady=2)
        if currformula != None:
            self.formulaText.insert(END, Formula.getFormula(currformula))
        cancelbutton=Button(self.formulaFrame, text='Cancel',
                            bg='#99ccff',command=close)
        cancelbutton.pack(fill=BOTH,padx=2,pady=2)
        donebutton=Button(self.formulaFrame, text='Done',
                          bg='#99ccff',command=calculate)
        donebutton.pack(fill=BOTH,padx=2,pady=2)
        '''clrbutton=Button(self.formulaFrame, text='Clear',
                           bg='#99ccff',command=clear)
        clrbutton.pack(fill=BOTH,padx=2,pady=2) '''
        #add to canvas
        self.formulaWin = self.create_window(x1+self.inset,y1+self.inset,
                                width=w,height=h,
                                window=self.formulaFrame,anchor='nw',
                                tag='formulabox')
        self.formulaText.focus_set()
        return

    def convertFormulae(self, rows, cols=None):
        """Convert the formulas in the cells to their result values"""

        if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
            return None

        print (rows, cols)
        if cols == None:
            cols = range(self.cols)
        for r in rows:
            #absr=self.get_AbsoluteRow(r)
            for c in cols:
                val = self.model.getValueAt(r,c)
                self.model.setValueAt(val, r, c)
        return

    def paste(self, event=None):
        """Copy from clipboard"""

        print (self.parentframe.clipboard_get())
        return

    def copyCell(self, rows, cols=None):
        """Copy cell contents to a temp internal clipboard"""

        row = rows[0]; col = cols[0]
        #absrow = self.get_AbsoluteRow(row)
        self.clipboard = copy.deepcopy(self.model.getCellRecord(row, col))
        return

    def pasteCell(self, rows, cols=None):
        """Paste cell from internal clipboard"""

        row = rows[0]; col = cols[0]
        #absrow = self.get_AbsoluteRow(row)
        val = self.clipboard
        self.model.setValueAt(val, row, col)
        self.redrawTable()
        return

    def copyColumns(self):
        """Copy current selected cols"""

        M = self.model
        coldata = {}
        for col in self.multiplecollist:
            name = M.columnNames[col]
            coldata[name] = M.getColumnData(columnName=name)
        return coldata

    def pasteColumns(self, coldata):
        """Paste new cols, overwrites existing names"""

        M = self.model
        for name in coldata:
            if name not in M.columnNames:
                M.addColumn(name)
            for r in range(len(coldata[name])):
                val = coldata[name][r]
                col = M.columnNames.index(name)
                if r >= self.rows:
                    break
                M.setValueAt(val, r, col)
        self.redrawTable()
        return coldata

    # --- Some cell specific actions here ---

    def setcellColor(self, rows, cols=None, newColor=None, key=None, redraw=True):
        """Set the cell color for one or more cells and save it in the model color"""

        model = self.getModel()
        if newColor == None:
            ctuple, newColor = tkColorChooser.askcolor(title='pick a color')
            if newColor == None:
                return

        if type(rows) is int:
            x=rows
            rows=[]
            rows.append(x)
        if self.allrows == True:
            #we use all rows if the whole column has been selected
            rows = range(0,self.rows)
        if cols == None:
            cols = range(self.cols)
        for col in cols:
            for row in rows:
                #absrow = self.get_AbsoluteRow(row)
                model.setColorAt(row, col, color=newColor, key=key)
                #setcolor(absrow, col)
        if redraw == True:
            self.redrawTable()
        return

    def popupMenu(self, event, rows=None, cols=None, outside=None):
        """Add left and right click behaviour for canvas, should not have to override
            this function, it will take its values from defined dicts in constructor"""

        defaultactions = {"Set Fill Color" : lambda : self.setcellColor(rows,cols,key='bg'),
                        "Set Text Color" : lambda : self.setcellColor(rows,cols,key='fg'),
                        "Copy" : lambda : self.copyCell(rows, cols),
                        "Paste" : lambda : self.pasteCell(rows, cols),
                        "Fill Down" : lambda : self.fillDown(rows, cols),
                        "Fill Right" : lambda : self.fillAcross(cols, rows),
                        "Add Row(s)" : lambda : self.addRows(),
                        "Delete Row(s)" : lambda : self.deleteRow(),
                        "View Record" : lambda : self.getRecordInfo(row),
                        "Clear Data" : lambda : self.deleteCells(rows, cols),
                        "Select All" : self.select_All,
                        "Auto Fit Columns" : self.autoResizeColumns,
                        "Filter Records" : self.showFilteringBar,
                        "New": self.new,
                        "Load": self.load,
                        "Save": self.save,
                        "Import text":self.importTable,
                        "Export csv": self.exportTable,
                        "Plot Selected" : self.plotSelected,
                        "Plot Options" : self.plotSetup,
                        "Export Table" : self.exportTable,
                        "Preferences" : self.showtablePrefs,
                        "Formulae->Value" : lambda : self.convertFormulae(rows, cols)}

        main = ["Set Fill Color","Set Text Color","Copy", "Paste", "Fill Down","Fill Right",
                "Clear Data"]
        general = ["Select All", "Add Row(s)" , "Delete Row(s)", "Auto Fit Columns", "Filter Records", "Preferences"]
        filecommands = ['New','Load','Save','Import text','Export csv']
        plotcommands = ['Plot Selected','Plot Options']

        def createSubMenu(parent, label, commands):
            menu = Menu(parent, tearoff = 0)
            popupmenu.add_cascade(label=label,menu=menu)
            for action in commands:
                menu.add_command(label=action, command=defaultactions[action])
            return menu

        def add_commands(fieldtype):
            """Add commands to popup menu for column type and specific cell"""
            functions = self.columnactions[fieldtype]
            for f in functions.keys():
                func = getattr(self, functions[f])
                popupmenu.add_command(label=f, command= lambda : func(row,col))
            return

        popupmenu = Menu(self, tearoff = 0)
        def popupFocusOut(event):
            popupmenu.unpost()

        if outside == None:
            #if outside table, just show general items
            row = self.get_row_clicked(event)
            col = self.get_col_clicked(event)
            coltype = self.model.getColumnType(col)
            def add_defaultcommands():
                """now add general actions for all cells"""
                for action in main:
                    if action == 'Fill Down' and (rows == None or len(rows) <= 1):
                        continue
                    if action == 'Fill Right' and (cols == None or len(cols) <= 1):
                        continue
                    else:
                        popupmenu.add_command(label=action, command=defaultactions[action])
                return

            if coltype in self.columnactions:
                add_commands(coltype)
            add_defaultcommands()

        for action in general:
            popupmenu.add_command(label=action, command=defaultactions[action])

        popupmenu.add_separator()
        createSubMenu(popupmenu, 'File', filecommands)
        createSubMenu(popupmenu, 'Plot', plotcommands)
        popupmenu.bind("<FocusOut>", popupFocusOut)
        popupmenu.focus_set()
        popupmenu.post(event.x_root, event.y_root)
        return popupmenu

    # --- spreadsheet type functions ---

    def fillDown(self, rowlist, collist):
        """Fill down a column, or multiple columns"""
        model = self.model
        #absrow  = self.get_AbsoluteRow(rowlist[0])
        #remove first element as we don't want to overwrite it
        rowlist.remove(rowlist[0])

        #if this is a formula, we have to treat it specially
        for col in collist:
            val = self.model.getCellRecord(row, col)
            f=val #formula to copy
            i=1
            for r in rowlist:
                #absr = self.get_AbsoluteRow(r)
                if Formula.isFormula(f):
                    newval = model.copyFormula(f, r, col, offset=i)
                    model.setFormulaAt(newval, r, col)
                else:
                    model.setValueAt(val, r, col)
                #print 'setting', val, 'at row', r
                i+=1

        self.redrawTable()
        return

    def fillAcross(self, collist, rowlist):
        """Fill across a row, or multiple rows"""
        model = self.model
        #row = self.currentrow
        #absrow  = self.get_AbsoluteRow(collist[0])
        frstcol = collist[0]
        collist.remove(frstcol)

        for row in rowlist:
            #absr = self.get_AbsoluteRow(row)
            val = self.model.getCellRecord(absr, frstcol)
            f=val     #formula to copy
            i=1
            for c in collist:
                if Formula.isFormula(f):
                    newval = model.copyFormula(f, r, c, offset=i, dim='x')
                    model.setFormulaAt(newval, r, c)
                else:
                    model.setValueAt(val, r, c)
                i+=1
        self.redrawTable()
        return

    def getSelectionValues(self):
        """Get values for current multiple cell selection"""
        if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
            return None
        rows = self.multiplerowlist
        cols = self.multiplecollist
        model = self.model
        if len(rows)<1 or len(cols)<1:
            return None
        #if only one row selected we plot whole col
        if len(rows) == 1:
            rows = self.rowrange
        lists = []

        for c in cols:
            x=[]
            for r in rows:
                #absr = self.get_AbsoluteRow(r)
                val = model.getValueAt(r,c)
                if val == None or val == '':
                    continue
                x.append(val)
            lists.append(x)
        return lists

    def plotSelected(self, graphtype='XY'):
        """Plot the selected data using pylab - if possible"""

        from .Plot import pylabPlotter
        if not hasattr(self, 'pyplot'):
            self.pyplot = pylabPlotter()
        plotdata = []
        for p in self.getSelectionValues():
            x = []
            fail = False
            for d in p:
                try:
                    x.append(float(d))
                except:
                    fail = True
                    continue
            if fail == False:
                plotdata.append(x)

        pltlabels = self.getplotlabels()
        if len(pltlabels) > 2:
            self.pyplot.setDataSeries(pltlabels)
            self.pyplot.showlegend = 1
        self.pyplot.plotCurrent(data=plotdata, graphtype=graphtype)
        return

    def plotSetup(self):
        """Call pylab plot dialog setup, send data if we haven't already
            plotted"""

        from PylabPlot import pylabPlotter
        if not hasattr(self, 'pyplot'):
            self.pyplot = pylabPlotter()
        plotdata = self.getSelectionValues()
        if not self.pyplot.hasData() and plotdata != None:
            print ('has data')
            plotdata = self.getSelectionValues()
            pltlabels = self.getplotlabels()
            self.pyplot.setDataSeries(pltlabels)
            self.pyplot.plotSetup(plotdata)
        else:
            self.pyplot.plotSetup()
        return

    def getplotlabels(self):
        """Get labels for plot series from col labels"""
        pltlabels = []
        for col in self.multiplecollist:
            pltlabels.append(self.model.getColumnLabel(col))
        return pltlabels

    #--- Drawing stuff ---

    def drawGrid(self, startrow, endrow):
        """Draw the table grid lines"""
        self.delete('gridline','text')
        rows=len(self.rowrange)
        cols=self.cols
        w = self.cellwidth
        h = self.rowheight
        x_start=self.x_start
        y_start=self.y_start
        x_pos=x_start

        if self.vertlines==1:
            for col in range(cols+1):
                x=self.col_positions[col]
                self.create_line(x,y_start,x,y_start+rows*h, tag='gridline',
                                     fill=self.grid_color, width=self.linewidth)
        if self.horizlines==1:
            for row in range(startrow, endrow+1):
                y_pos=y_start+row*h
                self.create_line(x_start,y_pos,self.tablewidth,y_pos, tag='gridline',
                                    fill=self.grid_color, width=self.linewidth)
        return

    def drawRowHeader(self):
        """User has clicked to select a cell"""
        self.delete('rowheader')
        x_start=self.x_start
        y_start=self.y_start
        h=self.rowheight
        rowpos=0
        for row in self.rowrange:
            x1,y1,x2,y2 = self.getCellCoords(rowpos,0)
            self.create_rectangle(0,y1,x_start-2,y2,
                                      fill='gray75',
                                      outline='white',
                                      width=1,
                                      tag='rowheader')
            self.create_text(x_start/2,y1+h/2,
                                      text=row+1,
                                      fill='black',
                                      font=self.thefont,
                                      tag='rowheader')
            rowpos+=1
        return

    def drawSelectedRect(self, row, col, color=None):
        """User has clicked to select a cell"""
        if col >= self.cols:
            return
        self.delete('currentrect')
        bg = self.selectedcolor
        if color == None:
            color = 'gray25'
        w=3
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                                  fill=bg,
                                  outline=color,
                                  width=w,
                                  stipple='gray50',
                                  tag='currentrect')
        #self.lower('currentrect')
        #raise text above all
        self.lift('celltext'+str(col)+'_'+str(row))
        return

    def drawRect(self, row, col, color=None, tag=None, delete=1):
        """Cell is colored"""
        if delete==1:
            self.delete('cellbg'+str(row)+str(col))
        if color==None or color==self.cellbackgr:
            return
        else:
            bg=color
        if tag==None:
            recttag='fillrect'
        else:
            recttag=tag
        w=1
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                                  fill=bg,
                                  outline=bg,
                                  width=w,
                                  tag=(recttag,'cellbg'+str(row)+str(col)))
        self.lower(recttag)
        return

    def drawCellEntry(self, row, col, text=None):
        """When the user single/double clicks on a text/number cell, bring up entry window"""

        if self.read_only == True:
            return
        #absrow = self.get_AbsoluteRow(row)
        h=self.rowheight
        model=self.getModel()
        cellvalue = self.model.getCellRecord(row, col)
        if Formula.isFormula(cellvalue):
            return
        else:
            text = self.model.getValueAt(row, col)
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        #Draw an entry window
        txtvar = StringVar()
        txtvar.set(text)
        def callback(e):
            value = txtvar.get()
            if value == '=':
                #do a dialog that gets the formula into a text area
                #then they can click on the cells they want
                #when done the user presses ok and its entered into the cell
                self.cellentry.destroy()
                #its all done here..
                self.formula_Dialog(row, col)
                return

            coltype = self.model.getColumnType(col)
            if coltype == 'number':
                sta = self.checkDataEntry(e)
                if sta == 1:
                    model.setValueAt(value,row,col)
            elif coltype == 'text':
                model.setValueAt(value,row,col)

            color = self.model.getColorAt(row,col,'fg')
            self.drawText(row, col, value, color, align=self.align)
            if e.keysym=='Return':
                self.delete('entry')
                #self.drawRect(row, col)
                #self.gotonextCell(e)
            return

        self.cellentry=Entry(self.parentframe,width=20,
                        textvariable=txtvar,
                        #bg=self.entrybackgr,
                        #relief=FLAT,
                        takefocus=1,
                        font=self.thefont)
        self.cellentry.icursor(END)
        self.cellentry.bind('<Return>', callback)
        self.cellentry.bind('<KeyRelease>', callback)
        self.cellentry.focus_set()
        self.entrywin=self.create_window(x1+self.inset,y1+self.inset,
                                width=w-self.inset*2,height=h-self.inset*2,
                                window=self.cellentry,anchor='nw',
                                tag='entry')

        return

    def checkDataEntry(self,event=None):
        """do validation checks on data entry in a widget"""
        #if user enters commas, change to points
        import re
        value=event.widget.get()
        if value!='':
            try:
                value=re.sub(',','.', value)
                value=float(value)

            except ValueError:
                event.widget.configure(bg='red')
                return 0
        elif value == '':
            return 1
        return 1

    def drawText(self, row, col, celltxt, fgcolor=None, align=None):
        """Draw the text inside a cell area"""

        self.delete('celltext'+str(col)+'_'+str(row))
        h=self.rowheight
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        wrap = False
        pad=5
        # If celltxt is a number then we make it a string
        if type(celltxt) is float or type(celltxt) is int:
            celltxt=str(celltxt)
        length = len(celltxt)
        if length == 0:
            return
        #if cell width is less than x, print nothing
        if w<=10:
            return

        if fgcolor == None or fgcolor == "None":
            fgcolor = 'black'
        if align == None:
            align = 'w'
        if align == 'w':
            x1 = x1-w/2+pad
        elif align == 'e':
            x1 = x1+w/2-pad

        if w < 18:
            celltxt = '.'
        else:
            fontsize = self.fontsize
            colname = self.model.getColumnName(col)
            #scaling between canvas and text normalised to about font 14
            scale = 8.5 * float(fontsize)/12
            size = length * scale
            if size > w:
                newlength = w / scale
                #print w, size, length, newlength
                celltxt = celltxt[0:int(math.floor(newlength))]

        #if celltxt is dict then we are drawing a hyperlink
        if self.isLink(celltxt) == True:
            haslink=0
            linktext=celltxt['text']
            if len(linktext) > w/scale or w<28:
                linktext=linktext[0:int(w/fontsize*1.2)-2]+'..'
            if celltxt['link']!=None and celltxt['link']!='':
                f,s = self.thefont
                linkfont = (f, s, 'underline')
                linkcolor='blue'
                haslink=1
            else:
                linkfont = self.thefont
                linkcolor=fgcolor

            rect = self.create_text(x1+w/2,y1+h/2,
                                      text=linktext,
                                      fill=linkcolor,
                                      font=linkfont,
                                      tag=('text','hlink','celltext'+str(col)+'_'+str(row)))
            if haslink == 1:
                self.tag_bind(rect, '<Double-Button-1>', self.check_hyperlink)

        #just normal text
        else:
            rect = self.create_text(x1+w/2,y1+h/2,
                                      text=celltxt,
                                      fill=fgcolor,
                                      font=self.thefont,
                                      anchor=align,
                                      tag=('text','celltext'+str(col)+'_'+str(row)))
        return

    def isLink(self, cell):
        """Checks if cell is a hyperlink, without using isinstance"""
        try:
            if 'link' in cell:
                return True
        except:
            return False

    def drawSelectedRow(self):
        """Draw the highlight rect for the currently selected row"""

        self.delete('rowrect')
        row = self.currentrow
        x1,y1,x2,y2 = self.getCellCoords(row,0)
        x2 = self.tablewidth
        rect = self.create_rectangle(x1,y1,x2,y2,
                                  fill=self.rowselectedcolor,
                                  outline=self.rowselectedcolor,
                                  tag='rowrect')
        self.lower('rowrect')
        self.lower('fillrect')
        self.tablerowheader.drawSelectedRows(self.currentrow)
        return

    def drawSelectedCol(self, col=None, delete=1):
        """Draw an outline rect fot the current column selection"""

        if delete == 1:
            self.delete('colrect')
        if col == None:
            col=self.currentcol
        w=2
        x1,y1,x2,y2 = self.getCellCoords(0,col)
        y2 = self.rows * self.rowheight
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2+w/2,
                                     outline='blue',width=w,
                                     tag='colrect')
        return

    def drawMultipleRows(self, rowlist):
        """Draw more than one row selection"""

        self.delete('multiplesel')
        for r in rowlist:
            if r not in self.visiblerows or r > self.rows-1:
                continue
            x1,y1,x2,y2 = self.getCellCoords(r,0)
            x2 = self.tablewidth
            rect = self.create_rectangle(x1,y1,x2,y2,
                                      fill=self.multipleselectioncolor,
                                      outline=self.rowselectedcolor,
                                      tag=('multiplesel','rowrect'))
        self.lower('multiplesel')
        self.lower('fillrect')
        return

    def drawMultipleCells(self):
        """Draw an outline box for multiple cell selection"""

        self.delete('multicellrect')
        rows = self.multiplerowlist
        cols = self.multiplecollist
        w=2
        x1,y1,a,b = self.getCellCoords(rows[0],cols[0])
        c,d,x2,y2 = self.getCellCoords(rows[len(rows)-1],cols[len(cols)-1])
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2,
                             outline='blue',width=w,activefill='red',activestipple='gray25',
                             tag='multicellrect')

        return

    def drawTooltip(self, row, col):
        """Draw a tooltip showing contents of cell"""

        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        text = self.model.getValueAt(row,col)
        if isinstance(text, dict):
            if 'link' in text:
                text = text['link']

        # If text is a number we make it a string
        if type(text) is float or type is int:
            text = str(text)
        if text == None or text == '' or len(str(text))<=10:
            return

        sfont = font.Font(family='Arial', size=12,weight='bold')
        obj = self.create_text(x1+w/1.5,y2,text=text,
                                anchor='w',
                                font=sfont,tag='tooltip')

        box = self.bbox(obj)
        x1=box[0]-1
        y1=box[1]-1
        x2=box[2]+1
        y2=box[3]+1

        rect = self.create_rectangle(x1+1,y1+1,x2+1,y2+1,tag='tooltip',fill='black')
        rect2 = self.create_rectangle(x1,y1,x2,y2,tag='tooltip',fill='lightyellow')
        self.lift(obj)
        return

    def setcellbackgr(self):
        clr = self.getaColor(self.cellbackgr)
        if clr != None:
            self.cellbackgr = clr
        return

    def setgrid_color(self):
        clr = self.getaColor(self.grid_color)
        if clr != None:
            self.grid_color = clr

        return

    def setrowselectedcolor(self):
        clr = self.getaColor(self.rowselectedcolor)
        if clr != None:
            self.rowselectedcolor = clr
        return

    def getaColor(self, oldcolor):

        ctuple, newcolor = tkColorChooser.askcolor(title='pick a color', initialcolor=oldcolor,
                                                   parent=self.parentframe)
        if ctuple == None:
            return None
        return str(newcolor)

    #--- Preferences stuff ---

    def showtablePrefs(self, prefs=None):
        """Show table options dialog using an instance of prefs"""
        #self.prefs = prefs
        if self.prefs == None:
            self.loadPrefs()
        self.prefswindow=Toplevel()
        x,y,w,h = self.getGeometry(self.master)
        self.prefswindow.geometry('+%s+%s' %(int(x+w/2),int(y+h/2)))
        self.prefswindow.title('Preferences')
        self.prefswindow.resizable(width=FALSE, height=FALSE)

        frame1=Frame(self.prefswindow)
        frame1.pack(side=LEFT)
        frame2=Frame(self.prefswindow)
        frame2.pack()
        def close_prefsdialog():
            self.prefswindow.destroy()
        row=0
        Checkbutton(frame1, text="Show horizontal lines", variable=self.horizlinesvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        Checkbutton(frame1, text="Show vertical lines", variable=self.vertlinesvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        Checkbutton(frame1, text="Alternate Row Color", variable=self.alternaterowsvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        lblrowheight = Label(frame1,text='Row Height:')
        lblrowheight.grid(row=row,column=0,padx=3,pady=2)
        rowheightentry = Spinbox(frame1,from_=12,to=50,width=10,
                            textvariable=self.rowheightvar)
        rowheightentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1
        lblcellwidth = Label(frame1,text='Cell Width:')
        lblcellwidth.grid(row=row,column=0,padx=3,pady=2)
        cellwidthentry = Spinbox(frame1,from_=20,to=500, width=10,
                             textvariable=self.cellwidthvar)
        cellwidthentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        lbllinewidth = Label(frame1,text='Line Width:')
        lbllinewidth.grid(row=row,column=0,padx=3,pady=2)
        linewidthentry = Spinbox(frame1,from_=0,to=10,width=10,
                            textvariable=self.linewidthvar)
        linewidthentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        rowhdrwidth = Label(frame1,text='Row Header Width:')
        rowhdrwidth.grid(row=row,column=0,padx=3,pady=2)
        rowhdrentry = Spinbox(frame1,from_=0,to=300, width=10,
                            textvariable=self.rowheaderwidthvar)
        rowhdrentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        #fonts
        fts = self.getFonts()
        self.fontvar = StringVar()
        self.fontvar.set(self.prefs.get('celltextfont'))
        def setFont(*args):
            self.thefont = self.fontvar.get()
            return

        self.fontbox = Combobox(frame2,
                        values=(fts),
                        text='Font:',
                        height = 6,
                        textvariable=self.fontvar)
        self.fontvar.trace('w', setFont)
        Label(frame2,text='Font:').grid(row=row,column=0,padx=3,pady=2)
        self.fontbox.grid(row=row,column=1, columnspan=2, sticky='nes', padx=3,pady=2)
        row=row+1

        lblfontsize=Label(frame2,text='Text Size:')
        lblfontsize.grid(row=row,column=0,padx=3,pady=2)
        fontsizeentry = Spinbox(frame2,from_=6,to=50, width=20,
                                textvariable=self.celltextsizevar)

        fontsizeentry.grid(row=row,column=1, sticky='wens',padx=3,pady=2)
        row=row+1

        #cell alignment
        lbl=Label(frame2,text='Alignment:')
        lbl.grid(row=row,column=0,padx=3,pady=2)
        alignentry_button = Menubutton(frame2,textvariable=self.cellalignvar, width=16)
        alignentry_menu = Menu(alignentry_button,tearoff=0)
        alignentry_button['menu'] = alignentry_menu
        alignments=['w','e','center']
        for text in alignments:
            alignentry_menu.add_radiobutton(label=text,
                                            variable=self.cellalignvar,
                                            value=text,
                                            indicatoron=1)
        alignentry_button.grid(row=row,column=1, sticky='nes', padx=3,pady=2)
        row=row+1

        #colors
        style = Style()
        style.configure("cb.TButton", background=self.cellbackgr)
        cellbackgrbutton = Button(frame2, text='table background', style="cb.TButton", #bg=self.cellbackgr,
                                 command=self.setcellbackgr)
        cellbackgrbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
        row=row+1
        style = Style()
        style.configure("gc.TButton", background=self.grid_color)
        grid_colorbutton = Button(frame2, text='grid color',  style="gc.TButton", #bg=self.grid_color,
                                 command=self.setgrid_color)
        grid_colorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
        row=row+1
        style = Style()
        style.configure("rhc.TButton", background=self.rowselectedcolor)
        rowselectedcolorbutton = Button(frame2, text='row highlight color', style="rhc.TButton",
                                 command=self.setrowselectedcolor)
        rowselectedcolorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
        row=row+1

        frame=Frame(self.prefswindow)
        frame.pack()
        # Apply Button
        b = Button(frame, text="Apply Settings", command=self.applyPrefs)
        b.grid(row=row,column=1,columnspan=2,sticky='news',padx=4,pady=4)

        # Close button
        c=Button(frame,text='Close', command=close_prefsdialog)
        c.grid(row=row,column=0,sticky='news',padx=4,pady=4)
        self.prefswindow.focus_set()
        self.prefswindow.grab_set()
        self.prefswindow.wait_window()
        return self.prefswindow

    def getFonts(self):
        fonts = set(list(font.families()))
        fonts = sorted(list(fonts))
        return fonts

    def loadPrefs(self, prefs=None):
        """Load table specific prefs from the prefs instance used
           if they are not present, create them."""

        if prefs == None:
            prefs=Preferences('Table',{'check_for_update':1})
        self.prefs = prefs
        defaultprefs = {'horizlines':self.horizlines, 'vertlines':self.vertlines,
                        'alternaterows':self.alternaterows,
                        'rowheight':self.rowheight,
                        'cellwidth':100,
                        'autoresizecols': 0,
                        'align': 'w',
                        'celltextsize':11, 'celltextfont':'Arial',
                        'cellbackgr': self.cellbackgr, 'grid_color': self.grid_color,
                        'linewidth' : self.linewidth,
                        'rowselectedcolor': self.rowselectedcolor,
                        'rowheaderwidth': self.rowheaderwidth}

        #print (self.prefs.__dict__)
        for prop in defaultprefs:
            if not prop in self.prefs.prefs:
                #print (defaultprefs[prop])
                if defaultprefs[prop] != 'None':
                    self.prefs.set(prop, defaultprefs[prop])

        self.defaultprefs = defaultprefs

        #Create tkvars for dialog
        self.rowheightvar = IntVar()
        self.rowheightvar.set(self.prefs.get('rowheight'))
        self.rowheight = self.rowheightvar.get()
        self.cellwidthvar = IntVar()
        self.cellwidthvar.set(self.prefs.get('cellwidth'))
        self.cellwidth = self.cellwidthvar.get()
        self.cellalignvar = StringVar()
        self.cellalignvar.set(self.prefs.get('align'))
        self.align = self.cellalignvar.get()
        self.linewidthvar = IntVar()
        self.linewidthvar.set(self.prefs.get('linewidth'))
        self.horizlinesvar = IntVar()
        self.horizlinesvar.set(self.prefs.get('horizlines'))
        self.vertlinesvar = IntVar()
        self.vertlinesvar.set(self.prefs.get('vertlines'))
        self.alternaterowsvar = IntVar()
        self.alternaterowsvar.set(self.prefs.get('alternaterows'))
        self.celltextsizevar = IntVar()
        self.celltextsizevar.set(self.prefs.get('celltextsize'))
        self.cellbackgr = self.prefs.get('cellbackgr')
        self.grid_color = self.prefs.get('grid_color')
        self.rowselectedcolor = self.prefs.get('rowselectedcolor')
        self.fontsize = self.celltextsizevar.get()
        self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
        self.rowheaderwidthvar = IntVar()
        self.rowheaderwidthvar.set(self.prefs.get('rowheaderwidth'))
        self.rowheaderwidth = self.rowheaderwidthvar.get()
        return

    def savePrefs(self):
        """Save and set the prefs"""

        try:
            self.prefs.set('horizlines', self.horizlinesvar.get())
            self.horizlines = self.horizlinesvar.get()
            self.prefs.set('vertlines', self.vertlinesvar.get())
            self.vertlines = self.vertlinesvar.get()
            self.prefs.set('alternaterows', self.alternaterowsvar.get())
            self.alternaterows = self.alternaterowsvar.get()
            self.prefs.set('rowheight', self.rowheightvar.get())
            self.rowheight = self.rowheightvar.get()
            self.prefs.set('cellwidth', self.cellwidthvar.get())
            self.cellwidth = self.cellwidthvar.get()
            self.prefs.set('align', self.cellalignvar.get())
            self.align = self.cellalignvar.get()
            self.prefs.set('linewidth', self.linewidthvar.get())
            self.linewidth = self.linewidthvar.get()
            self.prefs.set('celltextsize', self.celltextsizevar.get())
            self.prefs.set('celltextfont', self.fontvar.get())
            self.prefs.set('cellbackgr', self.cellbackgr)
            self.prefs.set('grid_color', self.grid_color)
            self.prefs.set('rowselectedcolor', self.rowselectedcolor)
            self.prefs.set('rowheaderwidth', self.rowheaderwidth)
            self.rowheaderwidth = self.rowheaderwidthvar.get()
            self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
            self.fontsize = self.prefs.get('celltextsize')

        except ValueError as e:
            print (e)
            pass
        self.prefs.save_prefs()
        return

    def applyPrefs(self):
        """Apply prefs to the table by redrawing"""

        self.savePrefs()
        self.redrawTable()
        return

    def AskForColorButton(self, frame, text, func):
        def SetColor():
            ctuple, variable = tkColorChooser.askcolor(title='pick a color',
                                                       initialcolor=self.cellbackgr)

            return
        bgcolorbutton = Button(frame, text=text,command=SetColor)
        return  bgcolorbutton

    def check_hyperlink(self,event=None):
        """Check if a hyperlink was clicked"""

        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        #absrow = self.get_AbsoluteRow(row)
        recdata = self.model.getValueAt(row, col)
        try:
            link = recdata['link']
            import webbrowser
            webbrowser.open(link,autoraise=1)
        except:
            pass
        return

    def show_progressbar(self,message=None):
        """Show progress bar window for loading of data"""
        progress_win=Toplevel() # Open a new window
        progress_win.title("Please Wait")
        #progress_win.geometry('+%d+%d' %(self.parentframe.rootx+200,self.parentframe.rooty+200))
        #force on top
        progress_win.grab_set()
        progress_win.transient(self.parentframe)
        if message==None:
            message='Working'
        lbl = Label(progress_win,text=message,font='Arial 16')

        lbl.grid(row=0,column=0,columnspan=2,sticky='news',padx=6,pady=4)
        progrlbl = Label(progress_win,text='Progress:')
        progrlbl.grid(row=1,column=0,sticky='news',padx=2,pady=4)
        import ProgressBar
        self.bar = ProgressBar.ProgressBar(progress_win)
        self.bar.frame.grid(row=1,column=1,columnspan=2,padx=2,pady=4)

        return progress_win

    def updateModel(self, model):
        """Call this method to update the table model"""

        self.model = model
        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()
        self.tablewidth = (self.cellwidth)*self.cols
        self.tablecolheader = ColumnHeader(self.parentframe, self)
        self.tablerowheader = RowHeader(self.parentframe, self)
        self.createTableFrame()
        return

    def new(self):
        """Clears all the data and makes a new table"""

        mpDlg = MultipleValDialog(title='Create new table',
                                    initialvalues=(10, 4),
                                    labels=('rows','columns'),
                                    types=('int','int'),
                                    parent=self.parentframe)

        if mpDlg.result == True:
            rows = mpDlg.results[0]
            cols = mpDlg.results[1]
            model = TableModel(rows=rows,columns=cols)
            self.updateModel(model)
        return

    def load(self, filename=None):
        """load from a file"""

        if filename == None:
            filename = filedialog.askopenfilename(parent=self.master,
                                                      defaultextension='.table',
                                                      initialdir=os.getcwd(),
                                                      filetypes=[("pickle","*.table"),
                                                        ("All files","*.*")])
        if not os.path.exists(filename):
            print ('file does not exist')
            return
        if filename:
            self.model.load(filename)
            self.redrawTable()
        return

    def save(self, filename=None):
        """Save model to pickle file"""

        if filename == None:
            filename = filedialog.asksaveasfilename(parent=self.master,
                                                        defaultextension='.table',
                                                        initialdir=os.getcwd(),
                                                        filetypes=[("pickle","*.table"),
                                                          ("All files","*.*")])
        if filename:
            self.model.save(filename)
        return

    def importTable(self):
        self.importCSV()

    def importCSV(self, filename=None):
        """Import from csv file"""

        if filename is None:
            from .Tables_IO import TableImporter
            importer = TableImporter()
            importdialog = importer.import_Dialog(self.master)
            self.master.wait_window(importdialog)
            model = TableModel()
            model.importDict(importer.data)
        else:
            model = TableModel()
            model.importCSV(filename)
        self.updateModel(model)
        return

    def exportTable(self, filename=None):
        """Do a simple export of the cell contents to csv"""

        from .Tables_IO import TableExporter
        exporter = TableExporter()
        exporter.ExportTableData(self)
        return

    @classmethod
    def checkOSType(cls):
        """Check the OS we are in"""

        ostyp=''
        var_s=['OSTYPE','OS']
        for var in var_s:
            if var in os.environ:
                try:
                    ostyp = str.lower(os.environ[var])
                except:
                    ostyp = os.environ[var].lower()

        ostyp=ostyp.lower()
        if ostyp.find('windows')!=-1:
            ostyp='windows'
        elif ostyp.find('darwin')!=-1 or ostyp.find('apple')!=-1:
            ostyp='mac'
        elif ostyp.find('linux')!=-1:
            ostyp='linux'
        else:
            ostyp='unknown'
            try:
                info=os.uname()
            except:
                pass
            ostyp=info[0].lower()
            if ostyp.find('darwin')!=-1:
                ostyp='mac'
        return ostyp

    def getGeometry(self, frame):
        """Get frame geometry"""
        return frame.winfo_rootx(), frame.winfo_rooty(), frame.winfo_width(), frame.winfo_height()

class ColumnHeader(Canvas):
    """Class that takes it's size and rendering from a parent table
        and column names from the table model."""

    def __init__(self, parent=None, table=None):
        Canvas.__init__(self, parent, bg='gray25', width=500, height=20)
        self.thefont='Arial 14'
        if table != None:
            self.table = table
            self.height = 20
            self.model = self.table.getModel()
            self.config(width=self.table.width)
            #self.colnames = self.model.columnNames
            self.columnlabels = self.model.columnlabels
            self.bind('<Button-1>',self.handle_left_click)
            self.bind("<ButtonRelease-1>", self.handle_left_release)
            self.bind('<B1-Motion>', self.handle_mouse_drag)
            self.bind('<Motion>', self.handle_mouse_move)
            self.bind('<Shift-Button-1>', self.handle_left_shift_click)
            if self.table.ostyp=='mac':
                #For mac we bind Shift, left-click to right click
                self.bind("<Button-2>", self.handle_right_click)
                self.bind('<Shift-Button-1>',self.handle_right_click)
            else:
                self.bind("<Button-3>", self.handle_right_click)
            self.thefont = self.table.thefont
        return

    def redraw(self):

        cols = self.model.getColumnCount()
        self.tablewidth=self.table.tablewidth
        self.configure(scrollregion=(0,0, self.table.tablewidth+self.table.x_start, self.height))
        self.delete('gridline','text')
        self.delete('rect')
        self.atdivider = None
        align='w'
        pad=4
        h=self.height
        #x_start=self.table.x_start

        if cols == 0:
            return
        for col in self.table.visiblecols:
            colname = self.model.columnNames[col]
            if not colname in self.model.columnlabels:
                self.model.columnlabels[colname]=colname
            collabel = self.model.columnlabels[colname]
            if colname in self.model.columnwidths:
                w = self.model.columnwidths[colname]
            else:
                w = self.table.cellwidth
            x = self.table.col_positions[col]

            if len(collabel)>w/10:
                collabel = collabel[0:int(w/12)]+'.'
            line = self.create_line(x, 0, x, h, tag=('gridline', 'vertline'),
                                 fill='white', width=2)

            self.create_text(x+pad,h/2,
                            text=collabel,
                            anchor=align,
                            fill='white',
                            font=self.thefont,
                            tag='text')


        x=self.table.col_positions[col+1]
        self.create_line(x,0, x,h, tag='gridline',
                        fill='white', width=2)

        return

    def handle_left_click(self,event):
        """Does cell selection when mouse is clicked on canvas"""

        self.delete('rect')
        self.table.delete('entry')
        self.table.delete('multicellrect')
        colclicked = self.table.get_col_clicked(event)
        if colclicked == None:
            return
        #set all rows selected
        self.table.allrows = True
        self.table.setSelectedCol(colclicked)

        if self.atdivider == 1:
            return
        self.drawRect(self.table.currentcol)
        #also draw a copy of the rect to be dragged
        self.draggedcol = None
        self.drawRect(self.table.currentcol, tag='dragrect',
                        color='red', outline='white')
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        #finally, draw the selected col on the table
        self.table.drawSelectedCol()
        return

    def handle_left_release(self,event):
        """When mouse released implement resize or col move"""

        self.delete('dragrect')
        if self.atdivider == 1:
            #col = self.table.get_col_clicked(event)
            x=int(self.canvasx(event.x))
            col = self.table.currentcol
            x1,y1,x2,y2 = self.table.getCellCoords(0,col)
            newwidth=x - x1
            if newwidth < 5:
                newwidth=5
            self.table.resizeColumn(col, newwidth)
            self.table.delete('resizeline')
            self.delete('resizeline')
            self.delete('resizesymbol')
            self.atdivider = 0
            return
        self.delete('resizesymbol')
        #move column
        if self.draggedcol != None and self.table.currentcol != self.draggedcol:
            self.model.moveColumn(self.table.currentcol, self.draggedcol)
            self.table.setSelectedCol(self.draggedcol)
            self.table.redrawTable()
            self.table.drawSelectedCol(self.table.currentcol)
            self.drawRect(self.table.currentcol)
        return

    def handle_mouse_drag(self, event):
        """Handle column drag, will be either to move cols or resize"""

        x=int(self.canvasx(event.x))
        if self.atdivider == 1:
            self.table.delete('resizeline')
            self.delete('resizeline')
            self.table.create_line(x, 0, x, self.table.rowheight*self.table.rows,
                                width=2, fill='gray', tag='resizeline')
            self.create_line(x, 0, x, self.height,
                                width=2, fill='gray', tag='resizeline')
            return
        else:
            w = self.table.cellwidth
            self.draggedcol = self.table.get_col_clicked(event)
            x1, y1, x2, y2 = self.coords('dragrect')
            x=int(self.canvasx(event.x))
            y = self.canvasy(event.y)
            self.move('dragrect', x-x1-w/2, 0)

        return

    def within(self, val, l, d):
        """Utility funtion to see if val is within d of any
            items in the list l"""
        for v in l:
            if abs(val-v) <= d:
                return 1
        return 0

    def handle_mouse_move(self, event):
        """Handle mouse moved in header, if near divider draw resize symbol"""

        self.delete('resizesymbol')
        w=self.table.cellwidth
        h=self.height
        x_start=self.table.x_start
        #x = event.x
        x=int(self.canvasx(event.x))
        if x > self.tablewidth+w:
            return
        #if event x is within x pixels of divider, draw resize symbol
        if x!=x_start and self.within(x, self.table.col_positions, 4):
            col = self.table.get_col_clicked(event)
            if col == None:
                return
            self.draw_resize_symbol(col)
            self.atdivider = 1
        else:
            self.atdivider = 0
        return

    def handle_right_click(self, event):
        """respond to a right click"""

        self.handle_left_click(event)
        if self.table.read_only == False:
            self.rightmenu = self.popupMenu(event)
        return

    def handle_right_release(self, event):
        self.rightmenu.destroy()
        return

    def handle_left_shift_click(self, event):
        """Handle shift click, for selecting multiple cols"""

        self.table.delete('colrect')
        self.delete('rect')
        currcol = self.table.currentcol
        colclicked = self.table.get_col_clicked(event)
        if colclicked > currcol:
            self.table.multiplecollist = range(currcol, colclicked+1)
        elif colclicked < currcol:
            self.table.multiplecollist = range(colclicked, currcol+1)
        else:
            return

        #print self.table.multiplecollist
        for c in self.table.multiplecollist:
            self.drawRect(c, delete=0)
            self.table.drawSelectedCol(c, delete=0)
        return

    def popupMenu(self, event):
        """Add left and right click behaviour for column header"""

        colname = self.model.columnNames[self.table.currentcol]
        collabel = self.model.columnlabels[colname]
        currcol = self.table.currentcol
        popupmenu = Menu(self, tearoff = 0)
        def popupFocusOut(event):
            popupmenu.unpost()
        popupmenu.add_command(label="Rename Column", command=self.relabel_Column)
        popupmenu.add_command(label="Sort by "+ collabel, command=lambda : self.table.sortTable(currcol))
        popupmenu.add_command(label="Sort by "+ collabel +' (descending)', command=lambda : self.table.sortTable(currcol,reverse=1))
        popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn)
        popupmenu.add_command(label="Add New Column", command=self.table.addColumn)

        popupmenu.bind("<FocusOut>", popupFocusOut)
        #self.bind("<Button-3>", popupFocusOut)
        popupmenu.focus_set()
        popupmenu.post(event.x_root, event.y_root)
        return popupmenu

    def relabel_Column(self):
        col=self.table.currentcol
        ans = simpledialog.askstring("New column name?", "Enter new name:")
        if ans !=None:
            if ans == '':
                messagebox.showwarning("Error", "Name should not be blank.")
                return
            else:
                self.model.relabel_Column(col, ans)
                self.redraw()
        return

    def draw_resize_symbol(self, col):
        """Draw a symbol to show that col can be resized when mouse here"""

        self.delete('resizesymbol')
        w=self.table.cellwidth
        h=self.height
        wdth=1
        hfac1=0.2
        hfac2=0.4
        x_start=self.table.x_start
        x1,y1,x2,y2 = self.table.getCellCoords(0,col)

        self.create_polygon(x2-3,h/4, x2-10,h/2, x2-3,h*3/4, tag='resizesymbol',
            fill='white', outline='gray', width=wdth)
        self.create_polygon(x2+2,h/4, x2+10,h/2, x2+2,h*3/4, tag='resizesymbol',
            fill='white', outline='gray', width=wdth)
        return

    def drawRect(self,col, tag=None, color=None, outline=None, delete=1):
        """User has clicked to select a col"""

        if tag==None:
            tag='rect'
        if color==None:
            color='#0099CC'
        if outline==None:
            outline='gray25'
        if delete == 1:
            self.delete(tag)
        w=2
        x1,y1,x2,y2 = self.table.getCellCoords(0,col)
        rect = self.create_rectangle(x1,y1-w,x2,self.height,
                                  fill=color,
                                  outline=outline,
                                  width=w,
                                  stipple='gray50',
                                  tag=tag)
        self.lower(tag)
        return

class RowHeader(Canvas):
    """Class that displays the row headings on the table
       takes it's size and rendering from the parent table
       This also handles row/record selection as opposed to cell
       selection"""

    def __init__(self, parent=None, table=None, width=40):
        Canvas.__init__(self, parent, bg='gray75', width=width, height=None)

        if table != None:
            self.table = table
            self.width = width
            self.x_start = 0
            self.inset = 1
            self.config(height = self.table.height)
            self.startrow = self.endrow = None
            self.model = self.table.getModel()
            self.bind('<Button-1>',self.handle_left_click)
            self.bind("<ButtonRelease-1>", self.handle_left_release)
            self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
            self.bind('<Button-3>',self.handle_right_click)
            self.bind('<B1-Motion>', self.handle_mouse_drag)
            #self.bind('<Shift-Button-1>', self.handle_left_shift_click)
        return

    def redraw(self, align='w', showkeys=False):
        """Redraw row header"""

        self.height = self.table.rowheight * self.table.rows+10
        self.configure(scrollregion=(0,0, self.width, self.height))
        self.delete('rowheader','text')
        self.delete('rect')
        w = float(self.width)
        h = self.table.rowheight
        x = self.x_start+w/2
        if align == 'w':
            x = x-w/2+3
        elif align == 'e':
            x = x+w/2-3
        for row in self.table.visiblerows:
            if showkeys == True:
                text = self.model.getRecName(row)
            else:
                text = row+1
            x1,y1,x2,y2 = self.table.getCellCoords(row,0)
            self.create_rectangle(0,y1,w-1,y2,
                                      fill='gray75',
                                      outline='white',
                                      width=1,
                                      tag='rowheader')
            self.create_text(x,y1+h/2,
                                      text=text,
                                      fill='black',
                                      font=self.table.thefont,
                                      tag='text', anchor=align)
        return

    def setWidth(self, w):
        """Set width"""
        self.width = w
        self.redraw()
        return

    def clearSelected(self):
        self.delete('rect')
        return

    def handle_left_click(self, event):
        rowclicked = self.table.get_row_clicked(event)
        self.startrow = rowclicked
        if 0 <= rowclicked < self.table.rows:
            self.delete('rect')
            self.table.delete('entry')
            self.table.delete('multicellrect')
            #set row selected
            self.table.setSelectedRow(rowclicked)
            self.table.drawSelectedRow()
            self.drawSelectedRows(self.table.currentrow)
        return

    def handle_left_release(self,event):

        return

    def handle_left_ctrl_click(self, event):
        """Handle ctrl clicks - for multiple row selections"""

        rowclicked = self.table.get_row_clicked(event)
        multirowlist = self.table.multiplerowlist
        if 0 <= rowclicked < self.table.rows:
            if rowclicked not in multirowlist:
                multirowlist.append(rowclicked)
            else:
                multirowlist.remove(rowclicked)
            self.table.drawMultipleRows(multirowlist)
            self.drawSelectedRows(multirowlist)
        return

    def handle_right_click(self,event):

        return

    '''def handle_mouse_drag(self, event):
        """Handle mouse drag for mult row selection"""
        rowover = self.table.get_row_clicked(event)
        colover = self.table.get_col_clicked(event)
        if colover == None or rowover == None:
            return'''

    def handle_mouse_drag(self, event):
        """Handle mouse moved with button held down, multiple selections"""

        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        rowover = self.table.get_row_clicked(event)
        colover = self.table.get_col_clicked(event)
        if rowover == None:
            return
        if rowover >= self.table.rows or self.startrow > self.table.rows:
            return
        else:
            self.endrow = rowover
        #draw the selected rows
        if self.endrow != self.startrow:
            if self.endrow < self.startrow:
                rowlist=range(self.endrow, self.startrow+1)
            else:
                rowlist=range(self.startrow, self.endrow+1)
            self.drawSelectedRows(rowlist)
            self.table.multiplerowlist = rowlist
            self.table.drawMultipleRows(rowlist)
        else:
            self.table.multiplerowlist = []
            self.table.multiplerowlist.append(rowover)
            self.drawSelectedRows(rowover)
            self.table.drawMultipleRows(self.table.multiplerowlist)
        return

    def drawSelectedRows(self, rows=None):
        """Draw selected rows, accepts a list or integer"""

        self.delete('rect')
        if type(rows) is not list:
            rowlist=[]
            rowlist.append(rows)
        else:
           rowlist = rows
        for r in rowlist:
            if r not in self.table.visiblerows:
                continue
            self.drawRect(r, delete=0)
        return

    def drawRect(self, row=None, tag=None, color=None, outline=None, delete=1):
        """Draw a rect representing row selection"""

        if tag==None:
            tag='rect'
        if color==None:
            color='#0099CC'
        if outline==None:
            outline='gray25'
        if delete == 1:
            self.delete(tag)
        w=0
        i = self.inset
        x1,y1,x2,y2 = self.table.getCellCoords(row, 0)
        rect = self.create_rectangle(0+i,y1+i,self.width-i,y2,
                                      fill=color,
                                      outline=outline,
                                      width=w,
                                      tag=tag)
        self.lift('text')
        return

class AutoScrollbar(Scrollbar):
    """a scrollbar that hides itself if it's not needed.  only
       works if you use the grid geometry manager."""

    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            # grid_remove is currently missing from Tkinter!
            self.tk.call("grid", "remove", self)
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)
    #def pack(self, **kw):
    #    raise TclError, "cannot use pack with this widget"
    #def place(self, **kw):
    #    raise TclError, "cannot use place with this widget"

Classes

class AutoScrollbar (master=None, **kw)

a scrollbar that hides itself if it's not needed. only works if you use the grid geometry manager.

Construct a Ttk Scrollbar with parent master.

STANDARD OPTIONS

class, cursor, style, takefocus

WIDGET-SPECIFIC OPTIONS

command, orient
Source code
class AutoScrollbar(Scrollbar):
    """a scrollbar that hides itself if it's not needed.  only
       works if you use the grid geometry manager."""

    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            # grid_remove is currently missing from Tkinter!
            self.tk.call("grid", "remove", self)
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)

Ancestors

  • tkinter.ttk.Scrollbar
  • tkinter.ttk.Widget
  • tkinter.Scrollbar
  • tkinter.Widget
  • tkinter.BaseWidget
  • tkinter.Misc
  • tkinter.Pack
  • tkinter.Place
  • tkinter.Grid

Methods

def set(self, lo, hi)

Set the fractional values of the slider position (upper and lower ends as value between 0 and 1).

Source code
def set(self, lo, hi):
    if float(lo) <= 0.0 and float(hi) >= 1.0:
        # grid_remove is currently missing from Tkinter!
        self.tk.call("grid", "remove", self)
    else:
        self.grid()
    Scrollbar.set(self, lo, hi)
class ColumnHeader (parent=None, table=None)

Class that takes it's size and rendering from a parent table and column names from the table model.

Construct a canvas widget with the parent MASTER.

Valid resource names: background, bd, bg, borderwidth, closeenough, confine, cursor, height, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, offset, relief, scrollregion, selectbackground, selectborderwidth, selectforeground, state, takefocus, width, xscrollcommand, xscrollincrement, yscrollcommand, yscrollincrement.

Source code
class ColumnHeader(Canvas):
    """Class that takes it's size and rendering from a parent table
        and column names from the table model."""

    def __init__(self, parent=None, table=None):
        Canvas.__init__(self, parent, bg='gray25', width=500, height=20)
        self.thefont='Arial 14'
        if table != None:
            self.table = table
            self.height = 20
            self.model = self.table.getModel()
            self.config(width=self.table.width)
            #self.colnames = self.model.columnNames
            self.columnlabels = self.model.columnlabels
            self.bind('<Button-1>',self.handle_left_click)
            self.bind("<ButtonRelease-1>", self.handle_left_release)
            self.bind('<B1-Motion>', self.handle_mouse_drag)
            self.bind('<Motion>', self.handle_mouse_move)
            self.bind('<Shift-Button-1>', self.handle_left_shift_click)
            if self.table.ostyp=='mac':
                #For mac we bind Shift, left-click to right click
                self.bind("<Button-2>", self.handle_right_click)
                self.bind('<Shift-Button-1>',self.handle_right_click)
            else:
                self.bind("<Button-3>", self.handle_right_click)
            self.thefont = self.table.thefont
        return

    def redraw(self):

        cols = self.model.getColumnCount()
        self.tablewidth=self.table.tablewidth
        self.configure(scrollregion=(0,0, self.table.tablewidth+self.table.x_start, self.height))
        self.delete('gridline','text')
        self.delete('rect')
        self.atdivider = None
        align='w'
        pad=4
        h=self.height
        #x_start=self.table.x_start

        if cols == 0:
            return
        for col in self.table.visiblecols:
            colname = self.model.columnNames[col]
            if not colname in self.model.columnlabels:
                self.model.columnlabels[colname]=colname
            collabel = self.model.columnlabels[colname]
            if colname in self.model.columnwidths:
                w = self.model.columnwidths[colname]
            else:
                w = self.table.cellwidth
            x = self.table.col_positions[col]

            if len(collabel)>w/10:
                collabel = collabel[0:int(w/12)]+'.'
            line = self.create_line(x, 0, x, h, tag=('gridline', 'vertline'),
                                 fill='white', width=2)

            self.create_text(x+pad,h/2,
                            text=collabel,
                            anchor=align,
                            fill='white',
                            font=self.thefont,
                            tag='text')


        x=self.table.col_positions[col+1]
        self.create_line(x,0, x,h, tag='gridline',
                        fill='white', width=2)

        return

    def handle_left_click(self,event):
        """Does cell selection when mouse is clicked on canvas"""

        self.delete('rect')
        self.table.delete('entry')
        self.table.delete('multicellrect')
        colclicked = self.table.get_col_clicked(event)
        if colclicked == None:
            return
        #set all rows selected
        self.table.allrows = True
        self.table.setSelectedCol(colclicked)

        if self.atdivider == 1:
            return
        self.drawRect(self.table.currentcol)
        #also draw a copy of the rect to be dragged
        self.draggedcol = None
        self.drawRect(self.table.currentcol, tag='dragrect',
                        color='red', outline='white')
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        #finally, draw the selected col on the table
        self.table.drawSelectedCol()
        return

    def handle_left_release(self,event):
        """When mouse released implement resize or col move"""

        self.delete('dragrect')
        if self.atdivider == 1:
            #col = self.table.get_col_clicked(event)
            x=int(self.canvasx(event.x))
            col = self.table.currentcol
            x1,y1,x2,y2 = self.table.getCellCoords(0,col)
            newwidth=x - x1
            if newwidth < 5:
                newwidth=5
            self.table.resizeColumn(col, newwidth)
            self.table.delete('resizeline')
            self.delete('resizeline')
            self.delete('resizesymbol')
            self.atdivider = 0
            return
        self.delete('resizesymbol')
        #move column
        if self.draggedcol != None and self.table.currentcol != self.draggedcol:
            self.model.moveColumn(self.table.currentcol, self.draggedcol)
            self.table.setSelectedCol(self.draggedcol)
            self.table.redrawTable()
            self.table.drawSelectedCol(self.table.currentcol)
            self.drawRect(self.table.currentcol)
        return

    def handle_mouse_drag(self, event):
        """Handle column drag, will be either to move cols or resize"""

        x=int(self.canvasx(event.x))
        if self.atdivider == 1:
            self.table.delete('resizeline')
            self.delete('resizeline')
            self.table.create_line(x, 0, x, self.table.rowheight*self.table.rows,
                                width=2, fill='gray', tag='resizeline')
            self.create_line(x, 0, x, self.height,
                                width=2, fill='gray', tag='resizeline')
            return
        else:
            w = self.table.cellwidth
            self.draggedcol = self.table.get_col_clicked(event)
            x1, y1, x2, y2 = self.coords('dragrect')
            x=int(self.canvasx(event.x))
            y = self.canvasy(event.y)
            self.move('dragrect', x-x1-w/2, 0)

        return

    def within(self, val, l, d):
        """Utility funtion to see if val is within d of any
            items in the list l"""
        for v in l:
            if abs(val-v) <= d:
                return 1
        return 0

    def handle_mouse_move(self, event):
        """Handle mouse moved in header, if near divider draw resize symbol"""

        self.delete('resizesymbol')
        w=self.table.cellwidth
        h=self.height
        x_start=self.table.x_start
        #x = event.x
        x=int(self.canvasx(event.x))
        if x > self.tablewidth+w:
            return
        #if event x is within x pixels of divider, draw resize symbol
        if x!=x_start and self.within(x, self.table.col_positions, 4):
            col = self.table.get_col_clicked(event)
            if col == None:
                return
            self.draw_resize_symbol(col)
            self.atdivider = 1
        else:
            self.atdivider = 0
        return

    def handle_right_click(self, event):
        """respond to a right click"""

        self.handle_left_click(event)
        if self.table.read_only == False:
            self.rightmenu = self.popupMenu(event)
        return

    def handle_right_release(self, event):
        self.rightmenu.destroy()
        return

    def handle_left_shift_click(self, event):
        """Handle shift click, for selecting multiple cols"""

        self.table.delete('colrect')
        self.delete('rect')
        currcol = self.table.currentcol
        colclicked = self.table.get_col_clicked(event)
        if colclicked > currcol:
            self.table.multiplecollist = range(currcol, colclicked+1)
        elif colclicked < currcol:
            self.table.multiplecollist = range(colclicked, currcol+1)
        else:
            return

        #print self.table.multiplecollist
        for c in self.table.multiplecollist:
            self.drawRect(c, delete=0)
            self.table.drawSelectedCol(c, delete=0)
        return

    def popupMenu(self, event):
        """Add left and right click behaviour for column header"""

        colname = self.model.columnNames[self.table.currentcol]
        collabel = self.model.columnlabels[colname]
        currcol = self.table.currentcol
        popupmenu = Menu(self, tearoff = 0)
        def popupFocusOut(event):
            popupmenu.unpost()
        popupmenu.add_command(label="Rename Column", command=self.relabel_Column)
        popupmenu.add_command(label="Sort by "+ collabel, command=lambda : self.table.sortTable(currcol))
        popupmenu.add_command(label="Sort by "+ collabel +' (descending)', command=lambda : self.table.sortTable(currcol,reverse=1))
        popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn)
        popupmenu.add_command(label="Add New Column", command=self.table.addColumn)

        popupmenu.bind("<FocusOut>", popupFocusOut)
        #self.bind("<Button-3>", popupFocusOut)
        popupmenu.focus_set()
        popupmenu.post(event.x_root, event.y_root)
        return popupmenu

    def relabel_Column(self):
        col=self.table.currentcol
        ans = simpledialog.askstring("New column name?", "Enter new name:")
        if ans !=None:
            if ans == '':
                messagebox.showwarning("Error", "Name should not be blank.")
                return
            else:
                self.model.relabel_Column(col, ans)
                self.redraw()
        return

    def draw_resize_symbol(self, col):
        """Draw a symbol to show that col can be resized when mouse here"""

        self.delete('resizesymbol')
        w=self.table.cellwidth
        h=self.height
        wdth=1
        hfac1=0.2
        hfac2=0.4
        x_start=self.table.x_start
        x1,y1,x2,y2 = self.table.getCellCoords(0,col)

        self.create_polygon(x2-3,h/4, x2-10,h/2, x2-3,h*3/4, tag='resizesymbol',
            fill='white', outline='gray', width=wdth)
        self.create_polygon(x2+2,h/4, x2+10,h/2, x2+2,h*3/4, tag='resizesymbol',
            fill='white', outline='gray', width=wdth)
        return

    def drawRect(self,col, tag=None, color=None, outline=None, delete=1):
        """User has clicked to select a col"""

        if tag==None:
            tag='rect'
        if color==None:
            color='#0099CC'
        if outline==None:
            outline='gray25'
        if delete == 1:
            self.delete(tag)
        w=2
        x1,y1,x2,y2 = self.table.getCellCoords(0,col)
        rect = self.create_rectangle(x1,y1-w,x2,self.height,
                                  fill=color,
                                  outline=outline,
                                  width=w,
                                  stipple='gray50',
                                  tag=tag)
        self.lower(tag)
        return

Ancestors

  • tkinter.Canvas
  • tkinter.Widget
  • tkinter.BaseWidget
  • tkinter.Misc
  • tkinter.Pack
  • tkinter.Place
  • tkinter.Grid
  • tkinter.XView
  • tkinter.YView

Methods

def drawRect(self, col, tag=None, color=None, outline=None, delete=1)

User has clicked to select a col

Source code
def drawRect(self,col, tag=None, color=None, outline=None, delete=1):
    """User has clicked to select a col"""

    if tag==None:
        tag='rect'
    if color==None:
        color='#0099CC'
    if outline==None:
        outline='gray25'
    if delete == 1:
        self.delete(tag)
    w=2
    x1,y1,x2,y2 = self.table.getCellCoords(0,col)
    rect = self.create_rectangle(x1,y1-w,x2,self.height,
                              fill=color,
                              outline=outline,
                              width=w,
                              stipple='gray50',
                              tag=tag)
    self.lower(tag)
    return
def draw_resize_symbol(self, col)

Draw a symbol to show that col can be resized when mouse here

Source code
def draw_resize_symbol(self, col):
    """Draw a symbol to show that col can be resized when mouse here"""

    self.delete('resizesymbol')
    w=self.table.cellwidth
    h=self.height
    wdth=1
    hfac1=0.2
    hfac2=0.4
    x_start=self.table.x_start
    x1,y1,x2,y2 = self.table.getCellCoords(0,col)

    self.create_polygon(x2-3,h/4, x2-10,h/2, x2-3,h*3/4, tag='resizesymbol',
        fill='white', outline='gray', width=wdth)
    self.create_polygon(x2+2,h/4, x2+10,h/2, x2+2,h*3/4, tag='resizesymbol',
        fill='white', outline='gray', width=wdth)
    return
def handle_left_click(self, event)

Does cell selection when mouse is clicked on canvas

Source code
def handle_left_click(self,event):
    """Does cell selection when mouse is clicked on canvas"""

    self.delete('rect')
    self.table.delete('entry')
    self.table.delete('multicellrect')
    colclicked = self.table.get_col_clicked(event)
    if colclicked == None:
        return
    #set all rows selected
    self.table.allrows = True
    self.table.setSelectedCol(colclicked)

    if self.atdivider == 1:
        return
    self.drawRect(self.table.currentcol)
    #also draw a copy of the rect to be dragged
    self.draggedcol = None
    self.drawRect(self.table.currentcol, tag='dragrect',
                    color='red', outline='white')
    if hasattr(self, 'rightmenu'):
        self.rightmenu.destroy()
    #finally, draw the selected col on the table
    self.table.drawSelectedCol()
    return
def handle_left_release(self, event)

When mouse released implement resize or col move

Source code
def handle_left_release(self,event):
    """When mouse released implement resize or col move"""

    self.delete('dragrect')
    if self.atdivider == 1:
        #col = self.table.get_col_clicked(event)
        x=int(self.canvasx(event.x))
        col = self.table.currentcol
        x1,y1,x2,y2 = self.table.getCellCoords(0,col)
        newwidth=x - x1
        if newwidth < 5:
            newwidth=5
        self.table.resizeColumn(col, newwidth)
        self.table.delete('resizeline')
        self.delete('resizeline')
        self.delete('resizesymbol')
        self.atdivider = 0
        return
    self.delete('resizesymbol')
    #move column
    if self.draggedcol != None and self.table.currentcol != self.draggedcol:
        self.model.moveColumn(self.table.currentcol, self.draggedcol)
        self.table.setSelectedCol(self.draggedcol)
        self.table.redrawTable()
        self.table.drawSelectedCol(self.table.currentcol)
        self.drawRect(self.table.currentcol)
    return
def handle_left_shift_click(self, event)

Handle shift click, for selecting multiple cols

Source code
def handle_left_shift_click(self, event):
    """Handle shift click, for selecting multiple cols"""

    self.table.delete('colrect')
    self.delete('rect')
    currcol = self.table.currentcol
    colclicked = self.table.get_col_clicked(event)
    if colclicked > currcol:
        self.table.multiplecollist = range(currcol, colclicked+1)
    elif colclicked < currcol:
        self.table.multiplecollist = range(colclicked, currcol+1)
    else:
        return

    #print self.table.multiplecollist
    for c in self.table.multiplecollist:
        self.drawRect(c, delete=0)
        self.table.drawSelectedCol(c, delete=0)
    return
def handle_mouse_drag(self, event)

Handle column drag, will be either to move cols or resize

Source code
def handle_mouse_drag(self, event):
    """Handle column drag, will be either to move cols or resize"""

    x=int(self.canvasx(event.x))
    if self.atdivider == 1:
        self.table.delete('resizeline')
        self.delete('resizeline')
        self.table.create_line(x, 0, x, self.table.rowheight*self.table.rows,
                            width=2, fill='gray', tag='resizeline')
        self.create_line(x, 0, x, self.height,
                            width=2, fill='gray', tag='resizeline')
        return
    else:
        w = self.table.cellwidth
        self.draggedcol = self.table.get_col_clicked(event)
        x1, y1, x2, y2 = self.coords('dragrect')
        x=int(self.canvasx(event.x))
        y = self.canvasy(event.y)
        self.move('dragrect', x-x1-w/2, 0)

    return
def handle_mouse_move(self, event)

Handle mouse moved in header, if near divider draw resize symbol

Source code
def handle_mouse_move(self, event):
    """Handle mouse moved in header, if near divider draw resize symbol"""

    self.delete('resizesymbol')
    w=self.table.cellwidth
    h=self.height
    x_start=self.table.x_start
    #x = event.x
    x=int(self.canvasx(event.x))
    if x > self.tablewidth+w:
        return
    #if event x is within x pixels of divider, draw resize symbol
    if x!=x_start and self.within(x, self.table.col_positions, 4):
        col = self.table.get_col_clicked(event)
        if col == None:
            return
        self.draw_resize_symbol(col)
        self.atdivider = 1
    else:
        self.atdivider = 0
    return
def handle_right_click(self, event)

respond to a right click

Source code
def handle_right_click(self, event):
    """respond to a right click"""

    self.handle_left_click(event)
    if self.table.read_only == False:
        self.rightmenu = self.popupMenu(event)
    return
def handle_right_release(self, event)
Source code
def handle_right_release(self, event):
    self.rightmenu.destroy()
    return
def popupMenu(self, event)

Add left and right click behaviour for column header

Source code
def popupMenu(self, event):
    """Add left and right click behaviour for column header"""

    colname = self.model.columnNames[self.table.currentcol]
    collabel = self.model.columnlabels[colname]
    currcol = self.table.currentcol
    popupmenu = Menu(self, tearoff = 0)
    def popupFocusOut(event):
        popupmenu.unpost()
    popupmenu.add_command(label="Rename Column", command=self.relabel_Column)
    popupmenu.add_command(label="Sort by "+ collabel, command=lambda : self.table.sortTable(currcol))
    popupmenu.add_command(label="Sort by "+ collabel +' (descending)', command=lambda : self.table.sortTable(currcol,reverse=1))
    popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn)
    popupmenu.add_command(label="Add New Column", command=self.table.addColumn)

    popupmenu.bind("<FocusOut>", popupFocusOut)
    #self.bind("<Button-3>", popupFocusOut)
    popupmenu.focus_set()
    popupmenu.post(event.x_root, event.y_root)
    return popupmenu
def redraw(self)
Source code
def redraw(self):

    cols = self.model.getColumnCount()
    self.tablewidth=self.table.tablewidth
    self.configure(scrollregion=(0,0, self.table.tablewidth+self.table.x_start, self.height))
    self.delete('gridline','text')
    self.delete('rect')
    self.atdivider = None
    align='w'
    pad=4
    h=self.height
    #x_start=self.table.x_start

    if cols == 0:
        return
    for col in self.table.visiblecols:
        colname = self.model.columnNames[col]
        if not colname in self.model.columnlabels:
            self.model.columnlabels[colname]=colname
        collabel = self.model.columnlabels[colname]
        if colname in self.model.columnwidths:
            w = self.model.columnwidths[colname]
        else:
            w = self.table.cellwidth
        x = self.table.col_positions[col]

        if len(collabel)>w/10:
            collabel = collabel[0:int(w/12)]+'.'
        line = self.create_line(x, 0, x, h, tag=('gridline', 'vertline'),
                             fill='white', width=2)

        self.create_text(x+pad,h/2,
                        text=collabel,
                        anchor=align,
                        fill='white',
                        font=self.thefont,
                        tag='text')


    x=self.table.col_positions[col+1]
    self.create_line(x,0, x,h, tag='gridline',
                    fill='white', width=2)

    return
def relabel_Column(self)
Source code
def relabel_Column(self):
    col=self.table.currentcol
    ans = simpledialog.askstring("New column name?", "Enter new name:")
    if ans !=None:
        if ans == '':
            messagebox.showwarning("Error", "Name should not be blank.")
            return
        else:
            self.model.relabel_Column(col, ans)
            self.redraw()
    return
def within(self, val, l, d)

Utility funtion to see if val is within d of any items in the list l

Source code
def within(self, val, l, d):
    """Utility funtion to see if val is within d of any
        items in the list l"""
    for v in l:
        if abs(val-v) <= d:
            return 1
    return 0
class RowHeader (parent=None, table=None, width=40)

Class that displays the row headings on the table takes it's size and rendering from the parent table This also handles row/record selection as opposed to cell selection

Construct a canvas widget with the parent MASTER.

Valid resource names: background, bd, bg, borderwidth, closeenough, confine, cursor, height, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, offset, relief, scrollregion, selectbackground, selectborderwidth, selectforeground, state, takefocus, width, xscrollcommand, xscrollincrement, yscrollcommand, yscrollincrement.

Source code
class RowHeader(Canvas):
    """Class that displays the row headings on the table
       takes it's size and rendering from the parent table
       This also handles row/record selection as opposed to cell
       selection"""

    def __init__(self, parent=None, table=None, width=40):
        Canvas.__init__(self, parent, bg='gray75', width=width, height=None)

        if table != None:
            self.table = table
            self.width = width
            self.x_start = 0
            self.inset = 1
            self.config(height = self.table.height)
            self.startrow = self.endrow = None
            self.model = self.table.getModel()
            self.bind('<Button-1>',self.handle_left_click)
            self.bind("<ButtonRelease-1>", self.handle_left_release)
            self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
            self.bind('<Button-3>',self.handle_right_click)
            self.bind('<B1-Motion>', self.handle_mouse_drag)
            #self.bind('<Shift-Button-1>', self.handle_left_shift_click)
        return

    def redraw(self, align='w', showkeys=False):
        """Redraw row header"""

        self.height = self.table.rowheight * self.table.rows+10
        self.configure(scrollregion=(0,0, self.width, self.height))
        self.delete('rowheader','text')
        self.delete('rect')
        w = float(self.width)
        h = self.table.rowheight
        x = self.x_start+w/2
        if align == 'w':
            x = x-w/2+3
        elif align == 'e':
            x = x+w/2-3
        for row in self.table.visiblerows:
            if showkeys == True:
                text = self.model.getRecName(row)
            else:
                text = row+1
            x1,y1,x2,y2 = self.table.getCellCoords(row,0)
            self.create_rectangle(0,y1,w-1,y2,
                                      fill='gray75',
                                      outline='white',
                                      width=1,
                                      tag='rowheader')
            self.create_text(x,y1+h/2,
                                      text=text,
                                      fill='black',
                                      font=self.table.thefont,
                                      tag='text', anchor=align)
        return

    def setWidth(self, w):
        """Set width"""
        self.width = w
        self.redraw()
        return

    def clearSelected(self):
        self.delete('rect')
        return

    def handle_left_click(self, event):
        rowclicked = self.table.get_row_clicked(event)
        self.startrow = rowclicked
        if 0 <= rowclicked < self.table.rows:
            self.delete('rect')
            self.table.delete('entry')
            self.table.delete('multicellrect')
            #set row selected
            self.table.setSelectedRow(rowclicked)
            self.table.drawSelectedRow()
            self.drawSelectedRows(self.table.currentrow)
        return

    def handle_left_release(self,event):

        return

    def handle_left_ctrl_click(self, event):
        """Handle ctrl clicks - for multiple row selections"""

        rowclicked = self.table.get_row_clicked(event)
        multirowlist = self.table.multiplerowlist
        if 0 <= rowclicked < self.table.rows:
            if rowclicked not in multirowlist:
                multirowlist.append(rowclicked)
            else:
                multirowlist.remove(rowclicked)
            self.table.drawMultipleRows(multirowlist)
            self.drawSelectedRows(multirowlist)
        return

    def handle_right_click(self,event):

        return

    '''def handle_mouse_drag(self, event):
        """Handle mouse drag for mult row selection"""
        rowover = self.table.get_row_clicked(event)
        colover = self.table.get_col_clicked(event)
        if colover == None or rowover == None:
            return'''

    def handle_mouse_drag(self, event):
        """Handle mouse moved with button held down, multiple selections"""

        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        rowover = self.table.get_row_clicked(event)
        colover = self.table.get_col_clicked(event)
        if rowover == None:
            return
        if rowover >= self.table.rows or self.startrow > self.table.rows:
            return
        else:
            self.endrow = rowover
        #draw the selected rows
        if self.endrow != self.startrow:
            if self.endrow < self.startrow:
                rowlist=range(self.endrow, self.startrow+1)
            else:
                rowlist=range(self.startrow, self.endrow+1)
            self.drawSelectedRows(rowlist)
            self.table.multiplerowlist = rowlist
            self.table.drawMultipleRows(rowlist)
        else:
            self.table.multiplerowlist = []
            self.table.multiplerowlist.append(rowover)
            self.drawSelectedRows(rowover)
            self.table.drawMultipleRows(self.table.multiplerowlist)
        return

    def drawSelectedRows(self, rows=None):
        """Draw selected rows, accepts a list or integer"""

        self.delete('rect')
        if type(rows) is not list:
            rowlist=[]
            rowlist.append(rows)
        else:
           rowlist = rows
        for r in rowlist:
            if r not in self.table.visiblerows:
                continue
            self.drawRect(r, delete=0)
        return

    def drawRect(self, row=None, tag=None, color=None, outline=None, delete=1):
        """Draw a rect representing row selection"""

        if tag==None:
            tag='rect'
        if color==None:
            color='#0099CC'
        if outline==None:
            outline='gray25'
        if delete == 1:
            self.delete(tag)
        w=0
        i = self.inset
        x1,y1,x2,y2 = self.table.getCellCoords(row, 0)
        rect = self.create_rectangle(0+i,y1+i,self.width-i,y2,
                                      fill=color,
                                      outline=outline,
                                      width=w,
                                      tag=tag)
        self.lift('text')
        return

Ancestors

  • tkinter.Canvas
  • tkinter.Widget
  • tkinter.BaseWidget
  • tkinter.Misc
  • tkinter.Pack
  • tkinter.Place
  • tkinter.Grid
  • tkinter.XView
  • tkinter.YView

Methods

def clearSelected(self)
Source code
def clearSelected(self):
    self.delete('rect')
    return
def drawRect(self, row=None, tag=None, color=None, outline=None, delete=1)

Draw a rect representing row selection

Source code
def drawRect(self, row=None, tag=None, color=None, outline=None, delete=1):
    """Draw a rect representing row selection"""

    if tag==None:
        tag='rect'
    if color==None:
        color='#0099CC'
    if outline==None:
        outline='gray25'
    if delete == 1:
        self.delete(tag)
    w=0
    i = self.inset
    x1,y1,x2,y2 = self.table.getCellCoords(row, 0)
    rect = self.create_rectangle(0+i,y1+i,self.width-i,y2,
                                  fill=color,
                                  outline=outline,
                                  width=w,
                                  tag=tag)
    self.lift('text')
    return
def drawSelectedRows(self, rows=None)

Draw selected rows, accepts a list or integer

Source code
def drawSelectedRows(self, rows=None):
    """Draw selected rows, accepts a list or integer"""

    self.delete('rect')
    if type(rows) is not list:
        rowlist=[]
        rowlist.append(rows)
    else:
       rowlist = rows
    for r in rowlist:
        if r not in self.table.visiblerows:
            continue
        self.drawRect(r, delete=0)
    return
def handle_left_click(self, event)
Source code
def handle_left_click(self, event):
    rowclicked = self.table.get_row_clicked(event)
    self.startrow = rowclicked
    if 0 <= rowclicked < self.table.rows:
        self.delete('rect')
        self.table.delete('entry')
        self.table.delete('multicellrect')
        #set row selected
        self.table.setSelectedRow(rowclicked)
        self.table.drawSelectedRow()
        self.drawSelectedRows(self.table.currentrow)
    return
def handle_left_ctrl_click(self, event)

Handle ctrl clicks - for multiple row selections

Source code
def handle_left_ctrl_click(self, event):
    """Handle ctrl clicks - for multiple row selections"""

    rowclicked = self.table.get_row_clicked(event)
    multirowlist = self.table.multiplerowlist
    if 0 <= rowclicked < self.table.rows:
        if rowclicked not in multirowlist:
            multirowlist.append(rowclicked)
        else:
            multirowlist.remove(rowclicked)
        self.table.drawMultipleRows(multirowlist)
        self.drawSelectedRows(multirowlist)
    return
def handle_left_release(self, event)
Source code
def handle_left_release(self,event):

    return
def handle_mouse_drag(self, event)

Handle mouse moved with button held down, multiple selections

Source code
def handle_mouse_drag(self, event):
    """Handle mouse moved with button held down, multiple selections"""

    if hasattr(self, 'cellentry'):
        self.cellentry.destroy()
    rowover = self.table.get_row_clicked(event)
    colover = self.table.get_col_clicked(event)
    if rowover == None:
        return
    if rowover >= self.table.rows or self.startrow > self.table.rows:
        return
    else:
        self.endrow = rowover
    #draw the selected rows
    if self.endrow != self.startrow:
        if self.endrow < self.startrow:
            rowlist=range(self.endrow, self.startrow+1)
        else:
            rowlist=range(self.startrow, self.endrow+1)
        self.drawSelectedRows(rowlist)
        self.table.multiplerowlist = rowlist
        self.table.drawMultipleRows(rowlist)
    else:
        self.table.multiplerowlist = []
        self.table.multiplerowlist.append(rowover)
        self.drawSelectedRows(rowover)
        self.table.drawMultipleRows(self.table.multiplerowlist)
    return
def handle_right_click(self, event)
Source code
def handle_right_click(self,event):

    return
def redraw(self, align='w', showkeys=False)

Redraw row header

Source code
def redraw(self, align='w', showkeys=False):
    """Redraw row header"""

    self.height = self.table.rowheight * self.table.rows+10
    self.configure(scrollregion=(0,0, self.width, self.height))
    self.delete('rowheader','text')
    self.delete('rect')
    w = float(self.width)
    h = self.table.rowheight
    x = self.x_start+w/2
    if align == 'w':
        x = x-w/2+3
    elif align == 'e':
        x = x+w/2-3
    for row in self.table.visiblerows:
        if showkeys == True:
            text = self.model.getRecName(row)
        else:
            text = row+1
        x1,y1,x2,y2 = self.table.getCellCoords(row,0)
        self.create_rectangle(0,y1,w-1,y2,
                                  fill='gray75',
                                  outline='white',
                                  width=1,
                                  tag='rowheader')
        self.create_text(x,y1+h/2,
                                  text=text,
                                  fill='black',
                                  font=self.table.thefont,
                                  tag='text', anchor=align)
    return
def setWidth(self, w)

Set width

Source code
def setWidth(self, w):
    """Set width"""
    self.width = w
    self.redraw()
    return
class TableCanvas (parent=None, model=None, data=None, read_only=False, width=None, height=None, rows=10, cols=5, **kwargs)

A tkinter class for providing table functionality

Construct a canvas widget with the parent MASTER.

Valid resource names: background, bd, bg, borderwidth, closeenough, confine, cursor, height, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, offset, relief, scrollregion, selectbackground, selectborderwidth, selectforeground, state, takefocus, width, xscrollcommand, xscrollincrement, yscrollcommand, yscrollincrement.

Source code
class TableCanvas(Canvas):
    """A tkinter class for providing table functionality"""

    def __init__(self, parent=None, model=None, data=None, read_only=False,
                 width=None, height=None,
                 rows=10, cols=5, **kwargs):
        Canvas.__init__(self, parent, bg='white',
                         width=width, height=height,
                         relief=GROOVE,
                         scrollregion=(0,0,300,200))
        self.parentframe = parent
        #get platform into a variable
        self.ostyp = self.checkOSType()
        self.platform = platform.system()
        self.width = width
        self.height = height
        self.set_defaults()

        self.currentpage = None
        self.navFrame = None
        self.currentrow = 0
        self.currentcol = 0
        self.reverseorder = 0
        self.startrow = self.endrow = None
        self.startcol = self.endcol = None
        self.allrows = False       #for selected all rows without setting multiplerowlist
        self.multiplerowlist=[]
        self.multiplecollist=[]
        self.col_positions=[]       #record current column grid positions
        self.mode = 'normal'
        self.read_only = read_only
        self.filtered = False

        self.loadPrefs()
        #set any options passed in kwargs to overwrite defaults and prefs
        for key in kwargs:
            self.__dict__[key] = kwargs[key]

        if data is not None:
            self.model = TableModel()
            self.model.importDict(data)
        elif model is not None:
            self.model = model
        else:
            self.model = TableModel(rows=rows,columns=cols)

        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()
        self.tablewidth = (self.cellwidth)*self.cols
        self.do_bindings()
        #initial sort order
        self.model.setSortOrder()

        #column specific actions, define for every column type in the model
        #when you add a column type you should edit this dict
        self.columnactions = {'text' : {"Edit":  'drawCellEntry' },
                              'number' : {"Edit": 'drawCellEntry' }}
        self.setFontSize()
        return

    def set_defaults(self):
        """Set default settings"""

        self.cellwidth=150
        self.maxcellwidth=200
        self.rowheight=20
        self.horizlines=1
        self.vertlines=1
        self.alternaterows=0
        self.autoresizecols = 0
        self.inset=2
        self.x_start=0
        self.y_start=1
        self.linewidth=1.0
        self.rowheaderwidth=40
        self.showkeynamesinheader=False
        self.thefont = ('Arial',12)
        self.cellbackgr = '#F7F7FA'
        self.entrybackgr = 'white'
        self.grid_color = '#ABB1AD'
        self.selectedcolor = 'yellow'
        self.rowselectedcolor = '#CCCCFF'
        self.multipleselectioncolor = '#ECD672'
        return

    def setFontSize(self):
        """Set font size to match font, we need to get rid of fontsize as
            a separate variable?"""

        if hasattr(self, 'thefont') and type(self.thefont) is tuple:
            self.fontsize = self.thefont[1]
        return

    def mouse_wheel(self, event):
        """Handle mouse wheel scroll for windows"""

        if event.num == 5 or event.delta == -120:
            event.widget.yview_scroll(1, UNITS)
            self.tablerowheader.yview_scroll(1, UNITS)
        if event.num == 4 or event.delta == 120:
            if self.canvasy(0) < 0:
                return
            event.widget.yview_scroll(-1, UNITS)
            self.tablerowheader.yview_scroll(-1, UNITS)
        self.redrawVisible()
        return

    def do_bindings(self):
        """Bind keys and mouse clicks, this can be overriden"""
        self.bind("<Button-1>",self.handle_left_click)
        self.bind("<Double-Button-1>",self.handle_double_click)
        self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
        self.bind("<Shift-Button-1>", self.handle_left_shift_click)

        self.bind("<ButtonRelease-1>", self.handle_left_release)
        if self.ostyp=='mac':
            #For mac we bind Shift, left-click to right click
            self.bind("<Button-2>", self.handle_right_click)
            self.bind('<Shift-Button-1>',self.handle_right_click)
        else:
            self.bind("<Button-3>", self.handle_right_click)

        self.bind('<B1-Motion>', self.handle_mouse_drag)
        self.bind('<Motion>', self.handle_motion)

        self.bind_all("<Control-x>", self.deleteRow)
        self.bind_all("<Control-n>", self.addRow)
        self.bind_all("<Delete>", self.clearData)
        self.bind_all("<Control-v>", self.paste)

        #if not hasattr(self,'parentapp'):
        #    self.parentapp = self.parentframe

        self.parentframe.master.bind_all("<Right>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Left>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Up>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Down>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<KP_8>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Return>", self.handle_arrow_keys)
        self.parentframe.master.bind_all("<Tab>", self.handle_arrow_keys)
        #if 'windows' in self.platform:
        self.bind("<MouseWheel>", self.mouse_wheel)
        self.bind('<Button-4>', self.mouse_wheel)
        self.bind('<Button-5>', self.mouse_wheel)
        self.focus_set()
        return

    def getModel(self):
        """Get the current table model"""
        return self.model

    def setModel(self, model):
        """Set a new model - requires redraw to reflect changes"""
        self.model = model
        return

    def createfromDict(self, data):
        """Attempt to create a new model/table from a dict"""

        try:
            namefield=self.namefield
        except:
            namefield=data.keys()[0]
        self.model = TableModel()
        self.model.importDict(data, namefield=namefield)
        self.model.setSortOrder(0,reverse=self.reverseorder)
        return

    def createTableFrame(self, callback=None):
        self.show(callback)
        return

    def show(self, callback=None):
        """Adds column header and scrollbars and combines them with
           the current table adding all to the master frame provided in constructor.
           Table is then redrawn."""

        #Add the table and header to the frame
        self.tablerowheader = RowHeader(self.parentframe, self, width=self.rowheaderwidth)
        self.tablecolheader = ColumnHeader(self.parentframe, self)
        self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews)
        self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0)
        self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews)
        self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news')
        self['xscrollcommand'] = self.Xscrollbar.set
        self['yscrollcommand'] = self.Yscrollbar.set
        self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set
        self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set
        self.parentframe.rowconfigure(1,weight=1)
        self.parentframe.columnconfigure(1,weight=1)

        self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0)
        self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0)
        self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0)

        self.adjustColumnWidths()
        self.redrawTable(callback=callback)
        self.parentframe.bind("<Configure>", self.redrawVisible)
        self.tablecolheader.xview("moveto", 0)
        self.xview("moveto", 0)
        return

    def getVisibleRegion(self):
        """Get visible region of table to display"""

        x1, y1 = self.canvasx(0), self.canvasy(0)
        #w, h = self.winfo_width(), self.winfo_height()
        w,h= self.master.winfo_width(), self.master.winfo_height()
        if w <= 1.0 or h <= 1.0:
            w, h = self.master.winfo_width(), self.master.winfo_height()
        x2, y2 = self.canvasx(w), self.canvasy(h)
        return x1, y1, x2, y2

    def getRowPosition(self, y):
        """Get current row from canvas position"""

        h = self.rowheight
        y_start = self.y_start
        row = (int(y)-y_start)/h
        if row < 0:
            return 0
        if row > self.rows:
            row = self.rows
        return row

    def getColPosition(self, x):
        """Get current col from canvas position"""

        x_start = self.x_start
        w = self.cellwidth
        i=0
        col=0
        for c in self.col_positions:
            col = i
            if c+w>=x:
                break
            i+=1
        return col

    def getVisibleRows(self, y1, y2):
        """Get the visible row range"""

        start = int(self.getRowPosition(y1))
        end = int(self.getRowPosition(y2))+1
        if end > self.rows:
            end = self.rows
        return start, end

    def getVisibleCols(self, x1, x2):
        """Get the visible column range"""

        start = self.getColPosition(x1)
        end = self.getColPosition(x2)+1
        if end > self.cols:
            end = self.cols
        return start, end

    def redrawVisible(self, event=None, callback=None):
        """Redraw the visible portion of the canvas"""

        model = self.model
        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()

        self.tablewidth = (self.cellwidth) * self.cols
        self.configure(bg=self.cellbackgr)
        self.setColPositions()

        #are we drawing a filtered subset of the recs?
        if self.filtered == True and self.model.filteredrecs != None:
            self.rows = len(self.model.filteredrecs)
            self.delete('colrect')

        self.rowrange = range(0,self.rows)
        self.configure(scrollregion=(0,0, self.tablewidth+self.x_start,
                self.rowheight*self.rows+10))

        x1, y1, x2, y2 = self.getVisibleRegion()
        startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2)
        self.visiblerows = range(startvisiblerow, endvisiblerow)
        startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2)
        self.visiblecols = range(startvisiblecol, endvisiblecol)

        if self.cols == 0 or self.rows == 0:
            self.delete('entry')
            self.delete('rowrect')
            self.delete('currentrect')
            self.delete('gridline','text')
            self.tablerowheader.redraw()
            return

        self.drawGrid(startvisiblerow, endvisiblerow)
        align = self.align
        self.delete('fillrect')
        for row in self.visiblerows:
            if callback != None:
                callback()
            for col in self.visiblecols:
                colname = model.getColumnName(col)
                bgcolor = model.getColorAt(row,col, 'bg')
                fgcolor = model.getColorAt(row,col, 'fg')
                text = model.getValueAt(row,col)
                self.drawText(row, col, text, fgcolor, align)
                if bgcolor != None:
                    self.drawRect(row,col, color=bgcolor)

        #self.drawSelectedCol()
        self.tablecolheader.redraw()
        self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader)
        #self.setSelectedRow(self.currentrow)
        self.drawSelectedRow()
        self.drawSelectedRect(self.currentrow, self.currentcol)
        #print self.multiplerowlist

        if len(self.multiplerowlist)>1:
            self.tablerowheader.drawSelectedRows(self.multiplerowlist)
            self.drawMultipleRows(self.multiplerowlist)
            self.drawMultipleCells()
        return

    def redrawTable(self, event=None, callback=None):
        self.redrawVisible(event, callback)
        return

    def redraw(self, event=None, callback=None):
        self.redrawVisible(event, callback)
        return

    def redrawCell(self, row=None, col=None, recname=None, colname=None):
        """Redraw a specific cell only"""

        if row == None and recname != None:
            row = self.model.getRecordIndex(recname)
        if col == None and colname != None:
            col = self.model.getColumnIndex(colname)
        bgcolor = self.model.getColorAt(row,col, 'bg')
        fgcolor = self.model.getColorAt(row,col, 'fg')
        text = self.model.getValueAt(row,col)
        self.drawText(row, col, text, fgcolor)
        if bgcolor != None:
            self.drawRect(row,col, color=bgcolor)
        return

    def adjustColumnWidths(self):
        """Optimally adjust col widths to accomodate the longest entry
            in each column - usually only called  on first redraw"""

        #self.cols = self.model.getColumnCount()
        try:
            fontsize = self.thefont[1]
        except:
            fontsize = self.fontsize
        scale = 8.5 * float(fontsize)/12
        for col in range(self.cols):
            colname = self.model.getColumnName(col)
            if colname in self.model.columnwidths:
                w = self.model.columnwidths[colname]
            else:
                w = self.cellwidth
            maxlen = self.model.getlongestEntry(col)
            size = maxlen * scale
            if size < w:
                continue
            #print col, size, self.cellwidth
            if size >= self.maxcellwidth:
                size = self.maxcellwidth
            self.model.columnwidths[colname] = size + float(fontsize)/12*6
        return

    def autoResizeColumns(self):
        """Automatically set nice column widths and draw"""

        self.adjustColumnWidths()
        self.redrawTable()
        return

    def setColPositions(self):
        """Determine current column grid positions"""

        self.col_positions=[]
        w=self.cellwidth
        x_pos=self.x_start
        self.col_positions.append(x_pos)
        for col in range(self.cols):
            colname=self.model.getColumnName(col)
            if colname in self.model.columnwidths:
                x_pos=x_pos+self.model.columnwidths[colname]
            else:
                x_pos=x_pos+w
            self.col_positions.append(x_pos)
        self.tablewidth = self.col_positions[len(self.col_positions)-1]
        return

    def sortTable(self, columnIndex=0, columnName=None, reverse=0):
        """Set up sort order dict based on currently selected field"""

        self.model.setSortOrder(columnIndex, columnName, reverse)
        self.redrawTable()
        return

    def set_xviews(self,*args):
        """Set the xview of table and col header"""

        self.xview(*args)
        self.tablecolheader.xview(*args)
        self.redrawVisible()
        return

    def set_yviews(self,*args):
        """Set the xview of table and row header"""

        self.yview(*args)
        self.tablerowheader.yview(*args)
        self.redrawVisible()
        return

    def addRow(self, key=None, **kwargs):
        """Add new row"""

        key = self.model.addRow(key, **kwargs)
        self.redrawTable()
        self.setSelectedRow(self.model.getRecordIndex(key))
        return

    def addRows(self, num=None):
        """Add new rows"""

        if num == None:
            num = simpledialog.askinteger("Now many rows?",
                                            "Number of rows:",initialvalue=1,
                                             parent=self.parentframe)
        if not num:
            return
        keys = self.model.autoAddRows(num)
        self.redrawTable()
        self.setSelectedRow(self.model.getRecordIndex(keys[0]))
        return

    def addColumn(self, newname=None):
        """Add a new column"""

        if newname == None:

            coltypes = self.getModel().getDefaultTypes()
            d = MultipleValDialog(title='New Column',
                                    initialvalues=(coltypes, ''),
                                    labels=('Column Type','Name'),
                                    types=('list','string'),
                                    parent=self.parentframe)
            if d.result == None:
                return
            else:
                coltype = d.results[0]
                newname = d.results[1]

        if newname != None:
            if newname in self.getModel().columnNames:
                messagebox.showwarning("Name exists",
                                         "Name already exists!",
                                         parent=self.parentframe)
            else:
                self.model.addColumn(newname)
                self.parentframe.configure(width=self.width)
                self.redrawTable()
        return

    def deleteRow(self):
        """Delete a row"""

        if len(self.multiplerowlist)>1:
            n = messagebox.askyesno("Delete",
                                      "Delete Selected Records?",
                                      parent=self.parentframe)
            if n == True:
                rows = self.multiplerowlist
                self.model.deleteRows(rows)
                self.clearSelected()
                self.setSelectedRow(0)
                self.redrawTable()
        else:
            n = messagebox.askyesno("Delete",
                                      "Delete This Record?",
                                      parent=self.parentframe)
            if n:
                row = self.getSelectedRow()
                self.model.deleteRow(row)
                self.setSelectedRow(row-1)
                self.clearSelected()
                self.redrawTable()
        return

    def deleteColumn(self):
        """Delete currently selected column"""

        n =  messagebox.askyesno("Delete",
                                   "Delete This Column?",
                                   parent=self.parentframe)
        if n:
            col = self.getSelectedColumn()
            self.model.deleteColumn(col)
            self.currentcol = self.currentcol - 1
            self.redrawTable()
        return

    def deleteCells(self, rows, cols):
        """Clear the cell contents"""

        n =  messagebox.askyesno("Clear Confirm",
                                   "Clear this data?",
                                   parent=self.parentframe)
        if not n:
            return
        for col in cols:
            for row in rows:
                #absrow = self.get_AbsoluteRow(row)
                self.model.deleteCellRecord(row, col)
                self.redrawCell(row,col)
        return

    def clearData(self, evt=None):
        """Delete cells from gui event"""

        rows = self.multiplerowlist
        cols = self.multiplecollist
        self.deleteCells(rows, cols)
        return

    def autoAddColumns(self, numcols=None):
        """Automatically add x number of cols"""

        if numcols == None:
            numcols = simpledialog.askinteger("Auto add rows.",
                                                "How many empty columns?",
                                                parent=self.parentframe)
        self.model.auto_AddColumns(numcols)
        self.parentframe.configure(width=self.width)
        self.redrawTable()
        return

    def getRecordInfo(self, row):
        """Show the record for this row"""

        model = self.model
        #We need a custom dialog for allowing field entries here
        #absrow = self.get_AbsoluteRow(row)
        d = RecordViewDialog(title="Record Details",
                                  parent=self.parentframe, table=self, row=row)
        return

    def findValue(self, searchstring=None, findagain=None):
        """Return the row/col for the input value"""

        if searchstring == None:
            searchstring = simpledialog.askstring("Search table.",
                                               "Enter search value",
                                               parent=self.parentframe)
        found=0
        if findagain == None or not hasattr(self,'foundlist'):
            self.foundlist=[]
        if self.model!=None:
            for row in range(self.rows):
                for col in range(self.cols):
                    text = str(self.model.getValueAt(row,col))
                    if text=='' or text==None:
                        continue
                    cell=row,col
                    if findagain == 1 and cell in self.foundlist:
                        continue
                    if text.lower().find(searchstring.lower())!=-1:
                        print ('found in',row,col)
                        found=1
                        #highlight cell
                        self.delete('searchrect')
                        self.drawRect(row, col, color='red', tag='searchrect', delete=0)
                        self.lift('searchrect')
                        self.lift('celltext'+str(col)+'_'+str(row))
                        #add row/col to foundlist
                        self.foundlist.append(cell)
                        #need to scroll to centre the cell here..
                        x,y = self.getCanvasPos(row, col)
                        self.xview('moveto', x)
                        self.yview('moveto', y)
                        self.tablecolheader.xview('moveto', x)
                        self.tablerowheader.yview('moveto', y)
                        return row, col
        if found==0:
            self.delete('searchrect')
            print ('nothing found')
            return None

    def showAll(self):
        self.model.filteredrecs = None
        self.filtered = False
        self.redrawTable()
        return

    def doFilter(self, event=None):
        """Filter the table display by some column values.
        We simply pass the model search function to the the filtering
        class and that handles everything else.
        See filtering frame class for how searching is done.
        """
        if self.model==None:
            return
        names = self.filterframe.doFiltering(searchfunc=self.model.filterBy)
        #create a list of filtered recs
        self.model.filteredrecs = names
        self.filtered = True
        self.redrawTable()
        return

    def createFilteringBar(self, parent=None, fields=None):
        """Add a filter frame"""

        if parent == None:
            parent = Toplevel()
            parent.title('Filter Records')
            x,y,w,h = self.getGeometry(self.master)
            parent.geometry('+%s+%s' %(x,y+h))
        if fields == None:
            fields = self.model.columnNames
        from .Filtering import FilterFrame
        self.filterframe = FilterFrame(parent, fields,
                                       self.doFilter, self.closeFilterFrame)
        self.filterframe.pack()
        return parent

    def showFilteringBar(self):
        if not hasattr(self, 'filterwin') or self.filterwin == None:
            self.filterwin = self.createFilteringBar()
            self.filterwin.protocol("WM_DELETE_WINDOW", self.closeFilterFrame)
        else:
            self.filterwin.lift()
        return

    def closeFilterFrame(self):
        """Callback for closing filter frame"""
        self.filterwin.destroy()
        self.filterwin = None
        self.showAll()
        return

    def resizeColumn(self, col, width):
        """Resize a column by dragging"""

        #print 'resizing column', col
        #recalculate all col positions..
        colname=self.model.getColumnName(col)
        self.model.columnwidths[colname]=width
        self.setColPositions()
        self.redrawTable()
        self.drawSelectedCol(self.currentcol)
        return

    def get_currentRecord(self):
        """Get the currently selected record"""

        rec = self.model.getRecordAtRow(self.currentrow)
        return rec

    def get_currentColName(self):
        """Get the currently selected record name"""

        colname = self.mo(self.currentcol)
        return colname

    def get_currentRecordName(self):
        """Get the currently selected record name"""

        recname = self.model.getRecName(self.currentrow)
        return recname

    def get_selectedRecordNames(self):
        """Get a list of the current multiple selection, if any"""

        recnames=[]
        for row in self.multiplerowlist:
            recnames.append(self.model.getRecName(row))
        return recnames

    def get_currentRecCol(self):
        """Get the clicked rec and col names as a tuple"""

        recname = self.get_currentRecordName()
        colname = self.get_currentColName()
        return (recname, colname)

    def get_row_clicked(self, event):
        """get row where event on canvas occurs"""

        h=self.rowheight
        #get coord on canvas, not window, need this if scrolling
        y = int(self.canvasy(event.y))
        y_start=self.y_start
        rowc = int((int(y)-y_start)/h)
        #rowc = math.floor(rowc)
        #print 'event.y',event.y, 'y',y
        #print 'rowclicked', rowc
        return rowc

    def get_col_clicked(self,event):
        """get col where event on canvas occurs"""

        w=self.cellwidth
        x = int(self.canvasx(event.x))
        x_start=self.x_start
        #print self.col_positions
        for colpos in self.col_positions:
            try:
                nextpos=self.col_positions[self.col_positions.index(colpos)+1]
            except:
                nextpos=self.tablewidth
            if x > colpos and x <= nextpos:
                #print 'x=', x, 'colpos', colpos, self.col_positions.index(colpos)
                return self.col_positions.index(colpos)
            else:
                #print None
                pass
        #return colc

    def setSelectedRow(self, row):
        """Set currently selected row and reset multiple row list"""

        self.currentrow = row
        self.multiplerowlist = []
        self.multiplerowlist.append(row)
        return

    def setSelectedCol(self, col):
        """Set currently selected column"""

        self.currentcol = col
        self.multiplecollist = []
        self.multiplecollist.append(col)
        return

    def setSelectedCells(self, startrow, endrow, startcol, endcol):
        """Set a block of cells selected"""

        self.currentrow = startrow
        self.currentcol = startcol
        if startrow < 0 or startcol < 0:
            return
        if endrow > self.rows or endcol > self.cols:
            return
        for r in range(startrow, endrow):
            self.multiplerowlist.append(r)
        for c in range(startcol, endcol):
            self.multiplecollist.append(c)
        return

    def getSelectedRow(self):
        """Get currently selected row"""
        return self.currentrow

    def getSelectedColumn(self):
        """Get currently selected column"""
        return self.currentcol

    def select_All(self):
        """Select all rows and cells"""

        self.startrow = 0
        self.endrow = self.rows
        self.multiplerowlist = range(self.startrow,self.endrow)
        self.drawMultipleRows(self.multiplerowlist)
        self.startcol = 0
        self.endcol = self.cols
        self.multiplecollist = range(self.startcol, self.endcol)
        self.drawMultipleCells()
        return

    def getCellCoords(self, row, col):
        """Get x-y coordinates to drawing a cell in a given row/col"""
        colname=self.model.getColumnName(col)
        if colname in self.model.columnwidths:
            w=self.model.columnwidths[colname]
        else:
            w=self.cellwidth
        h=self.rowheight
        x_start=self.x_start
        y_start=self.y_start

        #get nearest rect co-ords for that row/col
        #x1=x_start+w*col
        x1=self.col_positions[col]
        y1=y_start+h*row
        x2=x1+w
        y2=y1+h
        return x1,y1,x2,y2

    def getCanvasPos(self, row, col):
        """Get the cell x-y coords as a fraction of canvas size"""
        if self.rows==0:
            return None, None
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        cx=float(x1)/self.tablewidth
        cy=float(y1)/(self.rows*self.rowheight)
        return cx, cy

    def isInsideTable(self,x,y):
        """Returns true if x-y coord is inside table bounds"""
        if self.x_start < x < self.tablewidth and self.y_start < y < self.rows*self.rowheight:
            return 1
        else:
            return 0
        return answer

    def setRowHeight(self, h):
        """Set the row height"""
        self.rowheight = h
        return

    def clearSelected(self):
        self.delete('rect')
        self.delete('entry')
        self.delete('tooltip')
        self.delete('searchrect')
        self.delete('colrect')
        self.delete('multicellrect')

        #self.delete('formulabox')
        return

    def gotoprevRow(self):
        """Programmatically set previous row - eg. for button events"""
        self.clearSelected()
        current = self.getSelectedRow()
        self.setSelectedRow(current-1)
        self.startrow = current-1
        self.endrow = current-1
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(self.currentrow)
        self.drawSelectedRect(self.currentrow, self.currentcol)
        self.drawSelectedRow()
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def gotonextRow(self):
        """Programmatically set next row - eg. for button events"""
        self.clearSelected()
        current = self.getSelectedRow()
        self.setSelectedRow(current+1)
        self.startrow = current+1
        self.endrow = current+1
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(self.currentrow)
        self.drawSelectedRect(self.currentrow, self.currentcol)
        self.drawSelectedRow()
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def handle_left_click(self, event):
        """Respond to a single press"""

        #which row and column is the click inside?
        self.clearSelected()
        self.allrows = False
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        self.focus_set()
        if self.mode == 'formula':
            self.handleFormulaClick(rowclicked, colclicked)
            return
        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        #ensure popup menus are removed if present
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        if hasattr(self.tablecolheader, 'rightmenu'):
            self.tablecolheader.rightmenu.destroy()

        self.startrow = rowclicked
        self.endrow = rowclicked
        self.startcol = colclicked
        self.endcol = colclicked
        #reset multiple selection list
        self.multiplerowlist=[]
        self.multiplerowlist.append(rowclicked)
        if rowclicked is None or colclicked is None:
            return
        if self.read_only is True:    
            return
        if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
            self.setSelectedRow(rowclicked)
            self.setSelectedCol(colclicked)
            self.drawSelectedRect(self.currentrow, self.currentcol)
            self.drawSelectedRow()
            self.tablerowheader.drawSelectedRows(rowclicked)
            coltype = self.model.getColumnType(colclicked)
            if coltype == 'text' or coltype == 'number':
                self.drawCellEntry(rowclicked, colclicked)
        return

    def handle_left_release(self,event):
        self.endrow = self.get_row_clicked(event)
        return

    def handle_left_ctrl_click(self, event):
        """Handle ctrl clicks for multiple row selections"""
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
            if rowclicked not in self.multiplerowlist:
                self.multiplerowlist.append(rowclicked)
            else:
                self.multiplerowlist.remove(rowclicked)
            self.drawMultipleRows(self.multiplerowlist)
            if colclicked not in self.multiplecollist:
                self.multiplecollist.append(colclicked)
            #print self.multiplecollist
            self.drawMultipleCells()
        return

    def handle_left_shift_click(self, event):
        """Handle shift click, for selecting multiple rows"""
        #Has same effect as mouse drag, so just use same method
        self.handle_mouse_drag(event)
        return

    def handle_mouse_drag(self, event):
        """Handle mouse moved with button held down, multiple selections"""

        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        rowover = self.get_row_clicked(event)
        colover = self.get_col_clicked(event)
        if colover == None or rowover == None:
            return

        if rowover >= self.rows or self.startrow > self.rows:
            return
        else:
            self.endrow = rowover
        #do columns
        if colover > self.cols or self.startcol > self.cols:
            return
        else:
            self.endcol = colover
            if self.startcol is None or self.endcol is None:
                return
            if self.endcol < self.startcol:
                self.multiplecollist = range(self.endcol, self.startcol+1)
            else:
                self.multiplecollist = range(self.startcol, self.endcol+1)
            #print self.multiplecollist
        #draw the selected rows
        if self.endrow != self.startrow:
            if self.endrow < self.startrow:
                self.multiplerowlist = range(self.endrow, self.startrow+1)
            else:
                self.multiplerowlist = range(self.startrow, self.endrow+1)
            self.drawMultipleRows(self.multiplerowlist)
            self.tablerowheader.drawSelectedRows(self.multiplerowlist)
            #draw selected cells outline using row and col lists
            #print self.multiplerowlist
            self.drawMultipleCells()
        else:
            self.multiplerowlist = []
            self.multiplerowlist.append(self.currentrow)
            if len(self.multiplecollist) >= 1:
                self.drawMultipleCells()
            self.delete('multiplesel')
        #print self.multiplerowlist
        return

    def handle_arrow_keys(self, event):
        """Handle arrow keys press"""
        #print event.keysym

        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        x,y = self.getCanvasPos(self.currentrow, 0)
        if x == None:
            return

        if event.keysym == 'Up':
            if self.currentrow == 0:
                return
            else:
                self.currentrow  = self.currentrow -1
        elif event.keysym == 'Down':
            if self.currentrow >= self.rows-1:
                return
            else:
                self.currentrow  = self.currentrow +1
        elif event.keysym == 'Right' or event.keysym == 'Tab':
            if self.currentcol >= self.cols-1:
                if self.currentrow < self.rows-1:
                    self.currentcol = 0
                    self.currentrow  = self.currentrow +1
                else:
                    return
            else:
                self.currentcol  = self.currentcol +1
        elif event.keysym == 'Left':
            if self.currentcol == 0:
                if self.currentrow == 0:
                    return
                else:
                    self.currentcol = self.cols-1
                    self.currentrow = self.currentrow - 1
            else:
                self.currentcol  = self.currentcol -1
        self.drawSelectedRect(self.currentrow, self.currentcol)
        coltype = self.model.getColumnType(self.currentcol)
        if coltype == 'text' or coltype == 'number':
            self.delete('entry')
            self.drawCellEntry(self.currentrow, self.currentcol)
        return

    def handle_double_click(self, event):
        """Do double click stuff. Selected row/cols will already have
           been set with single click binding"""

        '''row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        model=self.getModel()
        cellvalue = model.getCellRecord(row, col)
        if Formula.isFormula(cellvalue):
            self.formula_Dialog(row, col, cellvalue)'''

        return

    def handle_right_click(self, event):
        """respond to a right click"""

        if self.read_only is True:
            return
        self.delete('tooltip')
        self.tablerowheader.clearSelected()
        if hasattr(self, 'rightmenu'):
            self.rightmenu.destroy()
        rowclicked = self.get_row_clicked(event)
        colclicked = self.get_col_clicked(event)
        if colclicked == None:
            self.rightmenu = self.popupMenu(event, outside=1)
            return

        if (rowclicked in self.multiplerowlist or self.allrows == True) and colclicked in self.multiplecollist:
            self.rightmenu = self.popupMenu(event, rows=self.multiplerowlist, cols=self.multiplecollist)
        else:
            if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
                self.clearSelected()
                self.allrows = False
                self.setSelectedRow(rowclicked)
                self.setSelectedCol(colclicked)
                self.drawSelectedRect(self.currentrow, self.currentcol)
                self.drawSelectedRow()
            if self.isInsideTable(event.x,event.y) == 1:
                self.rightmenu = self.popupMenu(event,rows=self.multiplerowlist, cols=self.multiplecollist)
            else:
                self.rightmenu = self.popupMenu(event, outside=1)
        return

    def handle_motion(self, event):
        """Handle mouse motion on table"""

        self.delete('tooltip')
        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        if row == None or col == None:
            return
        if 0 <= row < self.rows and 0 <= col < self.cols:
            self.drawTooltip(row, col)

        return

    def gotonextCell(self, event):
        """Move highlighted cell to next cell in row or a new col"""
        #print 'next'
        if hasattr(self, 'cellentry'):
            self.cellentry.destroy()
        self.currentcol=self.currentcol+1
        if self.currentcol >= self.cols-1:
            self.currentrow  = self.currentrow +1
            self.currentcol = self.currentcol+1
        self.drawSelectedRect(self.currentrow, self.currentcol)
        return

    def movetoSelectedRow(self, row=None, recname=None):
        """Move to selected row, updating table"""
        row=self.model.getRecordIndex(recname)
        self.setSelectedRow(row)
        self.drawSelectedRow()
        x,y = self.getCanvasPos(row, 0)
        self.yview('moveto', y-0.01)
        self.tablecolheader.yview('moveto', y)
        return

    def handleFormulaClick(self, row, col):
        """Do a dialog for cell formula entry"""

        model = self.getModel()
        cell = list(model.getRecColNames(row, col))
        #absrow = self.get_AbsoluteRow(row)
        self.formulaText.insert(END, str(cell))
        self.formulaText.focus_set()
        self.drawSelectedRect(row, col, color='red')
        return

    def formula_Dialog(self, row, col, currformula=None):
        """Formula dialog"""
        self.mode = 'formula'
        print (self.mode)
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=300
        h=h=self.rowheight * 3
        def close():
            if hasattr(self,'formulaWin'):
                self.delete('formulabox')
            self.mode = 'normal'
        def calculate():
            #get text area contents and do formula
            f = self.formulaText.get(1.0, END)
            f = f.strip('\n')
            self.model.setFormulaAt(f,row,col)
            value = self.model.doFormula(f)
            color = self.model.getColorAt(row,col,'fg')
            self.drawText(row, col, value, color)
            close()
            self.mode = 'normal'
            return
        def clear():
            self.formulaText.delete(1.0, END)

        self.formulaFrame = Frame(width=w,height=h,bd=3)
        self.formulaText = Text(self.formulaFrame, width=30, height=8, bg='white',relief=GROOVE)
        self.formulaText.pack(side=LEFT,padx=2,pady=2)
        if currformula != None:
            self.formulaText.insert(END, Formula.getFormula(currformula))
        cancelbutton=Button(self.formulaFrame, text='Cancel',
                            bg='#99ccff',command=close)
        cancelbutton.pack(fill=BOTH,padx=2,pady=2)
        donebutton=Button(self.formulaFrame, text='Done',
                          bg='#99ccff',command=calculate)
        donebutton.pack(fill=BOTH,padx=2,pady=2)
        '''clrbutton=Button(self.formulaFrame, text='Clear',
                           bg='#99ccff',command=clear)
        clrbutton.pack(fill=BOTH,padx=2,pady=2) '''
        #add to canvas
        self.formulaWin = self.create_window(x1+self.inset,y1+self.inset,
                                width=w,height=h,
                                window=self.formulaFrame,anchor='nw',
                                tag='formulabox')
        self.formulaText.focus_set()
        return

    def convertFormulae(self, rows, cols=None):
        """Convert the formulas in the cells to their result values"""

        if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
            return None

        print (rows, cols)
        if cols == None:
            cols = range(self.cols)
        for r in rows:
            #absr=self.get_AbsoluteRow(r)
            for c in cols:
                val = self.model.getValueAt(r,c)
                self.model.setValueAt(val, r, c)
        return

    def paste(self, event=None):
        """Copy from clipboard"""

        print (self.parentframe.clipboard_get())
        return

    def copyCell(self, rows, cols=None):
        """Copy cell contents to a temp internal clipboard"""

        row = rows[0]; col = cols[0]
        #absrow = self.get_AbsoluteRow(row)
        self.clipboard = copy.deepcopy(self.model.getCellRecord(row, col))
        return

    def pasteCell(self, rows, cols=None):
        """Paste cell from internal clipboard"""

        row = rows[0]; col = cols[0]
        #absrow = self.get_AbsoluteRow(row)
        val = self.clipboard
        self.model.setValueAt(val, row, col)
        self.redrawTable()
        return

    def copyColumns(self):
        """Copy current selected cols"""

        M = self.model
        coldata = {}
        for col in self.multiplecollist:
            name = M.columnNames[col]
            coldata[name] = M.getColumnData(columnName=name)
        return coldata

    def pasteColumns(self, coldata):
        """Paste new cols, overwrites existing names"""

        M = self.model
        for name in coldata:
            if name not in M.columnNames:
                M.addColumn(name)
            for r in range(len(coldata[name])):
                val = coldata[name][r]
                col = M.columnNames.index(name)
                if r >= self.rows:
                    break
                M.setValueAt(val, r, col)
        self.redrawTable()
        return coldata

    # --- Some cell specific actions here ---

    def setcellColor(self, rows, cols=None, newColor=None, key=None, redraw=True):
        """Set the cell color for one or more cells and save it in the model color"""

        model = self.getModel()
        if newColor == None:
            ctuple, newColor = tkColorChooser.askcolor(title='pick a color')
            if newColor == None:
                return

        if type(rows) is int:
            x=rows
            rows=[]
            rows.append(x)
        if self.allrows == True:
            #we use all rows if the whole column has been selected
            rows = range(0,self.rows)
        if cols == None:
            cols = range(self.cols)
        for col in cols:
            for row in rows:
                #absrow = self.get_AbsoluteRow(row)
                model.setColorAt(row, col, color=newColor, key=key)
                #setcolor(absrow, col)
        if redraw == True:
            self.redrawTable()
        return

    def popupMenu(self, event, rows=None, cols=None, outside=None):
        """Add left and right click behaviour for canvas, should not have to override
            this function, it will take its values from defined dicts in constructor"""

        defaultactions = {"Set Fill Color" : lambda : self.setcellColor(rows,cols,key='bg'),
                        "Set Text Color" : lambda : self.setcellColor(rows,cols,key='fg'),
                        "Copy" : lambda : self.copyCell(rows, cols),
                        "Paste" : lambda : self.pasteCell(rows, cols),
                        "Fill Down" : lambda : self.fillDown(rows, cols),
                        "Fill Right" : lambda : self.fillAcross(cols, rows),
                        "Add Row(s)" : lambda : self.addRows(),
                        "Delete Row(s)" : lambda : self.deleteRow(),
                        "View Record" : lambda : self.getRecordInfo(row),
                        "Clear Data" : lambda : self.deleteCells(rows, cols),
                        "Select All" : self.select_All,
                        "Auto Fit Columns" : self.autoResizeColumns,
                        "Filter Records" : self.showFilteringBar,
                        "New": self.new,
                        "Load": self.load,
                        "Save": self.save,
                        "Import text":self.importTable,
                        "Export csv": self.exportTable,
                        "Plot Selected" : self.plotSelected,
                        "Plot Options" : self.plotSetup,
                        "Export Table" : self.exportTable,
                        "Preferences" : self.showtablePrefs,
                        "Formulae->Value" : lambda : self.convertFormulae(rows, cols)}

        main = ["Set Fill Color","Set Text Color","Copy", "Paste", "Fill Down","Fill Right",
                "Clear Data"]
        general = ["Select All", "Add Row(s)" , "Delete Row(s)", "Auto Fit Columns", "Filter Records", "Preferences"]
        filecommands = ['New','Load','Save','Import text','Export csv']
        plotcommands = ['Plot Selected','Plot Options']

        def createSubMenu(parent, label, commands):
            menu = Menu(parent, tearoff = 0)
            popupmenu.add_cascade(label=label,menu=menu)
            for action in commands:
                menu.add_command(label=action, command=defaultactions[action])
            return menu

        def add_commands(fieldtype):
            """Add commands to popup menu for column type and specific cell"""
            functions = self.columnactions[fieldtype]
            for f in functions.keys():
                func = getattr(self, functions[f])
                popupmenu.add_command(label=f, command= lambda : func(row,col))
            return

        popupmenu = Menu(self, tearoff = 0)
        def popupFocusOut(event):
            popupmenu.unpost()

        if outside == None:
            #if outside table, just show general items
            row = self.get_row_clicked(event)
            col = self.get_col_clicked(event)
            coltype = self.model.getColumnType(col)
            def add_defaultcommands():
                """now add general actions for all cells"""
                for action in main:
                    if action == 'Fill Down' and (rows == None or len(rows) <= 1):
                        continue
                    if action == 'Fill Right' and (cols == None or len(cols) <= 1):
                        continue
                    else:
                        popupmenu.add_command(label=action, command=defaultactions[action])
                return

            if coltype in self.columnactions:
                add_commands(coltype)
            add_defaultcommands()

        for action in general:
            popupmenu.add_command(label=action, command=defaultactions[action])

        popupmenu.add_separator()
        createSubMenu(popupmenu, 'File', filecommands)
        createSubMenu(popupmenu, 'Plot', plotcommands)
        popupmenu.bind("<FocusOut>", popupFocusOut)
        popupmenu.focus_set()
        popupmenu.post(event.x_root, event.y_root)
        return popupmenu

    # --- spreadsheet type functions ---

    def fillDown(self, rowlist, collist):
        """Fill down a column, or multiple columns"""
        model = self.model
        #absrow  = self.get_AbsoluteRow(rowlist[0])
        #remove first element as we don't want to overwrite it
        rowlist.remove(rowlist[0])

        #if this is a formula, we have to treat it specially
        for col in collist:
            val = self.model.getCellRecord(row, col)
            f=val #formula to copy
            i=1
            for r in rowlist:
                #absr = self.get_AbsoluteRow(r)
                if Formula.isFormula(f):
                    newval = model.copyFormula(f, r, col, offset=i)
                    model.setFormulaAt(newval, r, col)
                else:
                    model.setValueAt(val, r, col)
                #print 'setting', val, 'at row', r
                i+=1

        self.redrawTable()
        return

    def fillAcross(self, collist, rowlist):
        """Fill across a row, or multiple rows"""
        model = self.model
        #row = self.currentrow
        #absrow  = self.get_AbsoluteRow(collist[0])
        frstcol = collist[0]
        collist.remove(frstcol)

        for row in rowlist:
            #absr = self.get_AbsoluteRow(row)
            val = self.model.getCellRecord(absr, frstcol)
            f=val     #formula to copy
            i=1
            for c in collist:
                if Formula.isFormula(f):
                    newval = model.copyFormula(f, r, c, offset=i, dim='x')
                    model.setFormulaAt(newval, r, c)
                else:
                    model.setValueAt(val, r, c)
                i+=1
        self.redrawTable()
        return

    def getSelectionValues(self):
        """Get values for current multiple cell selection"""
        if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
            return None
        rows = self.multiplerowlist
        cols = self.multiplecollist
        model = self.model
        if len(rows)<1 or len(cols)<1:
            return None
        #if only one row selected we plot whole col
        if len(rows) == 1:
            rows = self.rowrange
        lists = []

        for c in cols:
            x=[]
            for r in rows:
                #absr = self.get_AbsoluteRow(r)
                val = model.getValueAt(r,c)
                if val == None or val == '':
                    continue
                x.append(val)
            lists.append(x)
        return lists

    def plotSelected(self, graphtype='XY'):
        """Plot the selected data using pylab - if possible"""

        from .Plot import pylabPlotter
        if not hasattr(self, 'pyplot'):
            self.pyplot = pylabPlotter()
        plotdata = []
        for p in self.getSelectionValues():
            x = []
            fail = False
            for d in p:
                try:
                    x.append(float(d))
                except:
                    fail = True
                    continue
            if fail == False:
                plotdata.append(x)

        pltlabels = self.getplotlabels()
        if len(pltlabels) > 2:
            self.pyplot.setDataSeries(pltlabels)
            self.pyplot.showlegend = 1
        self.pyplot.plotCurrent(data=plotdata, graphtype=graphtype)
        return

    def plotSetup(self):
        """Call pylab plot dialog setup, send data if we haven't already
            plotted"""

        from PylabPlot import pylabPlotter
        if not hasattr(self, 'pyplot'):
            self.pyplot = pylabPlotter()
        plotdata = self.getSelectionValues()
        if not self.pyplot.hasData() and plotdata != None:
            print ('has data')
            plotdata = self.getSelectionValues()
            pltlabels = self.getplotlabels()
            self.pyplot.setDataSeries(pltlabels)
            self.pyplot.plotSetup(plotdata)
        else:
            self.pyplot.plotSetup()
        return

    def getplotlabels(self):
        """Get labels for plot series from col labels"""
        pltlabels = []
        for col in self.multiplecollist:
            pltlabels.append(self.model.getColumnLabel(col))
        return pltlabels

    #--- Drawing stuff ---

    def drawGrid(self, startrow, endrow):
        """Draw the table grid lines"""
        self.delete('gridline','text')
        rows=len(self.rowrange)
        cols=self.cols
        w = self.cellwidth
        h = self.rowheight
        x_start=self.x_start
        y_start=self.y_start
        x_pos=x_start

        if self.vertlines==1:
            for col in range(cols+1):
                x=self.col_positions[col]
                self.create_line(x,y_start,x,y_start+rows*h, tag='gridline',
                                     fill=self.grid_color, width=self.linewidth)
        if self.horizlines==1:
            for row in range(startrow, endrow+1):
                y_pos=y_start+row*h
                self.create_line(x_start,y_pos,self.tablewidth,y_pos, tag='gridline',
                                    fill=self.grid_color, width=self.linewidth)
        return

    def drawRowHeader(self):
        """User has clicked to select a cell"""
        self.delete('rowheader')
        x_start=self.x_start
        y_start=self.y_start
        h=self.rowheight
        rowpos=0
        for row in self.rowrange:
            x1,y1,x2,y2 = self.getCellCoords(rowpos,0)
            self.create_rectangle(0,y1,x_start-2,y2,
                                      fill='gray75',
                                      outline='white',
                                      width=1,
                                      tag='rowheader')
            self.create_text(x_start/2,y1+h/2,
                                      text=row+1,
                                      fill='black',
                                      font=self.thefont,
                                      tag='rowheader')
            rowpos+=1
        return

    def drawSelectedRect(self, row, col, color=None):
        """User has clicked to select a cell"""
        if col >= self.cols:
            return
        self.delete('currentrect')
        bg = self.selectedcolor
        if color == None:
            color = 'gray25'
        w=3
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                                  fill=bg,
                                  outline=color,
                                  width=w,
                                  stipple='gray50',
                                  tag='currentrect')
        #self.lower('currentrect')
        #raise text above all
        self.lift('celltext'+str(col)+'_'+str(row))
        return

    def drawRect(self, row, col, color=None, tag=None, delete=1):
        """Cell is colored"""
        if delete==1:
            self.delete('cellbg'+str(row)+str(col))
        if color==None or color==self.cellbackgr:
            return
        else:
            bg=color
        if tag==None:
            recttag='fillrect'
        else:
            recttag=tag
        w=1
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                                  fill=bg,
                                  outline=bg,
                                  width=w,
                                  tag=(recttag,'cellbg'+str(row)+str(col)))
        self.lower(recttag)
        return

    def drawCellEntry(self, row, col, text=None):
        """When the user single/double clicks on a text/number cell, bring up entry window"""

        if self.read_only == True:
            return
        #absrow = self.get_AbsoluteRow(row)
        h=self.rowheight
        model=self.getModel()
        cellvalue = self.model.getCellRecord(row, col)
        if Formula.isFormula(cellvalue):
            return
        else:
            text = self.model.getValueAt(row, col)
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        #Draw an entry window
        txtvar = StringVar()
        txtvar.set(text)
        def callback(e):
            value = txtvar.get()
            if value == '=':
                #do a dialog that gets the formula into a text area
                #then they can click on the cells they want
                #when done the user presses ok and its entered into the cell
                self.cellentry.destroy()
                #its all done here..
                self.formula_Dialog(row, col)
                return

            coltype = self.model.getColumnType(col)
            if coltype == 'number':
                sta = self.checkDataEntry(e)
                if sta == 1:
                    model.setValueAt(value,row,col)
            elif coltype == 'text':
                model.setValueAt(value,row,col)

            color = self.model.getColorAt(row,col,'fg')
            self.drawText(row, col, value, color, align=self.align)
            if e.keysym=='Return':
                self.delete('entry')
                #self.drawRect(row, col)
                #self.gotonextCell(e)
            return

        self.cellentry=Entry(self.parentframe,width=20,
                        textvariable=txtvar,
                        #bg=self.entrybackgr,
                        #relief=FLAT,
                        takefocus=1,
                        font=self.thefont)
        self.cellentry.icursor(END)
        self.cellentry.bind('<Return>', callback)
        self.cellentry.bind('<KeyRelease>', callback)
        self.cellentry.focus_set()
        self.entrywin=self.create_window(x1+self.inset,y1+self.inset,
                                width=w-self.inset*2,height=h-self.inset*2,
                                window=self.cellentry,anchor='nw',
                                tag='entry')

        return

    def checkDataEntry(self,event=None):
        """do validation checks on data entry in a widget"""
        #if user enters commas, change to points
        import re
        value=event.widget.get()
        if value!='':
            try:
                value=re.sub(',','.', value)
                value=float(value)

            except ValueError:
                event.widget.configure(bg='red')
                return 0
        elif value == '':
            return 1
        return 1

    def drawText(self, row, col, celltxt, fgcolor=None, align=None):
        """Draw the text inside a cell area"""

        self.delete('celltext'+str(col)+'_'+str(row))
        h=self.rowheight
        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        wrap = False
        pad=5
        # If celltxt is a number then we make it a string
        if type(celltxt) is float or type(celltxt) is int:
            celltxt=str(celltxt)
        length = len(celltxt)
        if length == 0:
            return
        #if cell width is less than x, print nothing
        if w<=10:
            return

        if fgcolor == None or fgcolor == "None":
            fgcolor = 'black'
        if align == None:
            align = 'w'
        if align == 'w':
            x1 = x1-w/2+pad
        elif align == 'e':
            x1 = x1+w/2-pad

        if w < 18:
            celltxt = '.'
        else:
            fontsize = self.fontsize
            colname = self.model.getColumnName(col)
            #scaling between canvas and text normalised to about font 14
            scale = 8.5 * float(fontsize)/12
            size = length * scale
            if size > w:
                newlength = w / scale
                #print w, size, length, newlength
                celltxt = celltxt[0:int(math.floor(newlength))]

        #if celltxt is dict then we are drawing a hyperlink
        if self.isLink(celltxt) == True:
            haslink=0
            linktext=celltxt['text']
            if len(linktext) > w/scale or w<28:
                linktext=linktext[0:int(w/fontsize*1.2)-2]+'..'
            if celltxt['link']!=None and celltxt['link']!='':
                f,s = self.thefont
                linkfont = (f, s, 'underline')
                linkcolor='blue'
                haslink=1
            else:
                linkfont = self.thefont
                linkcolor=fgcolor

            rect = self.create_text(x1+w/2,y1+h/2,
                                      text=linktext,
                                      fill=linkcolor,
                                      font=linkfont,
                                      tag=('text','hlink','celltext'+str(col)+'_'+str(row)))
            if haslink == 1:
                self.tag_bind(rect, '<Double-Button-1>', self.check_hyperlink)

        #just normal text
        else:
            rect = self.create_text(x1+w/2,y1+h/2,
                                      text=celltxt,
                                      fill=fgcolor,
                                      font=self.thefont,
                                      anchor=align,
                                      tag=('text','celltext'+str(col)+'_'+str(row)))
        return

    def isLink(self, cell):
        """Checks if cell is a hyperlink, without using isinstance"""
        try:
            if 'link' in cell:
                return True
        except:
            return False

    def drawSelectedRow(self):
        """Draw the highlight rect for the currently selected row"""

        self.delete('rowrect')
        row = self.currentrow
        x1,y1,x2,y2 = self.getCellCoords(row,0)
        x2 = self.tablewidth
        rect = self.create_rectangle(x1,y1,x2,y2,
                                  fill=self.rowselectedcolor,
                                  outline=self.rowselectedcolor,
                                  tag='rowrect')
        self.lower('rowrect')
        self.lower('fillrect')
        self.tablerowheader.drawSelectedRows(self.currentrow)
        return

    def drawSelectedCol(self, col=None, delete=1):
        """Draw an outline rect fot the current column selection"""

        if delete == 1:
            self.delete('colrect')
        if col == None:
            col=self.currentcol
        w=2
        x1,y1,x2,y2 = self.getCellCoords(0,col)
        y2 = self.rows * self.rowheight
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2+w/2,
                                     outline='blue',width=w,
                                     tag='colrect')
        return

    def drawMultipleRows(self, rowlist):
        """Draw more than one row selection"""

        self.delete('multiplesel')
        for r in rowlist:
            if r not in self.visiblerows or r > self.rows-1:
                continue
            x1,y1,x2,y2 = self.getCellCoords(r,0)
            x2 = self.tablewidth
            rect = self.create_rectangle(x1,y1,x2,y2,
                                      fill=self.multipleselectioncolor,
                                      outline=self.rowselectedcolor,
                                      tag=('multiplesel','rowrect'))
        self.lower('multiplesel')
        self.lower('fillrect')
        return

    def drawMultipleCells(self):
        """Draw an outline box for multiple cell selection"""

        self.delete('multicellrect')
        rows = self.multiplerowlist
        cols = self.multiplecollist
        w=2
        x1,y1,a,b = self.getCellCoords(rows[0],cols[0])
        c,d,x2,y2 = self.getCellCoords(rows[len(rows)-1],cols[len(cols)-1])
        rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2,
                             outline='blue',width=w,activefill='red',activestipple='gray25',
                             tag='multicellrect')

        return

    def drawTooltip(self, row, col):
        """Draw a tooltip showing contents of cell"""

        x1,y1,x2,y2 = self.getCellCoords(row,col)
        w=x2-x1
        text = self.model.getValueAt(row,col)
        if isinstance(text, dict):
            if 'link' in text:
                text = text['link']

        # If text is a number we make it a string
        if type(text) is float or type is int:
            text = str(text)
        if text == None or text == '' or len(str(text))<=10:
            return

        sfont = font.Font(family='Arial', size=12,weight='bold')
        obj = self.create_text(x1+w/1.5,y2,text=text,
                                anchor='w',
                                font=sfont,tag='tooltip')

        box = self.bbox(obj)
        x1=box[0]-1
        y1=box[1]-1
        x2=box[2]+1
        y2=box[3]+1

        rect = self.create_rectangle(x1+1,y1+1,x2+1,y2+1,tag='tooltip',fill='black')
        rect2 = self.create_rectangle(x1,y1,x2,y2,tag='tooltip',fill='lightyellow')
        self.lift(obj)
        return

    def setcellbackgr(self):
        clr = self.getaColor(self.cellbackgr)
        if clr != None:
            self.cellbackgr = clr
        return

    def setgrid_color(self):
        clr = self.getaColor(self.grid_color)
        if clr != None:
            self.grid_color = clr

        return

    def setrowselectedcolor(self):
        clr = self.getaColor(self.rowselectedcolor)
        if clr != None:
            self.rowselectedcolor = clr
        return

    def getaColor(self, oldcolor):

        ctuple, newcolor = tkColorChooser.askcolor(title='pick a color', initialcolor=oldcolor,
                                                   parent=self.parentframe)
        if ctuple == None:
            return None
        return str(newcolor)

    #--- Preferences stuff ---

    def showtablePrefs(self, prefs=None):
        """Show table options dialog using an instance of prefs"""
        #self.prefs = prefs
        if self.prefs == None:
            self.loadPrefs()
        self.prefswindow=Toplevel()
        x,y,w,h = self.getGeometry(self.master)
        self.prefswindow.geometry('+%s+%s' %(int(x+w/2),int(y+h/2)))
        self.prefswindow.title('Preferences')
        self.prefswindow.resizable(width=FALSE, height=FALSE)

        frame1=Frame(self.prefswindow)
        frame1.pack(side=LEFT)
        frame2=Frame(self.prefswindow)
        frame2.pack()
        def close_prefsdialog():
            self.prefswindow.destroy()
        row=0
        Checkbutton(frame1, text="Show horizontal lines", variable=self.horizlinesvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        Checkbutton(frame1, text="Show vertical lines", variable=self.vertlinesvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        Checkbutton(frame1, text="Alternate Row Color", variable=self.alternaterowsvar,
                    onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
        row=row+1
        lblrowheight = Label(frame1,text='Row Height:')
        lblrowheight.grid(row=row,column=0,padx=3,pady=2)
        rowheightentry = Spinbox(frame1,from_=12,to=50,width=10,
                            textvariable=self.rowheightvar)
        rowheightentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1
        lblcellwidth = Label(frame1,text='Cell Width:')
        lblcellwidth.grid(row=row,column=0,padx=3,pady=2)
        cellwidthentry = Spinbox(frame1,from_=20,to=500, width=10,
                             textvariable=self.cellwidthvar)
        cellwidthentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        lbllinewidth = Label(frame1,text='Line Width:')
        lbllinewidth.grid(row=row,column=0,padx=3,pady=2)
        linewidthentry = Spinbox(frame1,from_=0,to=10,width=10,
                            textvariable=self.linewidthvar)
        linewidthentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        rowhdrwidth = Label(frame1,text='Row Header Width:')
        rowhdrwidth.grid(row=row,column=0,padx=3,pady=2)
        rowhdrentry = Spinbox(frame1,from_=0,to=300, width=10,
                            textvariable=self.rowheaderwidthvar)
        rowhdrentry.grid(row=row,column=1,padx=3,pady=2)
        row=row+1

        #fonts
        fts = self.getFonts()
        self.fontvar = StringVar()
        self.fontvar.set(self.prefs.get('celltextfont'))
        def setFont(*args):
            self.thefont = self.fontvar.get()
            return

        self.fontbox = Combobox(frame2,
                        values=(fts),
                        text='Font:',
                        height = 6,
                        textvariable=self.fontvar)
        self.fontvar.trace('w', setFont)
        Label(frame2,text='Font:').grid(row=row,column=0,padx=3,pady=2)
        self.fontbox.grid(row=row,column=1, columnspan=2, sticky='nes', padx=3,pady=2)
        row=row+1

        lblfontsize=Label(frame2,text='Text Size:')
        lblfontsize.grid(row=row,column=0,padx=3,pady=2)
        fontsizeentry = Spinbox(frame2,from_=6,to=50, width=20,
                                textvariable=self.celltextsizevar)

        fontsizeentry.grid(row=row,column=1, sticky='wens',padx=3,pady=2)
        row=row+1

        #cell alignment
        lbl=Label(frame2,text='Alignment:')
        lbl.grid(row=row,column=0,padx=3,pady=2)
        alignentry_button = Menubutton(frame2,textvariable=self.cellalignvar, width=16)
        alignentry_menu = Menu(alignentry_button,tearoff=0)
        alignentry_button['menu'] = alignentry_menu
        alignments=['w','e','center']
        for text in alignments:
            alignentry_menu.add_radiobutton(label=text,
                                            variable=self.cellalignvar,
                                            value=text,
                                            indicatoron=1)
        alignentry_button.grid(row=row,column=1, sticky='nes', padx=3,pady=2)
        row=row+1

        #colors
        style = Style()
        style.configure("cb.TButton", background=self.cellbackgr)
        cellbackgrbutton = Button(frame2, text='table background', style="cb.TButton", #bg=self.cellbackgr,
                                 command=self.setcellbackgr)
        cellbackgrbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
        row=row+1
        style = Style()
        style.configure("gc.TButton", background=self.grid_color)
        grid_colorbutton = Button(frame2, text='grid color',  style="gc.TButton", #bg=self.grid_color,
                                 command=self.setgrid_color)
        grid_colorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
        row=row+1
        style = Style()
        style.configure("rhc.TButton", background=self.rowselectedcolor)
        rowselectedcolorbutton = Button(frame2, text='row highlight color', style="rhc.TButton",
                                 command=self.setrowselectedcolor)
        rowselectedcolorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
        row=row+1

        frame=Frame(self.prefswindow)
        frame.pack()
        # Apply Button
        b = Button(frame, text="Apply Settings", command=self.applyPrefs)
        b.grid(row=row,column=1,columnspan=2,sticky='news',padx=4,pady=4)

        # Close button
        c=Button(frame,text='Close', command=close_prefsdialog)
        c.grid(row=row,column=0,sticky='news',padx=4,pady=4)
        self.prefswindow.focus_set()
        self.prefswindow.grab_set()
        self.prefswindow.wait_window()
        return self.prefswindow

    def getFonts(self):
        fonts = set(list(font.families()))
        fonts = sorted(list(fonts))
        return fonts

    def loadPrefs(self, prefs=None):
        """Load table specific prefs from the prefs instance used
           if they are not present, create them."""

        if prefs == None:
            prefs=Preferences('Table',{'check_for_update':1})
        self.prefs = prefs
        defaultprefs = {'horizlines':self.horizlines, 'vertlines':self.vertlines,
                        'alternaterows':self.alternaterows,
                        'rowheight':self.rowheight,
                        'cellwidth':100,
                        'autoresizecols': 0,
                        'align': 'w',
                        'celltextsize':11, 'celltextfont':'Arial',
                        'cellbackgr': self.cellbackgr, 'grid_color': self.grid_color,
                        'linewidth' : self.linewidth,
                        'rowselectedcolor': self.rowselectedcolor,
                        'rowheaderwidth': self.rowheaderwidth}

        #print (self.prefs.__dict__)
        for prop in defaultprefs:
            if not prop in self.prefs.prefs:
                #print (defaultprefs[prop])
                if defaultprefs[prop] != 'None':
                    self.prefs.set(prop, defaultprefs[prop])

        self.defaultprefs = defaultprefs

        #Create tkvars for dialog
        self.rowheightvar = IntVar()
        self.rowheightvar.set(self.prefs.get('rowheight'))
        self.rowheight = self.rowheightvar.get()
        self.cellwidthvar = IntVar()
        self.cellwidthvar.set(self.prefs.get('cellwidth'))
        self.cellwidth = self.cellwidthvar.get()
        self.cellalignvar = StringVar()
        self.cellalignvar.set(self.prefs.get('align'))
        self.align = self.cellalignvar.get()
        self.linewidthvar = IntVar()
        self.linewidthvar.set(self.prefs.get('linewidth'))
        self.horizlinesvar = IntVar()
        self.horizlinesvar.set(self.prefs.get('horizlines'))
        self.vertlinesvar = IntVar()
        self.vertlinesvar.set(self.prefs.get('vertlines'))
        self.alternaterowsvar = IntVar()
        self.alternaterowsvar.set(self.prefs.get('alternaterows'))
        self.celltextsizevar = IntVar()
        self.celltextsizevar.set(self.prefs.get('celltextsize'))
        self.cellbackgr = self.prefs.get('cellbackgr')
        self.grid_color = self.prefs.get('grid_color')
        self.rowselectedcolor = self.prefs.get('rowselectedcolor')
        self.fontsize = self.celltextsizevar.get()
        self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
        self.rowheaderwidthvar = IntVar()
        self.rowheaderwidthvar.set(self.prefs.get('rowheaderwidth'))
        self.rowheaderwidth = self.rowheaderwidthvar.get()
        return

    def savePrefs(self):
        """Save and set the prefs"""

        try:
            self.prefs.set('horizlines', self.horizlinesvar.get())
            self.horizlines = self.horizlinesvar.get()
            self.prefs.set('vertlines', self.vertlinesvar.get())
            self.vertlines = self.vertlinesvar.get()
            self.prefs.set('alternaterows', self.alternaterowsvar.get())
            self.alternaterows = self.alternaterowsvar.get()
            self.prefs.set('rowheight', self.rowheightvar.get())
            self.rowheight = self.rowheightvar.get()
            self.prefs.set('cellwidth', self.cellwidthvar.get())
            self.cellwidth = self.cellwidthvar.get()
            self.prefs.set('align', self.cellalignvar.get())
            self.align = self.cellalignvar.get()
            self.prefs.set('linewidth', self.linewidthvar.get())
            self.linewidth = self.linewidthvar.get()
            self.prefs.set('celltextsize', self.celltextsizevar.get())
            self.prefs.set('celltextfont', self.fontvar.get())
            self.prefs.set('cellbackgr', self.cellbackgr)
            self.prefs.set('grid_color', self.grid_color)
            self.prefs.set('rowselectedcolor', self.rowselectedcolor)
            self.prefs.set('rowheaderwidth', self.rowheaderwidth)
            self.rowheaderwidth = self.rowheaderwidthvar.get()
            self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
            self.fontsize = self.prefs.get('celltextsize')

        except ValueError as e:
            print (e)
            pass
        self.prefs.save_prefs()
        return

    def applyPrefs(self):
        """Apply prefs to the table by redrawing"""

        self.savePrefs()
        self.redrawTable()
        return

    def AskForColorButton(self, frame, text, func):
        def SetColor():
            ctuple, variable = tkColorChooser.askcolor(title='pick a color',
                                                       initialcolor=self.cellbackgr)

            return
        bgcolorbutton = Button(frame, text=text,command=SetColor)
        return  bgcolorbutton

    def check_hyperlink(self,event=None):
        """Check if a hyperlink was clicked"""

        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        #absrow = self.get_AbsoluteRow(row)
        recdata = self.model.getValueAt(row, col)
        try:
            link = recdata['link']
            import webbrowser
            webbrowser.open(link,autoraise=1)
        except:
            pass
        return

    def show_progressbar(self,message=None):
        """Show progress bar window for loading of data"""
        progress_win=Toplevel() # Open a new window
        progress_win.title("Please Wait")
        #progress_win.geometry('+%d+%d' %(self.parentframe.rootx+200,self.parentframe.rooty+200))
        #force on top
        progress_win.grab_set()
        progress_win.transient(self.parentframe)
        if message==None:
            message='Working'
        lbl = Label(progress_win,text=message,font='Arial 16')

        lbl.grid(row=0,column=0,columnspan=2,sticky='news',padx=6,pady=4)
        progrlbl = Label(progress_win,text='Progress:')
        progrlbl.grid(row=1,column=0,sticky='news',padx=2,pady=4)
        import ProgressBar
        self.bar = ProgressBar.ProgressBar(progress_win)
        self.bar.frame.grid(row=1,column=1,columnspan=2,padx=2,pady=4)

        return progress_win

    def updateModel(self, model):
        """Call this method to update the table model"""

        self.model = model
        self.rows = self.model.getRowCount()
        self.cols = self.model.getColumnCount()
        self.tablewidth = (self.cellwidth)*self.cols
        self.tablecolheader = ColumnHeader(self.parentframe, self)
        self.tablerowheader = RowHeader(self.parentframe, self)
        self.createTableFrame()
        return

    def new(self):
        """Clears all the data and makes a new table"""

        mpDlg = MultipleValDialog(title='Create new table',
                                    initialvalues=(10, 4),
                                    labels=('rows','columns'),
                                    types=('int','int'),
                                    parent=self.parentframe)

        if mpDlg.result == True:
            rows = mpDlg.results[0]
            cols = mpDlg.results[1]
            model = TableModel(rows=rows,columns=cols)
            self.updateModel(model)
        return

    def load(self, filename=None):
        """load from a file"""

        if filename == None:
            filename = filedialog.askopenfilename(parent=self.master,
                                                      defaultextension='.table',
                                                      initialdir=os.getcwd(),
                                                      filetypes=[("pickle","*.table"),
                                                        ("All files","*.*")])
        if not os.path.exists(filename):
            print ('file does not exist')
            return
        if filename:
            self.model.load(filename)
            self.redrawTable()
        return

    def save(self, filename=None):
        """Save model to pickle file"""

        if filename == None:
            filename = filedialog.asksaveasfilename(parent=self.master,
                                                        defaultextension='.table',
                                                        initialdir=os.getcwd(),
                                                        filetypes=[("pickle","*.table"),
                                                          ("All files","*.*")])
        if filename:
            self.model.save(filename)
        return

    def importTable(self):
        self.importCSV()

    def importCSV(self, filename=None):
        """Import from csv file"""

        if filename is None:
            from .Tables_IO import TableImporter
            importer = TableImporter()
            importdialog = importer.import_Dialog(self.master)
            self.master.wait_window(importdialog)
            model = TableModel()
            model.importDict(importer.data)
        else:
            model = TableModel()
            model.importCSV(filename)
        self.updateModel(model)
        return

    def exportTable(self, filename=None):
        """Do a simple export of the cell contents to csv"""

        from .Tables_IO import TableExporter
        exporter = TableExporter()
        exporter.ExportTableData(self)
        return

    @classmethod
    def checkOSType(cls):
        """Check the OS we are in"""

        ostyp=''
        var_s=['OSTYPE','OS']
        for var in var_s:
            if var in os.environ:
                try:
                    ostyp = str.lower(os.environ[var])
                except:
                    ostyp = os.environ[var].lower()

        ostyp=ostyp.lower()
        if ostyp.find('windows')!=-1:
            ostyp='windows'
        elif ostyp.find('darwin')!=-1 or ostyp.find('apple')!=-1:
            ostyp='mac'
        elif ostyp.find('linux')!=-1:
            ostyp='linux'
        else:
            ostyp='unknown'
            try:
                info=os.uname()
            except:
                pass
            ostyp=info[0].lower()
            if ostyp.find('darwin')!=-1:
                ostyp='mac'
        return ostyp

    def getGeometry(self, frame):
        """Get frame geometry"""
        return frame.winfo_rootx(), frame.winfo_rooty(), frame.winfo_width(), frame.winfo_height()

Ancestors

  • tkinter.Canvas
  • tkinter.Widget
  • tkinter.BaseWidget
  • tkinter.Misc
  • tkinter.Pack
  • tkinter.Place
  • tkinter.Grid
  • tkinter.XView
  • tkinter.YView

Subclasses

Static methods

def checkOSType()

Check the OS we are in

Source code
@classmethod
def checkOSType(cls):
    """Check the OS we are in"""

    ostyp=''
    var_s=['OSTYPE','OS']
    for var in var_s:
        if var in os.environ:
            try:
                ostyp = str.lower(os.environ[var])
            except:
                ostyp = os.environ[var].lower()

    ostyp=ostyp.lower()
    if ostyp.find('windows')!=-1:
        ostyp='windows'
    elif ostyp.find('darwin')!=-1 or ostyp.find('apple')!=-1:
        ostyp='mac'
    elif ostyp.find('linux')!=-1:
        ostyp='linux'
    else:
        ostyp='unknown'
        try:
            info=os.uname()
        except:
            pass
        ostyp=info[0].lower()
        if ostyp.find('darwin')!=-1:
            ostyp='mac'
    return ostyp

Methods

def AskForColorButton(self, frame, text, func)
Source code
def AskForColorButton(self, frame, text, func):
    def SetColor():
        ctuple, variable = tkColorChooser.askcolor(title='pick a color',
                                                   initialcolor=self.cellbackgr)

        return
    bgcolorbutton = Button(frame, text=text,command=SetColor)
    return  bgcolorbutton
def addColumn(self, newname=None)

Add a new column

Source code
def addColumn(self, newname=None):
    """Add a new column"""

    if newname == None:

        coltypes = self.getModel().getDefaultTypes()
        d = MultipleValDialog(title='New Column',
                                initialvalues=(coltypes, ''),
                                labels=('Column Type','Name'),
                                types=('list','string'),
                                parent=self.parentframe)
        if d.result == None:
            return
        else:
            coltype = d.results[0]
            newname = d.results[1]

    if newname != None:
        if newname in self.getModel().columnNames:
            messagebox.showwarning("Name exists",
                                     "Name already exists!",
                                     parent=self.parentframe)
        else:
            self.model.addColumn(newname)
            self.parentframe.configure(width=self.width)
            self.redrawTable()
    return
def addRow(self, key=None, **kwargs)

Add new row

Source code
def addRow(self, key=None, **kwargs):
    """Add new row"""

    key = self.model.addRow(key, **kwargs)
    self.redrawTable()
    self.setSelectedRow(self.model.getRecordIndex(key))
    return
def addRows(self, num=None)

Add new rows

Source code
def addRows(self, num=None):
    """Add new rows"""

    if num == None:
        num = simpledialog.askinteger("Now many rows?",
                                        "Number of rows:",initialvalue=1,
                                         parent=self.parentframe)
    if not num:
        return
    keys = self.model.autoAddRows(num)
    self.redrawTable()
    self.setSelectedRow(self.model.getRecordIndex(keys[0]))
    return
def adjustColumnWidths(self)

Optimally adjust col widths to accomodate the longest entry in each column - usually only called on first redraw

Source code
def adjustColumnWidths(self):
    """Optimally adjust col widths to accomodate the longest entry
        in each column - usually only called  on first redraw"""

    #self.cols = self.model.getColumnCount()
    try:
        fontsize = self.thefont[1]
    except:
        fontsize = self.fontsize
    scale = 8.5 * float(fontsize)/12
    for col in range(self.cols):
        colname = self.model.getColumnName(col)
        if colname in self.model.columnwidths:
            w = self.model.columnwidths[colname]
        else:
            w = self.cellwidth
        maxlen = self.model.getlongestEntry(col)
        size = maxlen * scale
        if size < w:
            continue
        #print col, size, self.cellwidth
        if size >= self.maxcellwidth:
            size = self.maxcellwidth
        self.model.columnwidths[colname] = size + float(fontsize)/12*6
    return
def applyPrefs(self)

Apply prefs to the table by redrawing

Source code
def applyPrefs(self):
    """Apply prefs to the table by redrawing"""

    self.savePrefs()
    self.redrawTable()
    return
def autoAddColumns(self, numcols=None)

Automatically add x number of cols

Source code
def autoAddColumns(self, numcols=None):
    """Automatically add x number of cols"""

    if numcols == None:
        numcols = simpledialog.askinteger("Auto add rows.",
                                            "How many empty columns?",
                                            parent=self.parentframe)
    self.model.auto_AddColumns(numcols)
    self.parentframe.configure(width=self.width)
    self.redrawTable()
    return
def autoResizeColumns(self)

Automatically set nice column widths and draw

Source code
def autoResizeColumns(self):
    """Automatically set nice column widths and draw"""

    self.adjustColumnWidths()
    self.redrawTable()
    return
def checkDataEntry(self, event=None)

do validation checks on data entry in a widget

Source code
def checkDataEntry(self,event=None):
    """do validation checks on data entry in a widget"""
    #if user enters commas, change to points
    import re
    value=event.widget.get()
    if value!='':
        try:
            value=re.sub(',','.', value)
            value=float(value)

        except ValueError:
            event.widget.configure(bg='red')
            return 0
    elif value == '':
        return 1
    return 1

Check if a hyperlink was clicked

Source code
def check_hyperlink(self,event=None):
    """Check if a hyperlink was clicked"""

    row = self.get_row_clicked(event)
    col = self.get_col_clicked(event)
    #absrow = self.get_AbsoluteRow(row)
    recdata = self.model.getValueAt(row, col)
    try:
        link = recdata['link']
        import webbrowser
        webbrowser.open(link,autoraise=1)
    except:
        pass
    return
def clearData(self, evt=None)

Delete cells from gui event

Source code
def clearData(self, evt=None):
    """Delete cells from gui event"""

    rows = self.multiplerowlist
    cols = self.multiplecollist
    self.deleteCells(rows, cols)
    return
def clearSelected(self)
Source code
def clearSelected(self):
    self.delete('rect')
    self.delete('entry')
    self.delete('tooltip')
    self.delete('searchrect')
    self.delete('colrect')
    self.delete('multicellrect')

    #self.delete('formulabox')
    return
def closeFilterFrame(self)

Callback for closing filter frame

Source code
def closeFilterFrame(self):
    """Callback for closing filter frame"""
    self.filterwin.destroy()
    self.filterwin = None
    self.showAll()
    return
def convertFormulae(self, rows, cols=None)

Convert the formulas in the cells to their result values

Source code
def convertFormulae(self, rows, cols=None):
    """Convert the formulas in the cells to their result values"""

    if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
        return None

    print (rows, cols)
    if cols == None:
        cols = range(self.cols)
    for r in rows:
        #absr=self.get_AbsoluteRow(r)
        for c in cols:
            val = self.model.getValueAt(r,c)
            self.model.setValueAt(val, r, c)
    return
def copyCell(self, rows, cols=None)

Copy cell contents to a temp internal clipboard

Source code
def copyCell(self, rows, cols=None):
    """Copy cell contents to a temp internal clipboard"""

    row = rows[0]; col = cols[0]
    #absrow = self.get_AbsoluteRow(row)
    self.clipboard = copy.deepcopy(self.model.getCellRecord(row, col))
    return
def copyColumns(self)

Copy current selected cols

Source code
def copyColumns(self):
    """Copy current selected cols"""

    M = self.model
    coldata = {}
    for col in self.multiplecollist:
        name = M.columnNames[col]
        coldata[name] = M.getColumnData(columnName=name)
    return coldata
def createFilteringBar(self, parent=None, fields=None)

Add a filter frame

Source code
def createFilteringBar(self, parent=None, fields=None):
    """Add a filter frame"""

    if parent == None:
        parent = Toplevel()
        parent.title('Filter Records')
        x,y,w,h = self.getGeometry(self.master)
        parent.geometry('+%s+%s' %(x,y+h))
    if fields == None:
        fields = self.model.columnNames
    from .Filtering import FilterFrame
    self.filterframe = FilterFrame(parent, fields,
                                   self.doFilter, self.closeFilterFrame)
    self.filterframe.pack()
    return parent
def createTableFrame(self, callback=None)
Source code
def createTableFrame(self, callback=None):
    self.show(callback)
    return
def createfromDict(self, data)

Attempt to create a new model/table from a dict

Source code
def createfromDict(self, data):
    """Attempt to create a new model/table from a dict"""

    try:
        namefield=self.namefield
    except:
        namefield=data.keys()[0]
    self.model = TableModel()
    self.model.importDict(data, namefield=namefield)
    self.model.setSortOrder(0,reverse=self.reverseorder)
    return
def deleteCells(self, rows, cols)

Clear the cell contents

Source code
def deleteCells(self, rows, cols):
    """Clear the cell contents"""

    n =  messagebox.askyesno("Clear Confirm",
                               "Clear this data?",
                               parent=self.parentframe)
    if not n:
        return
    for col in cols:
        for row in rows:
            #absrow = self.get_AbsoluteRow(row)
            self.model.deleteCellRecord(row, col)
            self.redrawCell(row,col)
    return
def deleteColumn(self)

Delete currently selected column

Source code
def deleteColumn(self):
    """Delete currently selected column"""

    n =  messagebox.askyesno("Delete",
                               "Delete This Column?",
                               parent=self.parentframe)
    if n:
        col = self.getSelectedColumn()
        self.model.deleteColumn(col)
        self.currentcol = self.currentcol - 1
        self.redrawTable()
    return
def deleteRow(self)

Delete a row

Source code
def deleteRow(self):
    """Delete a row"""

    if len(self.multiplerowlist)>1:
        n = messagebox.askyesno("Delete",
                                  "Delete Selected Records?",
                                  parent=self.parentframe)
        if n == True:
            rows = self.multiplerowlist
            self.model.deleteRows(rows)
            self.clearSelected()
            self.setSelectedRow(0)
            self.redrawTable()
    else:
        n = messagebox.askyesno("Delete",
                                  "Delete This Record?",
                                  parent=self.parentframe)
        if n:
            row = self.getSelectedRow()
            self.model.deleteRow(row)
            self.setSelectedRow(row-1)
            self.clearSelected()
            self.redrawTable()
    return
def doFilter(self, event=None)

Filter the table display by some column values. We simply pass the model search function to the the filtering class and that handles everything else. See filtering frame class for how searching is done.

Source code
def doFilter(self, event=None):
    """Filter the table display by some column values.
    We simply pass the model search function to the the filtering
    class and that handles everything else.
    See filtering frame class for how searching is done.
    """
    if self.model==None:
        return
    names = self.filterframe.doFiltering(searchfunc=self.model.filterBy)
    #create a list of filtered recs
    self.model.filteredrecs = names
    self.filtered = True
    self.redrawTable()
    return
def do_bindings(self)

Bind keys and mouse clicks, this can be overriden

Source code
def do_bindings(self):
    """Bind keys and mouse clicks, this can be overriden"""
    self.bind("<Button-1>",self.handle_left_click)
    self.bind("<Double-Button-1>",self.handle_double_click)
    self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
    self.bind("<Shift-Button-1>", self.handle_left_shift_click)

    self.bind("<ButtonRelease-1>", self.handle_left_release)
    if self.ostyp=='mac':
        #For mac we bind Shift, left-click to right click
        self.bind("<Button-2>", self.handle_right_click)
        self.bind('<Shift-Button-1>',self.handle_right_click)
    else:
        self.bind("<Button-3>", self.handle_right_click)

    self.bind('<B1-Motion>', self.handle_mouse_drag)
    self.bind('<Motion>', self.handle_motion)

    self.bind_all("<Control-x>", self.deleteRow)
    self.bind_all("<Control-n>", self.addRow)
    self.bind_all("<Delete>", self.clearData)
    self.bind_all("<Control-v>", self.paste)

    #if not hasattr(self,'parentapp'):
    #    self.parentapp = self.parentframe

    self.parentframe.master.bind_all("<Right>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<Left>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<Up>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<Down>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<KP_8>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<Return>", self.handle_arrow_keys)
    self.parentframe.master.bind_all("<Tab>", self.handle_arrow_keys)
    #if 'windows' in self.platform:
    self.bind("<MouseWheel>", self.mouse_wheel)
    self.bind('<Button-4>', self.mouse_wheel)
    self.bind('<Button-5>', self.mouse_wheel)
    self.focus_set()
    return
def drawCellEntry(self, row, col, text=None)

When the user single/double clicks on a text/number cell, bring up entry window

Source code
def drawCellEntry(self, row, col, text=None):
    """When the user single/double clicks on a text/number cell, bring up entry window"""

    if self.read_only == True:
        return
    #absrow = self.get_AbsoluteRow(row)
    h=self.rowheight
    model=self.getModel()
    cellvalue = self.model.getCellRecord(row, col)
    if Formula.isFormula(cellvalue):
        return
    else:
        text = self.model.getValueAt(row, col)
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    w=x2-x1
    #Draw an entry window
    txtvar = StringVar()
    txtvar.set(text)
    def callback(e):
        value = txtvar.get()
        if value == '=':
            #do a dialog that gets the formula into a text area
            #then they can click on the cells they want
            #when done the user presses ok and its entered into the cell
            self.cellentry.destroy()
            #its all done here..
            self.formula_Dialog(row, col)
            return

        coltype = self.model.getColumnType(col)
        if coltype == 'number':
            sta = self.checkDataEntry(e)
            if sta == 1:
                model.setValueAt(value,row,col)
        elif coltype == 'text':
            model.setValueAt(value,row,col)

        color = self.model.getColorAt(row,col,'fg')
        self.drawText(row, col, value, color, align=self.align)
        if e.keysym=='Return':
            self.delete('entry')
            #self.drawRect(row, col)
            #self.gotonextCell(e)
        return

    self.cellentry=Entry(self.parentframe,width=20,
                    textvariable=txtvar,
                    #bg=self.entrybackgr,
                    #relief=FLAT,
                    takefocus=1,
                    font=self.thefont)
    self.cellentry.icursor(END)
    self.cellentry.bind('<Return>', callback)
    self.cellentry.bind('<KeyRelease>', callback)
    self.cellentry.focus_set()
    self.entrywin=self.create_window(x1+self.inset,y1+self.inset,
                            width=w-self.inset*2,height=h-self.inset*2,
                            window=self.cellentry,anchor='nw',
                            tag='entry')

    return
def drawGrid(self, startrow, endrow)

Draw the table grid lines

Source code
def drawGrid(self, startrow, endrow):
    """Draw the table grid lines"""
    self.delete('gridline','text')
    rows=len(self.rowrange)
    cols=self.cols
    w = self.cellwidth
    h = self.rowheight
    x_start=self.x_start
    y_start=self.y_start
    x_pos=x_start

    if self.vertlines==1:
        for col in range(cols+1):
            x=self.col_positions[col]
            self.create_line(x,y_start,x,y_start+rows*h, tag='gridline',
                                 fill=self.grid_color, width=self.linewidth)
    if self.horizlines==1:
        for row in range(startrow, endrow+1):
            y_pos=y_start+row*h
            self.create_line(x_start,y_pos,self.tablewidth,y_pos, tag='gridline',
                                fill=self.grid_color, width=self.linewidth)
    return
def drawMultipleCells(self)

Draw an outline box for multiple cell selection

Source code
def drawMultipleCells(self):
    """Draw an outline box for multiple cell selection"""

    self.delete('multicellrect')
    rows = self.multiplerowlist
    cols = self.multiplecollist
    w=2
    x1,y1,a,b = self.getCellCoords(rows[0],cols[0])
    c,d,x2,y2 = self.getCellCoords(rows[len(rows)-1],cols[len(cols)-1])
    rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2,
                         outline='blue',width=w,activefill='red',activestipple='gray25',
                         tag='multicellrect')

    return
def drawMultipleRows(self, rowlist)

Draw more than one row selection

Source code
def drawMultipleRows(self, rowlist):
    """Draw more than one row selection"""

    self.delete('multiplesel')
    for r in rowlist:
        if r not in self.visiblerows or r > self.rows-1:
            continue
        x1,y1,x2,y2 = self.getCellCoords(r,0)
        x2 = self.tablewidth
        rect = self.create_rectangle(x1,y1,x2,y2,
                                  fill=self.multipleselectioncolor,
                                  outline=self.rowselectedcolor,
                                  tag=('multiplesel','rowrect'))
    self.lower('multiplesel')
    self.lower('fillrect')
    return
def drawRect(self, row, col, color=None, tag=None, delete=1)

Cell is colored

Source code
def drawRect(self, row, col, color=None, tag=None, delete=1):
    """Cell is colored"""
    if delete==1:
        self.delete('cellbg'+str(row)+str(col))
    if color==None or color==self.cellbackgr:
        return
    else:
        bg=color
    if tag==None:
        recttag='fillrect'
    else:
        recttag=tag
    w=1
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                              fill=bg,
                              outline=bg,
                              width=w,
                              tag=(recttag,'cellbg'+str(row)+str(col)))
    self.lower(recttag)
    return
def drawRowHeader(self)

User has clicked to select a cell

Source code
def drawRowHeader(self):
    """User has clicked to select a cell"""
    self.delete('rowheader')
    x_start=self.x_start
    y_start=self.y_start
    h=self.rowheight
    rowpos=0
    for row in self.rowrange:
        x1,y1,x2,y2 = self.getCellCoords(rowpos,0)
        self.create_rectangle(0,y1,x_start-2,y2,
                                  fill='gray75',
                                  outline='white',
                                  width=1,
                                  tag='rowheader')
        self.create_text(x_start/2,y1+h/2,
                                  text=row+1,
                                  fill='black',
                                  font=self.thefont,
                                  tag='rowheader')
        rowpos+=1
    return
def drawSelectedCol(self, col=None, delete=1)

Draw an outline rect fot the current column selection

Source code
def drawSelectedCol(self, col=None, delete=1):
    """Draw an outline rect fot the current column selection"""

    if delete == 1:
        self.delete('colrect')
    if col == None:
        col=self.currentcol
    w=2
    x1,y1,x2,y2 = self.getCellCoords(0,col)
    y2 = self.rows * self.rowheight
    rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2+w/2,
                                 outline='blue',width=w,
                                 tag='colrect')
    return
def drawSelectedRect(self, row, col, color=None)

User has clicked to select a cell

Source code
def drawSelectedRect(self, row, col, color=None):
    """User has clicked to select a cell"""
    if col >= self.cols:
        return
    self.delete('currentrect')
    bg = self.selectedcolor
    if color == None:
        color = 'gray25'
    w=3
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
                              fill=bg,
                              outline=color,
                              width=w,
                              stipple='gray50',
                              tag='currentrect')
    #self.lower('currentrect')
    #raise text above all
    self.lift('celltext'+str(col)+'_'+str(row))
    return
def drawSelectedRow(self)

Draw the highlight rect for the currently selected row

Source code
def drawSelectedRow(self):
    """Draw the highlight rect for the currently selected row"""

    self.delete('rowrect')
    row = self.currentrow
    x1,y1,x2,y2 = self.getCellCoords(row,0)
    x2 = self.tablewidth
    rect = self.create_rectangle(x1,y1,x2,y2,
                              fill=self.rowselectedcolor,
                              outline=self.rowselectedcolor,
                              tag='rowrect')
    self.lower('rowrect')
    self.lower('fillrect')
    self.tablerowheader.drawSelectedRows(self.currentrow)
    return
def drawText(self, row, col, celltxt, fgcolor=None, align=None)

Draw the text inside a cell area

Source code
def drawText(self, row, col, celltxt, fgcolor=None, align=None):
    """Draw the text inside a cell area"""

    self.delete('celltext'+str(col)+'_'+str(row))
    h=self.rowheight
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    w=x2-x1
    wrap = False
    pad=5
    # If celltxt is a number then we make it a string
    if type(celltxt) is float or type(celltxt) is int:
        celltxt=str(celltxt)
    length = len(celltxt)
    if length == 0:
        return
    #if cell width is less than x, print nothing
    if w<=10:
        return

    if fgcolor == None or fgcolor == "None":
        fgcolor = 'black'
    if align == None:
        align = 'w'
    if align == 'w':
        x1 = x1-w/2+pad
    elif align == 'e':
        x1 = x1+w/2-pad

    if w < 18:
        celltxt = '.'
    else:
        fontsize = self.fontsize
        colname = self.model.getColumnName(col)
        #scaling between canvas and text normalised to about font 14
        scale = 8.5 * float(fontsize)/12
        size = length * scale
        if size > w:
            newlength = w / scale
            #print w, size, length, newlength
            celltxt = celltxt[0:int(math.floor(newlength))]

    #if celltxt is dict then we are drawing a hyperlink
    if self.isLink(celltxt) == True:
        haslink=0
        linktext=celltxt['text']
        if len(linktext) > w/scale or w<28:
            linktext=linktext[0:int(w/fontsize*1.2)-2]+'..'
        if celltxt['link']!=None and celltxt['link']!='':
            f,s = self.thefont
            linkfont = (f, s, 'underline')
            linkcolor='blue'
            haslink=1
        else:
            linkfont = self.thefont
            linkcolor=fgcolor

        rect = self.create_text(x1+w/2,y1+h/2,
                                  text=linktext,
                                  fill=linkcolor,
                                  font=linkfont,
                                  tag=('text','hlink','celltext'+str(col)+'_'+str(row)))
        if haslink == 1:
            self.tag_bind(rect, '<Double-Button-1>', self.check_hyperlink)

    #just normal text
    else:
        rect = self.create_text(x1+w/2,y1+h/2,
                                  text=celltxt,
                                  fill=fgcolor,
                                  font=self.thefont,
                                  anchor=align,
                                  tag=('text','celltext'+str(col)+'_'+str(row)))
    return
def drawTooltip(self, row, col)

Draw a tooltip showing contents of cell

Source code
def drawTooltip(self, row, col):
    """Draw a tooltip showing contents of cell"""

    x1,y1,x2,y2 = self.getCellCoords(row,col)
    w=x2-x1
    text = self.model.getValueAt(row,col)
    if isinstance(text, dict):
        if 'link' in text:
            text = text['link']

    # If text is a number we make it a string
    if type(text) is float or type is int:
        text = str(text)
    if text == None or text == '' or len(str(text))<=10:
        return

    sfont = font.Font(family='Arial', size=12,weight='bold')
    obj = self.create_text(x1+w/1.5,y2,text=text,
                            anchor='w',
                            font=sfont,tag='tooltip')

    box = self.bbox(obj)
    x1=box[0]-1
    y1=box[1]-1
    x2=box[2]+1
    y2=box[3]+1

    rect = self.create_rectangle(x1+1,y1+1,x2+1,y2+1,tag='tooltip',fill='black')
    rect2 = self.create_rectangle(x1,y1,x2,y2,tag='tooltip',fill='lightyellow')
    self.lift(obj)
    return
def exportTable(self, filename=None)

Do a simple export of the cell contents to csv

Source code
def exportTable(self, filename=None):
    """Do a simple export of the cell contents to csv"""

    from .Tables_IO import TableExporter
    exporter = TableExporter()
    exporter.ExportTableData(self)
    return
def fillAcross(self, collist, rowlist)

Fill across a row, or multiple rows

Source code
def fillAcross(self, collist, rowlist):
    """Fill across a row, or multiple rows"""
    model = self.model
    #row = self.currentrow
    #absrow  = self.get_AbsoluteRow(collist[0])
    frstcol = collist[0]
    collist.remove(frstcol)

    for row in rowlist:
        #absr = self.get_AbsoluteRow(row)
        val = self.model.getCellRecord(absr, frstcol)
        f=val     #formula to copy
        i=1
        for c in collist:
            if Formula.isFormula(f):
                newval = model.copyFormula(f, r, c, offset=i, dim='x')
                model.setFormulaAt(newval, r, c)
            else:
                model.setValueAt(val, r, c)
            i+=1
    self.redrawTable()
    return
def fillDown(self, rowlist, collist)

Fill down a column, or multiple columns

Source code
def fillDown(self, rowlist, collist):
    """Fill down a column, or multiple columns"""
    model = self.model
    #absrow  = self.get_AbsoluteRow(rowlist[0])
    #remove first element as we don't want to overwrite it
    rowlist.remove(rowlist[0])

    #if this is a formula, we have to treat it specially
    for col in collist:
        val = self.model.getCellRecord(row, col)
        f=val #formula to copy
        i=1
        for r in rowlist:
            #absr = self.get_AbsoluteRow(r)
            if Formula.isFormula(f):
                newval = model.copyFormula(f, r, col, offset=i)
                model.setFormulaAt(newval, r, col)
            else:
                model.setValueAt(val, r, col)
            #print 'setting', val, 'at row', r
            i+=1

    self.redrawTable()
    return
def findValue(self, searchstring=None, findagain=None)

Return the row/col for the input value

Source code
def findValue(self, searchstring=None, findagain=None):
    """Return the row/col for the input value"""

    if searchstring == None:
        searchstring = simpledialog.askstring("Search table.",
                                           "Enter search value",
                                           parent=self.parentframe)
    found=0
    if findagain == None or not hasattr(self,'foundlist'):
        self.foundlist=[]
    if self.model!=None:
        for row in range(self.rows):
            for col in range(self.cols):
                text = str(self.model.getValueAt(row,col))
                if text=='' or text==None:
                    continue
                cell=row,col
                if findagain == 1 and cell in self.foundlist:
                    continue
                if text.lower().find(searchstring.lower())!=-1:
                    print ('found in',row,col)
                    found=1
                    #highlight cell
                    self.delete('searchrect')
                    self.drawRect(row, col, color='red', tag='searchrect', delete=0)
                    self.lift('searchrect')
                    self.lift('celltext'+str(col)+'_'+str(row))
                    #add row/col to foundlist
                    self.foundlist.append(cell)
                    #need to scroll to centre the cell here..
                    x,y = self.getCanvasPos(row, col)
                    self.xview('moveto', x)
                    self.yview('moveto', y)
                    self.tablecolheader.xview('moveto', x)
                    self.tablerowheader.yview('moveto', y)
                    return row, col
    if found==0:
        self.delete('searchrect')
        print ('nothing found')
        return None
def formula_Dialog(self, row, col, currformula=None)

Formula dialog

Source code
def formula_Dialog(self, row, col, currformula=None):
    """Formula dialog"""
    self.mode = 'formula'
    print (self.mode)
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    w=300
    h=h=self.rowheight * 3
    def close():
        if hasattr(self,'formulaWin'):
            self.delete('formulabox')
        self.mode = 'normal'
    def calculate():
        #get text area contents and do formula
        f = self.formulaText.get(1.0, END)
        f = f.strip('\n')
        self.model.setFormulaAt(f,row,col)
        value = self.model.doFormula(f)
        color = self.model.getColorAt(row,col,'fg')
        self.drawText(row, col, value, color)
        close()
        self.mode = 'normal'
        return
    def clear():
        self.formulaText.delete(1.0, END)

    self.formulaFrame = Frame(width=w,height=h,bd=3)
    self.formulaText = Text(self.formulaFrame, width=30, height=8, bg='white',relief=GROOVE)
    self.formulaText.pack(side=LEFT,padx=2,pady=2)
    if currformula != None:
        self.formulaText.insert(END, Formula.getFormula(currformula))
    cancelbutton=Button(self.formulaFrame, text='Cancel',
                        bg='#99ccff',command=close)
    cancelbutton.pack(fill=BOTH,padx=2,pady=2)
    donebutton=Button(self.formulaFrame, text='Done',
                      bg='#99ccff',command=calculate)
    donebutton.pack(fill=BOTH,padx=2,pady=2)
    '''clrbutton=Button(self.formulaFrame, text='Clear',
                       bg='#99ccff',command=clear)
    clrbutton.pack(fill=BOTH,padx=2,pady=2) '''
    #add to canvas
    self.formulaWin = self.create_window(x1+self.inset,y1+self.inset,
                            width=w,height=h,
                            window=self.formulaFrame,anchor='nw',
                            tag='formulabox')
    self.formulaText.focus_set()
    return
def getCanvasPos(self, row, col)

Get the cell x-y coords as a fraction of canvas size

Source code
def getCanvasPos(self, row, col):
    """Get the cell x-y coords as a fraction of canvas size"""
    if self.rows==0:
        return None, None
    x1,y1,x2,y2 = self.getCellCoords(row,col)
    cx=float(x1)/self.tablewidth
    cy=float(y1)/(self.rows*self.rowheight)
    return cx, cy
def getCellCoords(self, row, col)

Get x-y coordinates to drawing a cell in a given row/col

Source code
def getCellCoords(self, row, col):
    """Get x-y coordinates to drawing a cell in a given row/col"""
    colname=self.model.getColumnName(col)
    if colname in self.model.columnwidths:
        w=self.model.columnwidths[colname]
    else:
        w=self.cellwidth
    h=self.rowheight
    x_start=self.x_start
    y_start=self.y_start

    #get nearest rect co-ords for that row/col
    #x1=x_start+w*col
    x1=self.col_positions[col]
    y1=y_start+h*row
    x2=x1+w
    y2=y1+h
    return x1,y1,x2,y2
def getColPosition(self, x)

Get current col from canvas position

Source code
def getColPosition(self, x):
    """Get current col from canvas position"""

    x_start = self.x_start
    w = self.cellwidth
    i=0
    col=0
    for c in self.col_positions:
        col = i
        if c+w>=x:
            break
        i+=1
    return col
def getFonts(self)
Source code
def getFonts(self):
    fonts = set(list(font.families()))
    fonts = sorted(list(fonts))
    return fonts
def getGeometry(self, frame)

Get frame geometry

Source code
def getGeometry(self, frame):
    """Get frame geometry"""
    return frame.winfo_rootx(), frame.winfo_rooty(), frame.winfo_width(), frame.winfo_height()
def getModel(self)

Get the current table model

Source code
def getModel(self):
    """Get the current table model"""
    return self.model
def getRecordInfo(self, row)

Show the record for this row

Source code
def getRecordInfo(self, row):
    """Show the record for this row"""

    model = self.model
    #We need a custom dialog for allowing field entries here
    #absrow = self.get_AbsoluteRow(row)
    d = RecordViewDialog(title="Record Details",
                              parent=self.parentframe, table=self, row=row)
    return
def getRowPosition(self, y)

Get current row from canvas position

Source code
def getRowPosition(self, y):
    """Get current row from canvas position"""

    h = self.rowheight
    y_start = self.y_start
    row = (int(y)-y_start)/h
    if row < 0:
        return 0
    if row > self.rows:
        row = self.rows
    return row
def getSelectedColumn(self)

Get currently selected column

Source code
def getSelectedColumn(self):
    """Get currently selected column"""
    return self.currentcol
def getSelectedRow(self)

Get currently selected row

Source code
def getSelectedRow(self):
    """Get currently selected row"""
    return self.currentrow
def getSelectionValues(self)

Get values for current multiple cell selection

Source code
def getSelectionValues(self):
    """Get values for current multiple cell selection"""
    if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
        return None
    rows = self.multiplerowlist
    cols = self.multiplecollist
    model = self.model
    if len(rows)<1 or len(cols)<1:
        return None
    #if only one row selected we plot whole col
    if len(rows) == 1:
        rows = self.rowrange
    lists = []

    for c in cols:
        x=[]
        for r in rows:
            #absr = self.get_AbsoluteRow(r)
            val = model.getValueAt(r,c)
            if val == None or val == '':
                continue
            x.append(val)
        lists.append(x)
    return lists
def getVisibleCols(self, x1, x2)

Get the visible column range

Source code
def getVisibleCols(self, x1, x2):
    """Get the visible column range"""

    start = self.getColPosition(x1)
    end = self.getColPosition(x2)+1
    if end > self.cols:
        end = self.cols
    return start, end
def getVisibleRegion(self)

Get visible region of table to display

Source code
def getVisibleRegion(self):
    """Get visible region of table to display"""

    x1, y1 = self.canvasx(0), self.canvasy(0)
    #w, h = self.winfo_width(), self.winfo_height()
    w,h= self.master.winfo_width(), self.master.winfo_height()
    if w <= 1.0 or h <= 1.0:
        w, h = self.master.winfo_width(), self.master.winfo_height()
    x2, y2 = self.canvasx(w), self.canvasy(h)
    return x1, y1, x2, y2
def getVisibleRows(self, y1, y2)

Get the visible row range

Source code
def getVisibleRows(self, y1, y2):
    """Get the visible row range"""

    start = int(self.getRowPosition(y1))
    end = int(self.getRowPosition(y2))+1
    if end > self.rows:
        end = self.rows
    return start, end
def get_col_clicked(self, event)

get col where event on canvas occurs

Source code
def get_col_clicked(self,event):
    """get col where event on canvas occurs"""

    w=self.cellwidth
    x = int(self.canvasx(event.x))
    x_start=self.x_start
    #print self.col_positions
    for colpos in self.col_positions:
        try:
            nextpos=self.col_positions[self.col_positions.index(colpos)+1]
        except:
            nextpos=self.tablewidth
        if x > colpos and x <= nextpos:
            #print 'x=', x, 'colpos', colpos, self.col_positions.index(colpos)
            return self.col_positions.index(colpos)
        else:
            #print None
            pass
def get_currentColName(self)

Get the currently selected record name

Source code
def get_currentColName(self):
    """Get the currently selected record name"""

    colname = self.mo(self.currentcol)
    return colname
def get_currentRecCol(self)

Get the clicked rec and col names as a tuple

Source code
def get_currentRecCol(self):
    """Get the clicked rec and col names as a tuple"""

    recname = self.get_currentRecordName()
    colname = self.get_currentColName()
    return (recname, colname)
def get_currentRecord(self)

Get the currently selected record

Source code
def get_currentRecord(self):
    """Get the currently selected record"""

    rec = self.model.getRecordAtRow(self.currentrow)
    return rec
def get_currentRecordName(self)

Get the currently selected record name

Source code
def get_currentRecordName(self):
    """Get the currently selected record name"""

    recname = self.model.getRecName(self.currentrow)
    return recname
def get_row_clicked(self, event)

get row where event on canvas occurs

Source code
def get_row_clicked(self, event):
    """get row where event on canvas occurs"""

    h=self.rowheight
    #get coord on canvas, not window, need this if scrolling
    y = int(self.canvasy(event.y))
    y_start=self.y_start
    rowc = int((int(y)-y_start)/h)
    #rowc = math.floor(rowc)
    #print 'event.y',event.y, 'y',y
    #print 'rowclicked', rowc
    return rowc
def get_selectedRecordNames(self)

Get a list of the current multiple selection, if any

Source code
def get_selectedRecordNames(self):
    """Get a list of the current multiple selection, if any"""

    recnames=[]
    for row in self.multiplerowlist:
        recnames.append(self.model.getRecName(row))
    return recnames
def getaColor(self, oldcolor)
Source code
def getaColor(self, oldcolor):

    ctuple, newcolor = tkColorChooser.askcolor(title='pick a color', initialcolor=oldcolor,
                                               parent=self.parentframe)
    if ctuple == None:
        return None
    return str(newcolor)
def getplotlabels(self)

Get labels for plot series from col labels

Source code
def getplotlabels(self):
    """Get labels for plot series from col labels"""
    pltlabels = []
    for col in self.multiplecollist:
        pltlabels.append(self.model.getColumnLabel(col))
    return pltlabels
def gotonextCell(self, event)

Move highlighted cell to next cell in row or a new col

Source code
def gotonextCell(self, event):
    """Move highlighted cell to next cell in row or a new col"""
    #print 'next'
    if hasattr(self, 'cellentry'):
        self.cellentry.destroy()
    self.currentcol=self.currentcol+1
    if self.currentcol >= self.cols-1:
        self.currentrow  = self.currentrow +1
        self.currentcol = self.currentcol+1
    self.drawSelectedRect(self.currentrow, self.currentcol)
    return
def gotonextRow(self)

Programmatically set next row - eg. for button events

Source code
def gotonextRow(self):
    """Programmatically set next row - eg. for button events"""
    self.clearSelected()
    current = self.getSelectedRow()
    self.setSelectedRow(current+1)
    self.startrow = current+1
    self.endrow = current+1
    #reset multiple selection list
    self.multiplerowlist=[]
    self.multiplerowlist.append(self.currentrow)
    self.drawSelectedRect(self.currentrow, self.currentcol)
    self.drawSelectedRow()
    coltype = self.model.getColumnType(self.currentcol)
    if coltype == 'text' or coltype == 'number':
        self.drawCellEntry(self.currentrow, self.currentcol)
    return
def gotoprevRow(self)

Programmatically set previous row - eg. for button events

Source code
def gotoprevRow(self):
    """Programmatically set previous row - eg. for button events"""
    self.clearSelected()
    current = self.getSelectedRow()
    self.setSelectedRow(current-1)
    self.startrow = current-1
    self.endrow = current-1
    #reset multiple selection list
    self.multiplerowlist=[]
    self.multiplerowlist.append(self.currentrow)
    self.drawSelectedRect(self.currentrow, self.currentcol)
    self.drawSelectedRow()
    coltype = self.model.getColumnType(self.currentcol)
    if coltype == 'text' or coltype == 'number':
        self.drawCellEntry(self.currentrow, self.currentcol)
    return
def handleFormulaClick(self, row, col)

Do a dialog for cell formula entry

Source code
def handleFormulaClick(self, row, col):
    """Do a dialog for cell formula entry"""

    model = self.getModel()
    cell = list(model.getRecColNames(row, col))
    #absrow = self.get_AbsoluteRow(row)
    self.formulaText.insert(END, str(cell))
    self.formulaText.focus_set()
    self.drawSelectedRect(row, col, color='red')
    return
def handle_arrow_keys(self, event)

Handle arrow keys press

Source code
def handle_arrow_keys(self, event):
    """Handle arrow keys press"""
    #print event.keysym

    row = self.get_row_clicked(event)
    col = self.get_col_clicked(event)
    x,y = self.getCanvasPos(self.currentrow, 0)
    if x == None:
        return

    if event.keysym == 'Up':
        if self.currentrow == 0:
            return
        else:
            self.currentrow  = self.currentrow -1
    elif event.keysym == 'Down':
        if self.currentrow >= self.rows-1:
            return
        else:
            self.currentrow  = self.currentrow +1
    elif event.keysym == 'Right' or event.keysym == 'Tab':
        if self.currentcol >= self.cols-1:
            if self.currentrow < self.rows-1:
                self.currentcol = 0
                self.currentrow  = self.currentrow +1
            else:
                return
        else:
            self.currentcol  = self.currentcol +1
    elif event.keysym == 'Left':
        if self.currentcol == 0:
            if self.currentrow == 0:
                return
            else:
                self.currentcol = self.cols-1
                self.currentrow = self.currentrow - 1
        else:
            self.currentcol  = self.currentcol -1
    self.drawSelectedRect(self.currentrow, self.currentcol)
    coltype = self.model.getColumnType(self.currentcol)
    if coltype == 'text' or coltype == 'number':
        self.delete('entry')
        self.drawCellEntry(self.currentrow, self.currentcol)
    return
def handle_double_click(self, event)

Do double click stuff. Selected row/cols will already have been set with single click binding

Source code
def handle_double_click(self, event):
    """Do double click stuff. Selected row/cols will already have
       been set with single click binding"""

    '''row = self.get_row_clicked(event)
    col = self.get_col_clicked(event)
    model=self.getModel()
    cellvalue = model.getCellRecord(row, col)
    if Formula.isFormula(cellvalue):
        self.formula_Dialog(row, col, cellvalue)'''

    return
def handle_left_click(self, event)

Respond to a single press

Source code
def handle_left_click(self, event):
    """Respond to a single press"""

    #which row and column is the click inside?
    self.clearSelected()
    self.allrows = False
    rowclicked = self.get_row_clicked(event)
    colclicked = self.get_col_clicked(event)
    self.focus_set()
    if self.mode == 'formula':
        self.handleFormulaClick(rowclicked, colclicked)
        return
    if hasattr(self, 'cellentry'):
        self.cellentry.destroy()
    #ensure popup menus are removed if present
    if hasattr(self, 'rightmenu'):
        self.rightmenu.destroy()
    if hasattr(self.tablecolheader, 'rightmenu'):
        self.tablecolheader.rightmenu.destroy()

    self.startrow = rowclicked
    self.endrow = rowclicked
    self.startcol = colclicked
    self.endcol = colclicked
    #reset multiple selection list
    self.multiplerowlist=[]
    self.multiplerowlist.append(rowclicked)
    if rowclicked is None or colclicked is None:
        return
    if self.read_only is True:    
        return
    if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
        self.setSelectedRow(rowclicked)
        self.setSelectedCol(colclicked)
        self.drawSelectedRect(self.currentrow, self.currentcol)
        self.drawSelectedRow()
        self.tablerowheader.drawSelectedRows(rowclicked)
        coltype = self.model.getColumnType(colclicked)
        if coltype == 'text' or coltype == 'number':
            self.drawCellEntry(rowclicked, colclicked)
    return
def handle_left_ctrl_click(self, event)

Handle ctrl clicks for multiple row selections

Source code
def handle_left_ctrl_click(self, event):
    """Handle ctrl clicks for multiple row selections"""
    rowclicked = self.get_row_clicked(event)
    colclicked = self.get_col_clicked(event)
    if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
        if rowclicked not in self.multiplerowlist:
            self.multiplerowlist.append(rowclicked)
        else:
            self.multiplerowlist.remove(rowclicked)
        self.drawMultipleRows(self.multiplerowlist)
        if colclicked not in self.multiplecollist:
            self.multiplecollist.append(colclicked)
        #print self.multiplecollist
        self.drawMultipleCells()
    return
def handle_left_release(self, event)
Source code
def handle_left_release(self,event):
    self.endrow = self.get_row_clicked(event)
    return
def handle_left_shift_click(self, event)

Handle shift click, for selecting multiple rows

Source code
def handle_left_shift_click(self, event):
    """Handle shift click, for selecting multiple rows"""
    #Has same effect as mouse drag, so just use same method
    self.handle_mouse_drag(event)
    return
def handle_motion(self, event)

Handle mouse motion on table

Source code
def handle_motion(self, event):
    """Handle mouse motion on table"""

    self.delete('tooltip')
    row = self.get_row_clicked(event)
    col = self.get_col_clicked(event)
    if row == None or col == None:
        return
    if 0 <= row < self.rows and 0 <= col < self.cols:
        self.drawTooltip(row, col)

    return
def handle_mouse_drag(self, event)

Handle mouse moved with button held down, multiple selections

Source code
def handle_mouse_drag(self, event):
    """Handle mouse moved with button held down, multiple selections"""

    if hasattr(self, 'cellentry'):
        self.cellentry.destroy()
    rowover = self.get_row_clicked(event)
    colover = self.get_col_clicked(event)
    if colover == None or rowover == None:
        return

    if rowover >= self.rows or self.startrow > self.rows:
        return
    else:
        self.endrow = rowover
    #do columns
    if colover > self.cols or self.startcol > self.cols:
        return
    else:
        self.endcol = colover
        if self.startcol is None or self.endcol is None:
            return
        if self.endcol < self.startcol:
            self.multiplecollist = range(self.endcol, self.startcol+1)
        else:
            self.multiplecollist = range(self.startcol, self.endcol+1)
        #print self.multiplecollist
    #draw the selected rows
    if self.endrow != self.startrow:
        if self.endrow < self.startrow:
            self.multiplerowlist = range(self.endrow, self.startrow+1)
        else:
            self.multiplerowlist = range(self.startrow, self.endrow+1)
        self.drawMultipleRows(self.multiplerowlist)
        self.tablerowheader.drawSelectedRows(self.multiplerowlist)
        #draw selected cells outline using row and col lists
        #print self.multiplerowlist
        self.drawMultipleCells()
    else:
        self.multiplerowlist = []
        self.multiplerowlist.append(self.currentrow)
        if len(self.multiplecollist) >= 1:
            self.drawMultipleCells()
        self.delete('multiplesel')
    #print self.multiplerowlist
    return
def handle_right_click(self, event)

respond to a right click

Source code
def handle_right_click(self, event):
    """respond to a right click"""

    if self.read_only is True:
        return
    self.delete('tooltip')
    self.tablerowheader.clearSelected()
    if hasattr(self, 'rightmenu'):
        self.rightmenu.destroy()
    rowclicked = self.get_row_clicked(event)
    colclicked = self.get_col_clicked(event)
    if colclicked == None:
        self.rightmenu = self.popupMenu(event, outside=1)
        return

    if (rowclicked in self.multiplerowlist or self.allrows == True) and colclicked in self.multiplecollist:
        self.rightmenu = self.popupMenu(event, rows=self.multiplerowlist, cols=self.multiplecollist)
    else:
        if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
            self.clearSelected()
            self.allrows = False
            self.setSelectedRow(rowclicked)
            self.setSelectedCol(colclicked)
            self.drawSelectedRect(self.currentrow, self.currentcol)
            self.drawSelectedRow()
        if self.isInsideTable(event.x,event.y) == 1:
            self.rightmenu = self.popupMenu(event,rows=self.multiplerowlist, cols=self.multiplecollist)
        else:
            self.rightmenu = self.popupMenu(event, outside=1)
    return
def importCSV(self, filename=None)

Import from csv file

Source code
def importCSV(self, filename=None):
    """Import from csv file"""

    if filename is None:
        from .Tables_IO import TableImporter
        importer = TableImporter()
        importdialog = importer.import_Dialog(self.master)
        self.master.wait_window(importdialog)
        model = TableModel()
        model.importDict(importer.data)
    else:
        model = TableModel()
        model.importCSV(filename)
    self.updateModel(model)
    return
def importTable(self)
Source code
def importTable(self):
    self.importCSV()
def isInsideTable(self, x, y)

Returns true if x-y coord is inside table bounds

Source code
def isInsideTable(self,x,y):
    """Returns true if x-y coord is inside table bounds"""
    if self.x_start < x < self.tablewidth and self.y_start < y < self.rows*self.rowheight:
        return 1
    else:
        return 0
    return answer

Checks if cell is a hyperlink, without using isinstance

Source code
def isLink(self, cell):
    """Checks if cell is a hyperlink, without using isinstance"""
    try:
        if 'link' in cell:
            return True
    except:
        return False
def load(self, filename=None)

load from a file

Source code
def load(self, filename=None):
    """load from a file"""

    if filename == None:
        filename = filedialog.askopenfilename(parent=self.master,
                                                  defaultextension='.table',
                                                  initialdir=os.getcwd(),
                                                  filetypes=[("pickle","*.table"),
                                                    ("All files","*.*")])
    if not os.path.exists(filename):
        print ('file does not exist')
        return
    if filename:
        self.model.load(filename)
        self.redrawTable()
    return
def loadPrefs(self, prefs=None)

Load table specific prefs from the prefs instance used if they are not present, create them.

Source code
def loadPrefs(self, prefs=None):
    """Load table specific prefs from the prefs instance used
       if they are not present, create them."""

    if prefs == None:
        prefs=Preferences('Table',{'check_for_update':1})
    self.prefs = prefs
    defaultprefs = {'horizlines':self.horizlines, 'vertlines':self.vertlines,
                    'alternaterows':self.alternaterows,
                    'rowheight':self.rowheight,
                    'cellwidth':100,
                    'autoresizecols': 0,
                    'align': 'w',
                    'celltextsize':11, 'celltextfont':'Arial',
                    'cellbackgr': self.cellbackgr, 'grid_color': self.grid_color,
                    'linewidth' : self.linewidth,
                    'rowselectedcolor': self.rowselectedcolor,
                    'rowheaderwidth': self.rowheaderwidth}

    #print (self.prefs.__dict__)
    for prop in defaultprefs:
        if not prop in self.prefs.prefs:
            #print (defaultprefs[prop])
            if defaultprefs[prop] != 'None':
                self.prefs.set(prop, defaultprefs[prop])

    self.defaultprefs = defaultprefs

    #Create tkvars for dialog
    self.rowheightvar = IntVar()
    self.rowheightvar.set(self.prefs.get('rowheight'))
    self.rowheight = self.rowheightvar.get()
    self.cellwidthvar = IntVar()
    self.cellwidthvar.set(self.prefs.get('cellwidth'))
    self.cellwidth = self.cellwidthvar.get()
    self.cellalignvar = StringVar()
    self.cellalignvar.set(self.prefs.get('align'))
    self.align = self.cellalignvar.get()
    self.linewidthvar = IntVar()
    self.linewidthvar.set(self.prefs.get('linewidth'))
    self.horizlinesvar = IntVar()
    self.horizlinesvar.set(self.prefs.get('horizlines'))
    self.vertlinesvar = IntVar()
    self.vertlinesvar.set(self.prefs.get('vertlines'))
    self.alternaterowsvar = IntVar()
    self.alternaterowsvar.set(self.prefs.get('alternaterows'))
    self.celltextsizevar = IntVar()
    self.celltextsizevar.set(self.prefs.get('celltextsize'))
    self.cellbackgr = self.prefs.get('cellbackgr')
    self.grid_color = self.prefs.get('grid_color')
    self.rowselectedcolor = self.prefs.get('rowselectedcolor')
    self.fontsize = self.celltextsizevar.get()
    self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
    self.rowheaderwidthvar = IntVar()
    self.rowheaderwidthvar.set(self.prefs.get('rowheaderwidth'))
    self.rowheaderwidth = self.rowheaderwidthvar.get()
    return
def mouse_wheel(self, event)

Handle mouse wheel scroll for windows

Source code
def mouse_wheel(self, event):
    """Handle mouse wheel scroll for windows"""

    if event.num == 5 or event.delta == -120:
        event.widget.yview_scroll(1, UNITS)
        self.tablerowheader.yview_scroll(1, UNITS)
    if event.num == 4 or event.delta == 120:
        if self.canvasy(0) < 0:
            return
        event.widget.yview_scroll(-1, UNITS)
        self.tablerowheader.yview_scroll(-1, UNITS)
    self.redrawVisible()
    return
def movetoSelectedRow(self, row=None, recname=None)

Move to selected row, updating table

Source code
def movetoSelectedRow(self, row=None, recname=None):
    """Move to selected row, updating table"""
    row=self.model.getRecordIndex(recname)
    self.setSelectedRow(row)
    self.drawSelectedRow()
    x,y = self.getCanvasPos(row, 0)
    self.yview('moveto', y-0.01)
    self.tablecolheader.yview('moveto', y)
    return
def new(self)

Clears all the data and makes a new table

Source code
def new(self):
    """Clears all the data and makes a new table"""

    mpDlg = MultipleValDialog(title='Create new table',
                                initialvalues=(10, 4),
                                labels=('rows','columns'),
                                types=('int','int'),
                                parent=self.parentframe)

    if mpDlg.result == True:
        rows = mpDlg.results[0]
        cols = mpDlg.results[1]
        model = TableModel(rows=rows,columns=cols)
        self.updateModel(model)
    return
def paste(self, event=None)

Copy from clipboard

Source code
def paste(self, event=None):
    """Copy from clipboard"""

    print (self.parentframe.clipboard_get())
    return
def pasteCell(self, rows, cols=None)

Paste cell from internal clipboard

Source code
def pasteCell(self, rows, cols=None):
    """Paste cell from internal clipboard"""

    row = rows[0]; col = cols[0]
    #absrow = self.get_AbsoluteRow(row)
    val = self.clipboard
    self.model.setValueAt(val, row, col)
    self.redrawTable()
    return
def pasteColumns(self, coldata)

Paste new cols, overwrites existing names

Source code
def pasteColumns(self, coldata):
    """Paste new cols, overwrites existing names"""

    M = self.model
    for name in coldata:
        if name not in M.columnNames:
            M.addColumn(name)
        for r in range(len(coldata[name])):
            val = coldata[name][r]
            col = M.columnNames.index(name)
            if r >= self.rows:
                break
            M.setValueAt(val, r, col)
    self.redrawTable()
    return coldata
def plotSelected(self, graphtype='XY')

Plot the selected data using pylab - if possible

Source code
def plotSelected(self, graphtype='XY'):
    """Plot the selected data using pylab - if possible"""

    from .Plot import pylabPlotter
    if not hasattr(self, 'pyplot'):
        self.pyplot = pylabPlotter()
    plotdata = []
    for p in self.getSelectionValues():
        x = []
        fail = False
        for d in p:
            try:
                x.append(float(d))
            except:
                fail = True
                continue
        if fail == False:
            plotdata.append(x)

    pltlabels = self.getplotlabels()
    if len(pltlabels) > 2:
        self.pyplot.setDataSeries(pltlabels)
        self.pyplot.showlegend = 1
    self.pyplot.plotCurrent(data=plotdata, graphtype=graphtype)
    return
def plotSetup(self)

Call pylab plot dialog setup, send data if we haven't already plotted

Source code
def plotSetup(self):
    """Call pylab plot dialog setup, send data if we haven't already
        plotted"""

    from PylabPlot import pylabPlotter
    if not hasattr(self, 'pyplot'):
        self.pyplot = pylabPlotter()
    plotdata = self.getSelectionValues()
    if not self.pyplot.hasData() and plotdata != None:
        print ('has data')
        plotdata = self.getSelectionValues()
        pltlabels = self.getplotlabels()
        self.pyplot.setDataSeries(pltlabels)
        self.pyplot.plotSetup(plotdata)
    else:
        self.pyplot.plotSetup()
    return
def popupMenu(self, event, rows=None, cols=None, outside=None)

Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor

Source code
def popupMenu(self, event, rows=None, cols=None, outside=None):
    """Add left and right click behaviour for canvas, should not have to override
        this function, it will take its values from defined dicts in constructor"""

    defaultactions = {"Set Fill Color" : lambda : self.setcellColor(rows,cols,key='bg'),
                    "Set Text Color" : lambda : self.setcellColor(rows,cols,key='fg'),
                    "Copy" : lambda : self.copyCell(rows, cols),
                    "Paste" : lambda : self.pasteCell(rows, cols),
                    "Fill Down" : lambda : self.fillDown(rows, cols),
                    "Fill Right" : lambda : self.fillAcross(cols, rows),
                    "Add Row(s)" : lambda : self.addRows(),
                    "Delete Row(s)" : lambda : self.deleteRow(),
                    "View Record" : lambda : self.getRecordInfo(row),
                    "Clear Data" : lambda : self.deleteCells(rows, cols),
                    "Select All" : self.select_All,
                    "Auto Fit Columns" : self.autoResizeColumns,
                    "Filter Records" : self.showFilteringBar,
                    "New": self.new,
                    "Load": self.load,
                    "Save": self.save,
                    "Import text":self.importTable,
                    "Export csv": self.exportTable,
                    "Plot Selected" : self.plotSelected,
                    "Plot Options" : self.plotSetup,
                    "Export Table" : self.exportTable,
                    "Preferences" : self.showtablePrefs,
                    "Formulae->Value" : lambda : self.convertFormulae(rows, cols)}

    main = ["Set Fill Color","Set Text Color","Copy", "Paste", "Fill Down","Fill Right",
            "Clear Data"]
    general = ["Select All", "Add Row(s)" , "Delete Row(s)", "Auto Fit Columns", "Filter Records", "Preferences"]
    filecommands = ['New','Load','Save','Import text','Export csv']
    plotcommands = ['Plot Selected','Plot Options']

    def createSubMenu(parent, label, commands):
        menu = Menu(parent, tearoff = 0)
        popupmenu.add_cascade(label=label,menu=menu)
        for action in commands:
            menu.add_command(label=action, command=defaultactions[action])
        return menu

    def add_commands(fieldtype):
        """Add commands to popup menu for column type and specific cell"""
        functions = self.columnactions[fieldtype]
        for f in functions.keys():
            func = getattr(self, functions[f])
            popupmenu.add_command(label=f, command= lambda : func(row,col))
        return

    popupmenu = Menu(self, tearoff = 0)
    def popupFocusOut(event):
        popupmenu.unpost()

    if outside == None:
        #if outside table, just show general items
        row = self.get_row_clicked(event)
        col = self.get_col_clicked(event)
        coltype = self.model.getColumnType(col)
        def add_defaultcommands():
            """now add general actions for all cells"""
            for action in main:
                if action == 'Fill Down' and (rows == None or len(rows) <= 1):
                    continue
                if action == 'Fill Right' and (cols == None or len(cols) <= 1):
                    continue
                else:
                    popupmenu.add_command(label=action, command=defaultactions[action])
            return

        if coltype in self.columnactions:
            add_commands(coltype)
        add_defaultcommands()

    for action in general:
        popupmenu.add_command(label=action, command=defaultactions[action])

    popupmenu.add_separator()
    createSubMenu(popupmenu, 'File', filecommands)
    createSubMenu(popupmenu, 'Plot', plotcommands)
    popupmenu.bind("<FocusOut>", popupFocusOut)
    popupmenu.focus_set()
    popupmenu.post(event.x_root, event.y_root)
    return popupmenu
def redraw(self, event=None, callback=None)
Source code
def redraw(self, event=None, callback=None):
    self.redrawVisible(event, callback)
    return
def redrawCell(self, row=None, col=None, recname=None, colname=None)

Redraw a specific cell only

Source code
def redrawCell(self, row=None, col=None, recname=None, colname=None):
    """Redraw a specific cell only"""

    if row == None and recname != None:
        row = self.model.getRecordIndex(recname)
    if col == None and colname != None:
        col = self.model.getColumnIndex(colname)
    bgcolor = self.model.getColorAt(row,col, 'bg')
    fgcolor = self.model.getColorAt(row,col, 'fg')
    text = self.model.getValueAt(row,col)
    self.drawText(row, col, text, fgcolor)
    if bgcolor != None:
        self.drawRect(row,col, color=bgcolor)
    return
def redrawTable(self, event=None, callback=None)
Source code
def redrawTable(self, event=None, callback=None):
    self.redrawVisible(event, callback)
    return
def redrawVisible(self, event=None, callback=None)

Redraw the visible portion of the canvas

Source code
def redrawVisible(self, event=None, callback=None):
    """Redraw the visible portion of the canvas"""

    model = self.model
    self.rows = self.model.getRowCount()
    self.cols = self.model.getColumnCount()

    self.tablewidth = (self.cellwidth) * self.cols
    self.configure(bg=self.cellbackgr)
    self.setColPositions()

    #are we drawing a filtered subset of the recs?
    if self.filtered == True and self.model.filteredrecs != None:
        self.rows = len(self.model.filteredrecs)
        self.delete('colrect')

    self.rowrange = range(0,self.rows)
    self.configure(scrollregion=(0,0, self.tablewidth+self.x_start,
            self.rowheight*self.rows+10))

    x1, y1, x2, y2 = self.getVisibleRegion()
    startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2)
    self.visiblerows = range(startvisiblerow, endvisiblerow)
    startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2)
    self.visiblecols = range(startvisiblecol, endvisiblecol)

    if self.cols == 0 or self.rows == 0:
        self.delete('entry')
        self.delete('rowrect')
        self.delete('currentrect')
        self.delete('gridline','text')
        self.tablerowheader.redraw()
        return

    self.drawGrid(startvisiblerow, endvisiblerow)
    align = self.align
    self.delete('fillrect')
    for row in self.visiblerows:
        if callback != None:
            callback()
        for col in self.visiblecols:
            colname = model.getColumnName(col)
            bgcolor = model.getColorAt(row,col, 'bg')
            fgcolor = model.getColorAt(row,col, 'fg')
            text = model.getValueAt(row,col)
            self.drawText(row, col, text, fgcolor, align)
            if bgcolor != None:
                self.drawRect(row,col, color=bgcolor)

    #self.drawSelectedCol()
    self.tablecolheader.redraw()
    self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader)
    #self.setSelectedRow(self.currentrow)
    self.drawSelectedRow()
    self.drawSelectedRect(self.currentrow, self.currentcol)
    #print self.multiplerowlist

    if len(self.multiplerowlist)>1:
        self.tablerowheader.drawSelectedRows(self.multiplerowlist)
        self.drawMultipleRows(self.multiplerowlist)
        self.drawMultipleCells()
    return
def resizeColumn(self, col, width)

Resize a column by dragging

Source code
def resizeColumn(self, col, width):
    """Resize a column by dragging"""

    #print 'resizing column', col
    #recalculate all col positions..
    colname=self.model.getColumnName(col)
    self.model.columnwidths[colname]=width
    self.setColPositions()
    self.redrawTable()
    self.drawSelectedCol(self.currentcol)
    return
def save(self, filename=None)

Save model to pickle file

Source code
def save(self, filename=None):
    """Save model to pickle file"""

    if filename == None:
        filename = filedialog.asksaveasfilename(parent=self.master,
                                                    defaultextension='.table',
                                                    initialdir=os.getcwd(),
                                                    filetypes=[("pickle","*.table"),
                                                      ("All files","*.*")])
    if filename:
        self.model.save(filename)
    return
def savePrefs(self)

Save and set the prefs

Source code
def savePrefs(self):
    """Save and set the prefs"""

    try:
        self.prefs.set('horizlines', self.horizlinesvar.get())
        self.horizlines = self.horizlinesvar.get()
        self.prefs.set('vertlines', self.vertlinesvar.get())
        self.vertlines = self.vertlinesvar.get()
        self.prefs.set('alternaterows', self.alternaterowsvar.get())
        self.alternaterows = self.alternaterowsvar.get()
        self.prefs.set('rowheight', self.rowheightvar.get())
        self.rowheight = self.rowheightvar.get()
        self.prefs.set('cellwidth', self.cellwidthvar.get())
        self.cellwidth = self.cellwidthvar.get()
        self.prefs.set('align', self.cellalignvar.get())
        self.align = self.cellalignvar.get()
        self.prefs.set('linewidth', self.linewidthvar.get())
        self.linewidth = self.linewidthvar.get()
        self.prefs.set('celltextsize', self.celltextsizevar.get())
        self.prefs.set('celltextfont', self.fontvar.get())
        self.prefs.set('cellbackgr', self.cellbackgr)
        self.prefs.set('grid_color', self.grid_color)
        self.prefs.set('rowselectedcolor', self.rowselectedcolor)
        self.prefs.set('rowheaderwidth', self.rowheaderwidth)
        self.rowheaderwidth = self.rowheaderwidthvar.get()
        self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
        self.fontsize = self.prefs.get('celltextsize')

    except ValueError as e:
        print (e)
        pass
    self.prefs.save_prefs()
    return
def select_All(self)

Select all rows and cells

Source code
def select_All(self):
    """Select all rows and cells"""

    self.startrow = 0
    self.endrow = self.rows
    self.multiplerowlist = range(self.startrow,self.endrow)
    self.drawMultipleRows(self.multiplerowlist)
    self.startcol = 0
    self.endcol = self.cols
    self.multiplecollist = range(self.startcol, self.endcol)
    self.drawMultipleCells()
    return
def setColPositions(self)

Determine current column grid positions

Source code
def setColPositions(self):
    """Determine current column grid positions"""

    self.col_positions=[]
    w=self.cellwidth
    x_pos=self.x_start
    self.col_positions.append(x_pos)
    for col in range(self.cols):
        colname=self.model.getColumnName(col)
        if colname in self.model.columnwidths:
            x_pos=x_pos+self.model.columnwidths[colname]
        else:
            x_pos=x_pos+w
        self.col_positions.append(x_pos)
    self.tablewidth = self.col_positions[len(self.col_positions)-1]
    return
def setFontSize(self)

Set font size to match font, we need to get rid of fontsize as a separate variable?

Source code
def setFontSize(self):
    """Set font size to match font, we need to get rid of fontsize as
        a separate variable?"""

    if hasattr(self, 'thefont') and type(self.thefont) is tuple:
        self.fontsize = self.thefont[1]
    return
def setModel(self, model)

Set a new model - requires redraw to reflect changes

Source code
def setModel(self, model):
    """Set a new model - requires redraw to reflect changes"""
    self.model = model
    return
def setRowHeight(self, h)

Set the row height

Source code
def setRowHeight(self, h):
    """Set the row height"""
    self.rowheight = h
    return
def setSelectedCells(self, startrow, endrow, startcol, endcol)

Set a block of cells selected

Source code
def setSelectedCells(self, startrow, endrow, startcol, endcol):
    """Set a block of cells selected"""

    self.currentrow = startrow
    self.currentcol = startcol
    if startrow < 0 or startcol < 0:
        return
    if endrow > self.rows or endcol > self.cols:
        return
    for r in range(startrow, endrow):
        self.multiplerowlist.append(r)
    for c in range(startcol, endcol):
        self.multiplecollist.append(c)
    return
def setSelectedCol(self, col)

Set currently selected column

Source code
def setSelectedCol(self, col):
    """Set currently selected column"""

    self.currentcol = col
    self.multiplecollist = []
    self.multiplecollist.append(col)
    return
def setSelectedRow(self, row)

Set currently selected row and reset multiple row list

Source code
def setSelectedRow(self, row):
    """Set currently selected row and reset multiple row list"""

    self.currentrow = row
    self.multiplerowlist = []
    self.multiplerowlist.append(row)
    return
def set_defaults(self)

Set default settings

Source code
def set_defaults(self):
    """Set default settings"""

    self.cellwidth=150
    self.maxcellwidth=200
    self.rowheight=20
    self.horizlines=1
    self.vertlines=1
    self.alternaterows=0
    self.autoresizecols = 0
    self.inset=2
    self.x_start=0
    self.y_start=1
    self.linewidth=1.0
    self.rowheaderwidth=40
    self.showkeynamesinheader=False
    self.thefont = ('Arial',12)
    self.cellbackgr = '#F7F7FA'
    self.entrybackgr = 'white'
    self.grid_color = '#ABB1AD'
    self.selectedcolor = 'yellow'
    self.rowselectedcolor = '#CCCCFF'
    self.multipleselectioncolor = '#ECD672'
    return
def set_xviews(self, *args)

Set the xview of table and col header

Source code
def set_xviews(self,*args):
    """Set the xview of table and col header"""

    self.xview(*args)
    self.tablecolheader.xview(*args)
    self.redrawVisible()
    return
def set_yviews(self, *args)

Set the xview of table and row header

Source code
def set_yviews(self,*args):
    """Set the xview of table and row header"""

    self.yview(*args)
    self.tablerowheader.yview(*args)
    self.redrawVisible()
    return
def setcellColor(self, rows, cols=None, newColor=None, key=None, redraw=True)

Set the cell color for one or more cells and save it in the model color

Source code
def setcellColor(self, rows, cols=None, newColor=None, key=None, redraw=True):
    """Set the cell color for one or more cells and save it in the model color"""

    model = self.getModel()
    if newColor == None:
        ctuple, newColor = tkColorChooser.askcolor(title='pick a color')
        if newColor == None:
            return

    if type(rows) is int:
        x=rows
        rows=[]
        rows.append(x)
    if self.allrows == True:
        #we use all rows if the whole column has been selected
        rows = range(0,self.rows)
    if cols == None:
        cols = range(self.cols)
    for col in cols:
        for row in rows:
            #absrow = self.get_AbsoluteRow(row)
            model.setColorAt(row, col, color=newColor, key=key)
            #setcolor(absrow, col)
    if redraw == True:
        self.redrawTable()
    return
def setcellbackgr(self)
Source code
def setcellbackgr(self):
    clr = self.getaColor(self.cellbackgr)
    if clr != None:
        self.cellbackgr = clr
    return
def setgrid_color(self)
Source code
def setgrid_color(self):
    clr = self.getaColor(self.grid_color)
    if clr != None:
        self.grid_color = clr

    return
def setrowselectedcolor(self)
Source code
def setrowselectedcolor(self):
    clr = self.getaColor(self.rowselectedcolor)
    if clr != None:
        self.rowselectedcolor = clr
    return
def show(self, callback=None)

Adds column header and scrollbars and combines them with the current table adding all to the master frame provided in constructor. Table is then redrawn.

Source code
def show(self, callback=None):
    """Adds column header and scrollbars and combines them with
       the current table adding all to the master frame provided in constructor.
       Table is then redrawn."""

    #Add the table and header to the frame
    self.tablerowheader = RowHeader(self.parentframe, self, width=self.rowheaderwidth)
    self.tablecolheader = ColumnHeader(self.parentframe, self)
    self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews)
    self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0)
    self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews)
    self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news')
    self['xscrollcommand'] = self.Xscrollbar.set
    self['yscrollcommand'] = self.Yscrollbar.set
    self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set
    self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set
    self.parentframe.rowconfigure(1,weight=1)
    self.parentframe.columnconfigure(1,weight=1)

    self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0)
    self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0)
    self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0)

    self.adjustColumnWidths()
    self.redrawTable(callback=callback)
    self.parentframe.bind("<Configure>", self.redrawVisible)
    self.tablecolheader.xview("moveto", 0)
    self.xview("moveto", 0)
    return
def showAll(self)
Source code
def showAll(self):
    self.model.filteredrecs = None
    self.filtered = False
    self.redrawTable()
    return
def showFilteringBar(self)
Source code
def showFilteringBar(self):
    if not hasattr(self, 'filterwin') or self.filterwin == None:
        self.filterwin = self.createFilteringBar()
        self.filterwin.protocol("WM_DELETE_WINDOW", self.closeFilterFrame)
    else:
        self.filterwin.lift()
    return
def show_progressbar(self, message=None)

Show progress bar window for loading of data

Source code
def show_progressbar(self,message=None):
    """Show progress bar window for loading of data"""
    progress_win=Toplevel() # Open a new window
    progress_win.title("Please Wait")
    #progress_win.geometry('+%d+%d' %(self.parentframe.rootx+200,self.parentframe.rooty+200))
    #force on top
    progress_win.grab_set()
    progress_win.transient(self.parentframe)
    if message==None:
        message='Working'
    lbl = Label(progress_win,text=message,font='Arial 16')

    lbl.grid(row=0,column=0,columnspan=2,sticky='news',padx=6,pady=4)
    progrlbl = Label(progress_win,text='Progress:')
    progrlbl.grid(row=1,column=0,sticky='news',padx=2,pady=4)
    import ProgressBar
    self.bar = ProgressBar.ProgressBar(progress_win)
    self.bar.frame.grid(row=1,column=1,columnspan=2,padx=2,pady=4)

    return progress_win
def showtablePrefs(self, prefs=None)

Show table options dialog using an instance of prefs

Source code
def showtablePrefs(self, prefs=None):
    """Show table options dialog using an instance of prefs"""
    #self.prefs = prefs
    if self.prefs == None:
        self.loadPrefs()
    self.prefswindow=Toplevel()
    x,y,w,h = self.getGeometry(self.master)
    self.prefswindow.geometry('+%s+%s' %(int(x+w/2),int(y+h/2)))
    self.prefswindow.title('Preferences')
    self.prefswindow.resizable(width=FALSE, height=FALSE)

    frame1=Frame(self.prefswindow)
    frame1.pack(side=LEFT)
    frame2=Frame(self.prefswindow)
    frame2.pack()
    def close_prefsdialog():
        self.prefswindow.destroy()
    row=0
    Checkbutton(frame1, text="Show horizontal lines", variable=self.horizlinesvar,
                onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
    row=row+1
    Checkbutton(frame1, text="Show vertical lines", variable=self.vertlinesvar,
                onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
    row=row+1
    Checkbutton(frame1, text="Alternate Row Color", variable=self.alternaterowsvar,
                onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
    row=row+1
    lblrowheight = Label(frame1,text='Row Height:')
    lblrowheight.grid(row=row,column=0,padx=3,pady=2)
    rowheightentry = Spinbox(frame1,from_=12,to=50,width=10,
                        textvariable=self.rowheightvar)
    rowheightentry.grid(row=row,column=1,padx=3,pady=2)
    row=row+1
    lblcellwidth = Label(frame1,text='Cell Width:')
    lblcellwidth.grid(row=row,column=0,padx=3,pady=2)
    cellwidthentry = Spinbox(frame1,from_=20,to=500, width=10,
                         textvariable=self.cellwidthvar)
    cellwidthentry.grid(row=row,column=1,padx=3,pady=2)
    row=row+1

    lbllinewidth = Label(frame1,text='Line Width:')
    lbllinewidth.grid(row=row,column=0,padx=3,pady=2)
    linewidthentry = Spinbox(frame1,from_=0,to=10,width=10,
                        textvariable=self.linewidthvar)
    linewidthentry.grid(row=row,column=1,padx=3,pady=2)
    row=row+1

    rowhdrwidth = Label(frame1,text='Row Header Width:')
    rowhdrwidth.grid(row=row,column=0,padx=3,pady=2)
    rowhdrentry = Spinbox(frame1,from_=0,to=300, width=10,
                        textvariable=self.rowheaderwidthvar)
    rowhdrentry.grid(row=row,column=1,padx=3,pady=2)
    row=row+1

    #fonts
    fts = self.getFonts()
    self.fontvar = StringVar()
    self.fontvar.set(self.prefs.get('celltextfont'))
    def setFont(*args):
        self.thefont = self.fontvar.get()
        return

    self.fontbox = Combobox(frame2,
                    values=(fts),
                    text='Font:',
                    height = 6,
                    textvariable=self.fontvar)
    self.fontvar.trace('w', setFont)
    Label(frame2,text='Font:').grid(row=row,column=0,padx=3,pady=2)
    self.fontbox.grid(row=row,column=1, columnspan=2, sticky='nes', padx=3,pady=2)
    row=row+1

    lblfontsize=Label(frame2,text='Text Size:')
    lblfontsize.grid(row=row,column=0,padx=3,pady=2)
    fontsizeentry = Spinbox(frame2,from_=6,to=50, width=20,
                            textvariable=self.celltextsizevar)

    fontsizeentry.grid(row=row,column=1, sticky='wens',padx=3,pady=2)
    row=row+1

    #cell alignment
    lbl=Label(frame2,text='Alignment:')
    lbl.grid(row=row,column=0,padx=3,pady=2)
    alignentry_button = Menubutton(frame2,textvariable=self.cellalignvar, width=16)
    alignentry_menu = Menu(alignentry_button,tearoff=0)
    alignentry_button['menu'] = alignentry_menu
    alignments=['w','e','center']
    for text in alignments:
        alignentry_menu.add_radiobutton(label=text,
                                        variable=self.cellalignvar,
                                        value=text,
                                        indicatoron=1)
    alignentry_button.grid(row=row,column=1, sticky='nes', padx=3,pady=2)
    row=row+1

    #colors
    style = Style()
    style.configure("cb.TButton", background=self.cellbackgr)
    cellbackgrbutton = Button(frame2, text='table background', style="cb.TButton", #bg=self.cellbackgr,
                             command=self.setcellbackgr)
    cellbackgrbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
    row=row+1
    style = Style()
    style.configure("gc.TButton", background=self.grid_color)
    grid_colorbutton = Button(frame2, text='grid color',  style="gc.TButton", #bg=self.grid_color,
                             command=self.setgrid_color)
    grid_colorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
    row=row+1
    style = Style()
    style.configure("rhc.TButton", background=self.rowselectedcolor)
    rowselectedcolorbutton = Button(frame2, text='row highlight color', style="rhc.TButton",
                             command=self.setrowselectedcolor)
    rowselectedcolorbutton.grid(row=row,column=0,columnspan=2,  sticky='news',padx=3,pady=2)
    row=row+1

    frame=Frame(self.prefswindow)
    frame.pack()
    # Apply Button
    b = Button(frame, text="Apply Settings", command=self.applyPrefs)
    b.grid(row=row,column=1,columnspan=2,sticky='news',padx=4,pady=4)

    # Close button
    c=Button(frame,text='Close', command=close_prefsdialog)
    c.grid(row=row,column=0,sticky='news',padx=4,pady=4)
    self.prefswindow.focus_set()
    self.prefswindow.grab_set()
    self.prefswindow.wait_window()
    return self.prefswindow
def sortTable(self, columnIndex=0, columnName=None, reverse=0)

Set up sort order dict based on currently selected field

Source code
def sortTable(self, columnIndex=0, columnName=None, reverse=0):
    """Set up sort order dict based on currently selected field"""

    self.model.setSortOrder(columnIndex, columnName, reverse)
    self.redrawTable()
    return
def updateModel(self, model)

Call this method to update the table model

Source code
def updateModel(self, model):
    """Call this method to update the table model"""

    self.model = model
    self.rows = self.model.getRowCount()
    self.cols = self.model.getColumnCount()
    self.tablewidth = (self.cellwidth)*self.cols
    self.tablecolheader = ColumnHeader(self.parentframe, self)
    self.tablerowheader = RowHeader(self.parentframe, self)
    self.createTableFrame()
    return