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
def check_hyperlink(self, event=None)
-
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
def isLink(self, cell)
-
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