1
2 """
3 Implements the core TableCanvas class.
4 Created Oct 2008
5 Copyright (C) Damien Farrell
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 """
21
22 from Tkinter import *
23 from TableModels import TableModel
24 from TableFormula import Formula
25 from Prefs import Preferences
26 import tkFileDialog, tkMessageBox, tkSimpleDialog
27 import tkFont
28 import math, time
29 import os, types
30 import string, copy
31 import platform
34 """A tkinter class for providing table functionality"""
35
36 - def __init__(self, parent=None, model=None, width=None, height=None,
37 rows=10, cols=5, **kwargs):
38 Canvas.__init__(self, parent, bg='white',
39 width=width, height=height,
40 relief=GROOVE,
41 scrollregion=(0,0,300,200))
42 self.parentframe = parent
43
44 self.ostyp = self.checkOSType()
45 self.platform = platform.system()
46 self.width = width
47 self.height = height
48 self.set_defaults()
49
50 self.currentpage = None
51 self.navFrame = None
52 self.currentrow = 0
53 self.currentcol = 0
54 self.reverseorder = 0
55 self.startrow = self.endrow = None
56 self.startcol = self.endcol = None
57 self.allrows = False
58 self.multiplerowlist=[]
59 self.multiplecollist=[]
60 self.col_positions=[]
61 self.mode = 'normal'
62 self.editable = True
63 self.filtered = False
64
65 self.loadPrefs()
66
67 for key in kwargs:
68 self.__dict__[key] = kwargs[key]
69
70 if model == None:
71 self.model = TableModel(rows=rows,columns=cols)
72 else:
73 self.model = model
74
75 self.rows = self.model.getRowCount()
76 self.cols = self.model.getColumnCount()
77 self.tablewidth = (self.cellwidth)*self.cols
78 self.do_bindings()
79
80 self.model.setSortOrder()
81
82
83
84 self.columnactions = {'text' : {"Edit": 'drawCellEntry' },
85 'number' : {"Edit": 'drawCellEntry' }}
86 self.setFontSize()
87 return
88
90 """Set default settings"""
91 self.cellwidth=150
92 self.maxcellwidth=200
93 self.rowheight=20
94 self.horizlines=1
95 self.vertlines=1
96 self.alternaterows=0
97 self.autoresizecols = 0
98 self.inset=2
99 self.x_start=0
100 self.y_start=1
101 self.linewidth=1.0
102 self.rowheaderwidth=40
103 self.showkeynamesinheader=False
104 self.thefont = ('Arial',12)
105 self.cellbackgr = '#F7F7FA'
106 self.entrybackgr = 'white'
107 self.grid_color = '#ABB1AD'
108 self.selectedcolor = 'yellow'
109 self.rowselectedcolor = '#CCCCFF'
110 self.multipleselectioncolor = '#ECD672'
111 return
112
114 """Set font size to match font, we need to get rid of fontsize as
115 a separate variable?"""
116 if hasattr(self, 'thefont') and type(self.thefont) is types.TupleType:
117 self.fontsize = self.thefont[1]
118 return
119
121 """Handle mouse wheel scroll for windows"""
122
123 if event.num == 5 or event.delta == -120:
124 event.widget.yview_scroll(1, UNITS)
125 self.tablerowheader.yview_scroll(1, UNITS)
126 if event.num == 4 or event.delta == 120:
127 if self.canvasy(0) < 0:
128 return
129 event.widget.yview_scroll(-1, UNITS)
130 self.tablerowheader.yview_scroll(-1, UNITS)
131 self.redrawVisible()
132 return
133
135 """Bind keys and mouse clicks, this can be overriden"""
136 self.bind("<Button-1>",self.handle_left_click)
137 self.bind("<Double-Button-1>",self.handle_double_click)
138 self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
139 self.bind("<Shift-Button-1>", self.handle_left_shift_click)
140
141 self.bind("<ButtonRelease-1>", self.handle_left_release)
142 if self.ostyp=='mac':
143
144 self.bind("<Button-2>", self.handle_right_click)
145 self.bind('<Shift-Button-1>',self.handle_right_click)
146 else:
147 self.bind("<Button-3>", self.handle_right_click)
148
149 self.bind('<B1-Motion>', self.handle_mouse_drag)
150 self.bind('<Motion>', self.handle_motion)
151
152 self.bind_all("<Control-x>", self.deleteRow)
153 self.bind_all("<Control-n>", self.addRow)
154 self.bind_all("<Delete>", self.clearData)
155 self.bind_all("<Control-v>", self.paste)
156
157
158
159
160 self.parentframe.master.bind_all("<Right>", self.handle_arrow_keys)
161 self.parentframe.master.bind_all("<Left>", self.handle_arrow_keys)
162 self.parentframe.master.bind_all("<Up>", self.handle_arrow_keys)
163 self.parentframe.master.bind_all("<Down>", self.handle_arrow_keys)
164 self.parentframe.master.bind_all("<KP_8>", self.handle_arrow_keys)
165 self.parentframe.master.bind_all("<Return>", self.handle_arrow_keys)
166 self.parentframe.master.bind_all("<Tab>", self.handle_arrow_keys)
167
168 self.bind("<MouseWheel>", self.mouse_wheel)
169 self.bind('<Button-4>', self.mouse_wheel)
170 self.bind('<Button-5>', self.mouse_wheel)
171 self.focus_set()
172 return
173
175 """Get the current table model"""
176 return self.model
177
179 """Set a new model - requires redraw to reflect changes"""
180 self.model = model
181 return
182
184 """Attempt to create a new model/table from a dict"""
185 try:
186 namefield=self.namefield
187 except:
188 namefield=data.keys()[0]
189 self.model = TableModel()
190 self.model.importDict(data, namefield=namefield)
191 self.model.setSortOrder(0,reverse=self.reverseorder)
192 return
193
195 """Adds column header and scrollbars and combines them with
196 the current table adding all to the master frame provided in constructor.
197 Table is then redrawn."""
198
199
200 self.tablerowheader = RowHeader(self.parentframe, self, width=self.rowheaderwidth)
201 self.tablecolheader = ColumnHeader(self.parentframe, self)
202 self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews)
203 self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0)
204 self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews)
205 self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news')
206 self['xscrollcommand'] = self.Xscrollbar.set
207 self['yscrollcommand'] = self.Yscrollbar.set
208 self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set
209 self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set
210 self.parentframe.rowconfigure(1,weight=1)
211 self.parentframe.columnconfigure(1,weight=1)
212
213 self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0)
214 self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0)
215 self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0)
216
217 self.adjustColumnWidths()
218 self.redrawTable(callback=callback)
219 self.parentframe.bind("<Configure>", self.redrawVisible)
220 self.tablecolheader.xview("moveto", 0)
221 self.xview("moveto", 0)
222 return
223
225 x1, y1 = self.canvasx(0), self.canvasy(0)
226 w, h = self.winfo_width(), self.winfo_height()
227 if w <= 1.0 or h <= 1.0:
228 w, h = self.master.winfo_width(), self.master.winfo_height()
229 x2, y2 = self.canvasx(w), self.canvasy(h)
230 return x1, y1, x2, y2
231
233 h = self.rowheight
234 y_start = self.y_start
235 row = (int(y)-y_start)/h
236 if row < 0:
237 return 0
238 if row > self.rows:
239 row = self.rows
240 return row
241
243 x_start = self.x_start
244 w = self.cellwidth
245 i=0
246 col=0
247 for c in self.col_positions:
248 col = i
249 if c+w>=x:
250 break
251 i+=1
252 return col
253
261
269
271 """Redraw the visible portion of the canvas"""
272
273 model = self.model
274 self.rows = self.model.getRowCount()
275 self.cols = self.model.getColumnCount()
276 if self.cols == 0 or self.rows == 0:
277 self.delete('entry')
278 self.delete('rowrect')
279 self.delete('currentrect')
280 return
281 self.tablewidth = (self.cellwidth) * self.cols
282 self.configure(bg=self.cellbackgr)
283 self.setColPositions()
284
285
286 if self.filtered == True and self.model.filteredrecs != None:
287 self.rows = len(self.model.filteredrecs)
288 self.delete('colrect')
289
290 self.rowrange = range(0,self.rows)
291 self.configure(scrollregion=(0,0, self.tablewidth+self.x_start,
292 self.rowheight*self.rows+10))
293
294 x1, y1, x2, y2 = self.getVisibleRegion()
295 startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2)
296 self.visiblerows = range(startvisiblerow, endvisiblerow)
297 startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2)
298 self.visiblecols = range(startvisiblecol, endvisiblecol)
299
300 self.drawGrid(startvisiblerow, endvisiblerow)
301 align = self.align
302 self.delete('fillrect')
303 for row in self.visiblerows:
304 if callback != None:
305 callback()
306 for col in self.visiblecols:
307 colname = model.getColumnName(col)
308 bgcolor = model.getColorAt(row,col, 'bg')
309 fgcolor = model.getColorAt(row,col, 'fg')
310 text = model.getValueAt(row,col)
311 self.drawText(row, col, text, fgcolor, align)
312 if bgcolor != None:
313 self.drawRect(row,col, color=bgcolor)
314
315
316 self.tablecolheader.redraw()
317 self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader)
318
319 self.drawSelectedRow()
320 self.drawSelectedRect(self.currentrow, self.currentcol)
321
322
323 if len(self.multiplerowlist)>1:
324 self.tablerowheader.drawSelectedRows(self.multiplerowlist)
325 self.drawMultipleRows(self.multiplerowlist)
326 self.drawMultipleCells()
327 return
328
332
333 - def redrawCell(self, row=None, col=None, recname=None, colname=None):
334 """Redraw a specific cell only"""
335 if row == None and recname != None:
336 row = self.model.getRecordIndex(recname)
337 if col == None and colname != None:
338 col = self.model.getColumnIndex(colname)
339 bgcolor = self.model.getColorAt(row,col, 'bg')
340 fgcolor = self.model.getColorAt(row,col, 'fg')
341 text = self.model.getValueAt(row,col)
342 self.drawText(row, col, text, fgcolor)
343 if bgcolor != None:
344 self.drawRect(row,col, color=bgcolor)
345 return
346
348 """Optimally adjust col widths to accomodate the longest entry
349 in each column - usually only called on first redraw"""
350
351 try:
352 fontsize = self.thefont[1]
353 except:
354 fontsize = self.fontsize
355 scale = 8.5 * float(fontsize)/12
356 for col in range(self.cols):
357 colname = self.model.getColumnName(col)
358 if self.model.columnwidths.has_key(colname):
359 w = self.model.columnwidths[colname]
360 else:
361 w = self.cellwidth
362 maxlen = self.model.getlongestEntry(col)
363 size = maxlen * scale
364 if size < w:
365 continue
366
367 if size >= self.maxcellwidth:
368 size = self.maxcellwidth
369 self.model.columnwidths[colname] = size + float(fontsize)/12*6
370 return
371
377
379 """Determine current column grid positions"""
380 self.col_positions=[]
381 w=self.cellwidth
382 x_pos=self.x_start
383 self.col_positions.append(x_pos)
384 for col in range(self.cols):
385 colname=self.model.getColumnName(col)
386 if self.model.columnwidths.has_key(colname):
387 x_pos=x_pos+self.model.columnwidths[colname]
388 else:
389 x_pos=x_pos+w
390 self.col_positions.append(x_pos)
391 self.tablewidth = self.col_positions[len(self.col_positions)-1]
392 return
393
394 - def sortTable(self, columnIndex=0, columnName=None, reverse=0):
395 """Set up sort order dict based on currently selected field"""
396
397
398 self.model.setSortOrder(columnIndex, columnName, reverse)
399 self.redrawTable()
400 return
401
403 """Set the xview of table and col header"""
404 apply(self.xview,args)
405 apply(self.tablecolheader.xview,args)
406 self.redrawVisible()
407 return
408
410 """Set the xview of table and row header"""
411 apply(self.yview,args)
412 apply(self.tablerowheader.yview,args)
413 self.redrawVisible()
414 return
415
416 - def addRow(self, key=None, **kwargs):
422
424 """Add new rows"""
425 if num == None:
426 num = tkSimpleDialog.askinteger("Now many rows?",
427 "Number of rows:",initialvalue=1,
428 parent=self.parentframe)
429 if not num:
430 return
431 keys = self.model.autoAddRows(num)
432 self.redrawTable()
433 self.setSelectedRow(self.model.getRecordIndex(keys[0]))
434 return
435
437 """Add a new column"""
438 if newname == None:
439 from Dialogs import MultipleValDialog
440 coltypes = self.getModel().getDefaultTypes()
441 d = MultipleValDialog(title='New Column',
442 initialvalues=(coltypes, ''),
443 labels=('Column Type','Name'),
444 types=('list','string'),
445 parent=self.parentframe)
446 if d.result == None:
447 return
448 else:
449 coltype = d.results[0]
450 newname = d.results[1]
451
452 if newname != None:
453 if newname in self.getModel().columnNames:
454 tkMessageBox.showwarning("Name exists",
455 "Name already exists!",
456 parent=self.parentframe)
457 else:
458 self.model.addColumn(newname)
459 self.parentframe.configure(width=self.width)
460 self.redrawTable()
461 return
462
464 """Delete a row"""
465 if len(self.multiplerowlist)>1:
466 n = tkMessageBox.askyesno("Delete",
467 "Delete Selected Records?",
468 parent=self.parentframe)
469 if n == True:
470 rows = self.multiplerowlist
471 self.model.deleteRows(rows)
472 self.setSelectedRow(0)
473 self.clearSelected()
474 self.redrawTable()
475 else:
476 n = tkMessageBox.askyesno("Delete",
477 "Delete This Record?",
478 parent=self.parentframe)
479 if n:
480 row = self.getSelectedRow()
481 self.model.deleteRow(row)
482 self.setSelectedRow(row-1)
483 self.clearSelected()
484 self.redrawTable()
485 return
486
488 """Delete currently selected column"""
489 n = tkMessageBox.askyesno("Delete",
490 "Delete This Column?",
491 parent=self.parentframe)
492 if n:
493 col = self.getSelectedColumn()
494 self.model.deleteColumn(col)
495 self.currentcol = self.currentcol - 1
496 self.redrawTable()
497 return
498
500 """Clear the cell contents"""
501 n = tkMessageBox.askyesno("Clear Confirm",
502 "Clear this data?",
503 parent=self.parentframe)
504 if not n:
505 return
506 for col in cols:
507 for row in rows:
508
509 self.model.deleteCellRecord(row, col)
510 self.redrawCell(row,col)
511 return
512
514 """Delete cells from gui event"""
515 rows = self.multiplerowlist
516 cols = self.multiplecollist
517 self.deleteCells(rows, cols)
518 return
519
521 """Automatically add x number of cols"""
522 if numcols == None:
523 numcols = tkSimpleDialog.askinteger("Auto add rows.",
524 "How many empty columns?",
525 parent=self.parentframe)
526 self.model.auto_AddColumns(numcols)
527 self.parentframe.configure(width=self.width)
528 self.redrawTable()
529 return
530
532 """Show the record for this row"""
533 model = self.model
534
535
536 from Dialogs import RecordViewDialog
537 d = RecordViewDialog(title="Record Details",
538 parent=self.parentframe, table=self, row=row)
539 return
540
541 - def findValue(self, searchstring=None, findagain=None):
542 """Return the row/col for the input value"""
543 if searchstring == None:
544 searchstring = tkSimpleDialog.askstring("Search table.",
545 "Enter search value",
546 parent=self.parentframe)
547 found=0
548 if findagain == None or not hasattr(self,'foundlist'):
549 self.foundlist=[]
550 if self.model!=None:
551 for row in range(self.rows):
552 for col in range(self.cols):
553 text = str(self.model.getValueAt(row,col))
554 if text=='' or text==None:
555 continue
556 cell=row,col
557 if findagain == 1 and cell in self.foundlist:
558 continue
559 if text.lower().find(searchstring.lower())!=-1:
560 print 'found in',row,col
561 found=1
562
563 self.delete('searchrect')
564 self.drawRect(row, col, color='red', tag='searchrect', delete=0)
565 self.lift('searchrect')
566 self.lift('celltext'+str(col)+'_'+str(row))
567
568 self.foundlist.append(cell)
569
570 x,y = self.getCanvasPos(row, col)
571 self.xview('moveto', x)
572 self.yview('moveto', y)
573 self.tablecolheader.xview('moveto', x)
574 self.tablerowheader.yview('moveto', y)
575 return row, col
576 if found==0:
577 self.delete('searchrect')
578 print 'nothing found'
579 return None
580
582 self.model.filteredrecs = None
583 self.filtered = False
584 self.redrawTable()
585 return
586
588 """Filter the table display by some column values.
589 We simply pass the model search function to the the filtering
590 class and that handles everything else.
591 See filtering frame class for how searching is done.
592 """
593 if self.model==None:
594 return
595 names = self.filterframe.doFiltering(searchfunc=self.model.filterBy)
596
597 self.model.filteredrecs = names
598 self.filtered = True
599 self.redrawTable()
600 return
601
603 """Add a filter frame"""
604 if parent == None:
605 parent = Toplevel()
606 parent.title('Filter Records')
607 x,y,w,h = self.getGeometry(self.master)
608 parent.geometry('+%s+%s' %(x,y+h))
609 if fields == None:
610 fields = self.model.columnNames
611 from Filtering import FilterFrame
612 self.filterframe = FilterFrame(parent, fields,
613 self.doFilter, self.closeFilterFrame)
614 self.filterframe.pack()
615 return parent
616
618 if not hasattr(self, 'filterwin') or self.filterwin == None:
619 self.filterwin = self.createFilteringBar()
620 self.filterwin.protocol("WM_DELETE_WINDOW", self.closeFilterFrame)
621 else:
622 self.filterwin.lift()
623 return
624
626 """Callback for closing filter frame"""
627 self.filterwin.destroy()
628 self.filterwin = None
629 self.showAll()
630 return
631
642
644 """Get the currently selected record"""
645 rec = self.model.getRecordAtRow(self.currentrow)
646 return rec
647
649 """Get the currently selected record name"""
650 colname = self.mo(self.currentcol)
651 return colname
652
654 """Get the currently selected record name"""
655 recname = self.model.getRecName(self.currentrow)
656 return recname
657
659 """Get a list of the current multiple selection, if any"""
660 recnames=[]
661 for row in self.multiplerowlist:
662 recnames.append(self.model.getRecName(row))
663 return recnames
664
670
672 """get row where event on canvas occurs"""
673 h=self.rowheight
674
675 y = int(self.canvasy(event.y))
676 y_start=self.y_start
677 rowc = (int(y)-y_start)/h
678
679
680
681 return rowc
682
684 """get col where event on canvas occurs"""
685 w=self.cellwidth
686 x = int(self.canvasx(event.x))
687 x_start=self.x_start
688
689 for colpos in self.col_positions:
690 try:
691 nextpos=self.col_positions[self.col_positions.index(colpos)+1]
692 except:
693 nextpos=self.tablewidth
694 if x > colpos and x <= nextpos:
695
696 return self.col_positions.index(colpos)
697 else:
698
699 pass
700
701
703 """Set currently selected row and reset multiple row list"""
704 self.currentrow = row
705 self.multiplerowlist = []
706 self.multiplerowlist.append(row)
707 return
708
710 """Set currently selected column"""
711 self.currentcol = col
712 self.multiplecollist = []
713 self.multiplecollist.append(col)
714 return
715
717 """Set a block of cells selected"""
718 self.currentrow = startrow
719 self.currentcol = startcol
720 if startrow < 0 or startcol < 0:
721 return
722 if endrow > self.rows or endcol > self.cols:
723 return
724 for r in range(startrow, endrow):
725 self.multiplerowlist.append(r)
726 for c in range(startcol, endcol):
727 self.multiplecollist.append(c)
728 return
729
731 """Get currently selected row"""
732 return self.currentrow
733
735 """Get currently selected column"""
736 return self.currentcol
737
739 """Select all rows and cells"""
740 self.startrow = 0
741 self.endrow = self.rows
742 self.multiplerowlist = range(self.startrow,self.endrow)
743 self.drawMultipleRows(self.multiplerowlist)
744 self.startcol = 0
745 self.endcol = self.cols
746 self.multiplecollist = range(self.startcol, self.endcol)
747 self.drawMultipleCells()
748 return
749
751 """Get x-y coordinates to drawing a cell in a given row/col"""
752 colname=self.model.getColumnName(col)
753 if self.model.columnwidths.has_key(colname):
754 w=self.model.columnwidths[colname]
755 else:
756 w=self.cellwidth
757 h=self.rowheight
758 x_start=self.x_start
759 y_start=self.y_start
760
761
762
763 x1=self.col_positions[col]
764 y1=y_start+h*row
765 x2=x1+w
766 y2=y1+h
767 return x1,y1,x2,y2
768
770 """Get the cell x-y coords as a fraction of canvas size"""
771 if self.rows==0:
772 return None, None
773 x1,y1,x2,y2 = self.getCellCoords(row,col)
774 cx=float(x1)/self.tablewidth
775 cy=float(y1)/(self.rows*self.rowheight)
776 return cx, cy
777
779 """Returns true if x-y coord is inside table bounds"""
780 if self.x_start < x < self.tablewidth and self.y_start < y < self.rows*self.rowheight:
781 return 1
782 else:
783 return 0
784 return answer
785
787 """Set the row height"""
788 self.rowheight = h
789 return
790
792 self.delete('rect')
793 self.delete('entry')
794 self.delete('tooltip')
795 self.delete('searchrect')
796 self.delete('colrect')
797 self.delete('multicellrect')
798
799
800 return
801
803 """Programmatically set previous row - eg. for button events"""
804 self.clearSelected()
805 current = self.getSelectedRow()
806 self.setSelectedRow(current-1)
807 self.startrow = current-1
808 self.endrow = current-1
809
810 self.multiplerowlist=[]
811 self.multiplerowlist.append(self.currentrow)
812 self.drawSelectedRect(self.currentrow, self.currentcol)
813 self.drawSelectedRow()
814 coltype = self.model.getColumnType(self.currentcol)
815 if coltype == 'text' or coltype == 'number':
816 self.drawCellEntry(self.currentrow, self.currentcol)
817 return
818
820 """Programmatically set next row - eg. for button events"""
821 self.clearSelected()
822 current = self.getSelectedRow()
823 self.setSelectedRow(current+1)
824 self.startrow = current+1
825 self.endrow = current+1
826
827 self.multiplerowlist=[]
828 self.multiplerowlist.append(self.currentrow)
829 self.drawSelectedRect(self.currentrow, self.currentcol)
830 self.drawSelectedRow()
831 coltype = self.model.getColumnType(self.currentcol)
832 if coltype == 'text' or coltype == 'number':
833 self.drawCellEntry(self.currentrow, self.currentcol)
834 return
835
837 """Respond to a single press"""
838
839 self.clearSelected()
840 self.allrows = False
841 rowclicked = self.get_row_clicked(event)
842 colclicked = self.get_col_clicked(event)
843 self.focus_set()
844 if self.mode == 'formula':
845 self.handleFormulaClick(rowclicked, colclicked)
846 return
847 if hasattr(self, 'cellentry'):
848 self.cellentry.destroy()
849
850 if hasattr(self, 'rightmenu'):
851 self.rightmenu.destroy()
852 if hasattr(self.tablecolheader, 'rightmenu'):
853 self.tablecolheader.rightmenu.destroy()
854
855 self.startrow = rowclicked
856 self.endrow = rowclicked
857 self.startcol = colclicked
858 self.endcol = colclicked
859
860 self.multiplerowlist=[]
861 self.multiplerowlist.append(rowclicked)
862 if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
863 self.setSelectedRow(rowclicked)
864 self.setSelectedCol(colclicked)
865 self.drawSelectedRect(self.currentrow, self.currentcol)
866 self.drawSelectedRow()
867 self.tablerowheader.drawSelectedRows(rowclicked)
868 coltype = self.model.getColumnType(colclicked)
869 if coltype == 'text' or coltype == 'number':
870 self.drawCellEntry(rowclicked, colclicked)
871 return
872
876
878 """Handle ctrl clicks for multiple row selections"""
879 rowclicked = self.get_row_clicked(event)
880 colclicked = self.get_col_clicked(event)
881 if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
882 if rowclicked not in self.multiplerowlist:
883 self.multiplerowlist.append(rowclicked)
884 else:
885 self.multiplerowlist.remove(rowclicked)
886 self.drawMultipleRows(self.multiplerowlist)
887 if colclicked not in self.multiplecollist:
888 self.multiplecollist.append(colclicked)
889
890 self.drawMultipleCells()
891 return
892
894 """Handle shift click, for selecting multiple rows"""
895
896 self.handle_mouse_drag(event)
897 return
898
900 """Handle mouse moved with button held down, multiple selections"""
901 if hasattr(self, 'cellentry'):
902 self.cellentry.destroy()
903 rowover = self.get_row_clicked(event)
904 colover = self.get_col_clicked(event)
905 if colover == None or rowover == None:
906 return
907
908 if rowover >= self.rows or self.startrow > self.rows:
909 return
910 else:
911 self.endrow = rowover
912
913 if colover > self.cols or self.startcol > self.cols:
914 return
915 else:
916 self.endcol = colover
917 if self.endcol < self.startcol:
918 self.multiplecollist=range(self.endcol, self.startcol+1)
919 else:
920 self.multiplecollist=range(self.startcol, self.endcol+1)
921
922
923 if self.endrow != self.startrow:
924 if self.endrow < self.startrow:
925 self.multiplerowlist=range(self.endrow, self.startrow+1)
926 else:
927 self.multiplerowlist=range(self.startrow, self.endrow+1)
928 self.drawMultipleRows(self.multiplerowlist)
929 self.tablerowheader.drawSelectedRows(self.multiplerowlist)
930
931
932 self.drawMultipleCells()
933 else:
934 self.multiplerowlist = []
935 self.multiplerowlist.append(self.currentrow)
936 if len(self.multiplecollist) >= 1:
937 self.drawMultipleCells()
938 self.delete('multiplesel')
939
940 return
941
943 """Handle arrow keys press"""
944
945
946 row = self.get_row_clicked(event)
947 col = self.get_col_clicked(event)
948 x,y = self.getCanvasPos(self.currentrow, 0)
949 if x == None:
950 return
951
952 if event.keysym == 'Up':
953 if self.currentrow == 0:
954 return
955 else:
956
957
958 self.currentrow = self.currentrow -1
959 elif event.keysym == 'Down':
960 if self.currentrow >= self.rows-1:
961 return
962 else:
963
964
965 self.currentrow = self.currentrow +1
966 elif event.keysym == 'Right' or event.keysym == 'Tab':
967 if self.currentcol >= self.cols-1:
968 if self.currentrow < self.rows-1:
969 self.currentcol = 0
970 self.currentrow = self.currentrow +1
971 else:
972 return
973 else:
974 self.currentcol = self.currentcol +1
975 elif event.keysym == 'Left':
976 self.currentcol = self.currentcol -1
977 self.drawSelectedRect(self.currentrow, self.currentcol)
978 coltype = self.model.getColumnType(self.currentcol)
979 if coltype == 'text' or coltype == 'number':
980 self.delete('entry')
981 self.drawCellEntry(self.currentrow, self.currentcol)
982 return
983
998
1000 """respond to a right click"""
1001 self.delete('tooltip')
1002 self.tablerowheader.clearSelected()
1003 if hasattr(self, 'rightmenu'):
1004 self.rightmenu.destroy()
1005 rowclicked = self.get_row_clicked(event)
1006 colclicked = self.get_col_clicked(event)
1007 if colclicked == None:
1008 self.rightmenu = self.popupMenu(event, outside=1)
1009 return
1010
1011 if (rowclicked in self.multiplerowlist or self.allrows == True) and colclicked in self.multiplecollist:
1012 self.rightmenu = self.popupMenu(event, rows=self.multiplerowlist, cols=self.multiplecollist)
1013 else:
1014 if 0 <= rowclicked < self.rows and 0 <= colclicked < self.cols:
1015 self.clearSelected()
1016 self.allrows = False
1017 self.setSelectedRow(rowclicked)
1018 self.setSelectedCol(colclicked)
1019 self.drawSelectedRect(self.currentrow, self.currentcol)
1020 self.drawSelectedRow()
1021 if self.isInsideTable(event.x,event.y) == 1:
1022 self.rightmenu = self.popupMenu(event,rows=self.multiplerowlist, cols=self.multiplecollist)
1023 else:
1024 self.rightmenu = self.popupMenu(event, outside=1)
1025 return
1026
1028 """Handle mouse motion on table"""
1029 self.delete('tooltip')
1030 row = self.get_row_clicked(event)
1031 col = self.get_col_clicked(event)
1032 if 0 <= row < self.rows and 0 <= col < self.cols:
1033 self.drawTooltip(row, col)
1034
1035 return
1036
1038 """Move highlighted cell to next cell in row or a new col"""
1039
1040 if hasattr(self, 'cellentry'):
1041 self.cellentry.destroy()
1042 self.currentcol=self.currentcol+1
1043 if self.currentcol >= self.cols-1:
1044 self.currentrow = self.currentrow +1
1045 self.currentcol = self.currentcol+1
1046 self.drawSelectedRect(self.currentrow, self.currentcol)
1047 return
1048
1050 """Move to selected row, updating table"""
1051 row=self.model.getRecordIndex(recname)
1052 self.setSelectedRow(row)
1053 self.drawSelectedRow()
1054 x,y = self.getCanvasPos(row, 0)
1055 self.yview('moveto', y-0.01)
1056 self.tablecolheader.yview('moveto', y)
1057 return
1058
1069
1082 def calculate():
1083
1084 f = self.formulaText.get(1.0, END)
1085 f = f.strip('\n')
1086 self.model.setFormulaAt(f,absrow,col)
1087 value = self.model.doFormula(f)
1088 color = self.model.getColorAt(absrow,col,'fg')
1089 self.drawText(row, col, value, color)
1090 close()
1091 self.mode = 'normal'
1092 return
1093 def clear():
1094 self.formulaText.delete(1.0, END)
1095
1096 self.formulaFrame = Frame(width=w,height=h,bd=3,relief=RIDGE)
1097 self.formulaText = Text(self.formulaFrame, width=30, height=8, bg='white',relief=GROOVE)
1098 self.formulaText.pack(side=LEFT,padx=2,pady=2)
1099 if currformula != None:
1100 self.formulaText.insert(END, Formula.getFormula(currformula))
1101 cancelbutton=Button(self.formulaFrame, text='Cancel',
1102 relief=GROOVE,bg='#99ccff',command=close)
1103 cancelbutton.pack(fill=BOTH,padx=2,pady=2)
1104 donebutton=Button(self.formulaFrame, text='Done',
1105 relief=GROOVE,bg='#99ccff',command=calculate)
1106 donebutton.pack(fill=BOTH,padx=2,pady=2)
1107 '''clrbutton=Button(self.formulaFrame, text='Clear',
1108 relief=GROOVE,bg='#99ccff',command=clear)
1109 clrbutton.pack(fill=BOTH,padx=2,pady=2) '''
1110
1111 self.formulaWin = self.create_window(x1+self.inset,y1+self.inset,
1112 width=w,height=h,
1113 window=self.formulaFrame,anchor='nw',
1114 tag='formulabox')
1115 self.formulaText.focus_set()
1116 return
1117
1132
1133 - def paste(self, event=None):
1134 """Copy from clipboard"""
1135 print self.parentframe.clipboard_get()
1136 return
1137
1139 """Copy cell contents to a temp internal clipboard"""
1140 row = rows[0]; col = cols[0]
1141
1142 self.clipboard = copy.deepcopy(self.model.getCellRecord(row, col))
1143 return
1144
1146 """Paste cell from internal clipboard"""
1147 row = rows[0]; col = cols[0]
1148
1149 val = self.clipboard
1150 self.model.setValueAt(val, row, col)
1151 self.redrawTable()
1152 return
1153
1155 """Copy current selected cols"""
1156 M = self.model
1157 coldata = {}
1158 for col in self.multiplecollist:
1159 name = M.columnNames[col]
1160 coldata[name] = M.getColumnData(columnName=name)
1161 return coldata
1162
1164 """Paste new cols, overwrites existing names"""
1165 M = self.model
1166 for name in coldata:
1167 if name not in M.columnNames:
1168 M.addColumn(name)
1169 for r in range(len(coldata[name])):
1170 val = coldata[name][r]
1171 col = M.columnNames.index(name)
1172 if r >= self.rows:
1173 break
1174 M.setValueAt(val, r, col)
1175 self.redrawTable()
1176 return coldata
1177
1178
1179
1180 - def setcellColor(self, rows, cols=None, newColor=None, key=None, redraw=True):
1181 """Set the cell color for one or more cells and save it in the model color"""
1182
1183 model = self.getModel()
1184 if newColor == None:
1185 import tkColorChooser
1186 ctuple, newColor = tkColorChooser.askcolor(title='pick a color')
1187 if newColor == None:
1188 return
1189
1190 if type(rows) is IntType:
1191 x=rows
1192 rows=[]
1193 rows.append(x)
1194 if self.allrows == True:
1195
1196 rows = range(0,self.rows)
1197 if cols == None:
1198 cols = range(self.cols)
1199 for col in cols:
1200 for row in rows:
1201
1202 model.setColorAt(row, col, color=newColor, key=key)
1203
1204 if redraw == True:
1205 self.redrawTable()
1206 return
1207
1209 """Add left and right click behaviour for canvas, should not have to override
1210 this function, it will take its values from defined dicts in constructor"""
1211
1212 defaultactions = {"Set Fill Color" : lambda : self.setcellColor(rows,cols,key='bg'),
1213 "Set Text Color" : lambda : self.setcellColor(rows,cols,key='fg'),
1214 "Copy" : lambda : self.copyCell(rows, cols),
1215 "Paste" : lambda : self.pasteCell(rows, cols),
1216 "Fill Down" : lambda : self.fillDown(rows, cols),
1217 "Fill Right" : lambda : self.fillAcross(cols, rows),
1218 "Add Row(s)" : lambda : self.addRows(),
1219 "Delete Row(s)" : lambda : self.deleteRow(),
1220 "View Record" : lambda : self.getRecordInfo(row),
1221 "Clear Data" : lambda : self.deleteCells(rows, cols),
1222 "Select All" : self.select_All,
1223 "Auto Fit Columns" : self.autoResizeColumns,
1224 "Filter Records" : self.showFilteringBar,
1225 "New": self.new,
1226 "Load": self.load,
1227 "Save": self.save,
1228 "Import text":self.importTable,
1229 "Export csv": self.exportTable,
1230 "Plot Selected" : self.plotSelected,
1231 "Plot Options" : self.plotSetup,
1232 "Export Table" : self.exportTable,
1233 "Preferences" : self.showtablePrefs,
1234 "Formulae->Value" : lambda : self.convertFormulae(rows, cols)}
1235
1236 main = ["Set Fill Color","Set Text Color","Copy", "Paste", "Fill Down","Fill Right",
1237 "Clear Data", "Add Row(s)" , "Delete Row(s)"]
1238 general = ["Select All", "Auto Fit Columns", "Filter Records", "Preferences"]
1239 filecommands = ['New','Load','Save','Import text','Export csv']
1240 plotcommands = ['Plot Selected','Plot Options']
1241 utilcommands = ["View Record", "Formulae->Value"]
1242
1243 def createSubMenu(parent, label, commands):
1244 menu = Menu(parent, tearoff = 0)
1245 popupmenu.add_cascade(label=label,menu=menu)
1246 for action in commands:
1247 menu.add_command(label=action, command=defaultactions[action])
1248 return menu
1249
1250 def add_commands(fieldtype):
1251 """Add commands to popup menu for column type and specific cell"""
1252 functions = self.columnactions[fieldtype]
1253 for f in functions.keys():
1254 func = getattr(self, functions[f])
1255 popupmenu.add_command(label=f, command= lambda : func(row,col))
1256 return
1257
1258 popupmenu = Menu(self, tearoff = 0)
1259 def popupFocusOut(event):
1260 popupmenu.unpost()
1261
1262 if outside == None:
1263
1264 row = self.get_row_clicked(event)
1265 col = self.get_col_clicked(event)
1266 coltype = self.model.getColumnType(col)
1267 def add_defaultcommands():
1268 """now add general actions for all cells"""
1269 for action in main:
1270 if action == 'Fill Down' and (rows == None or len(rows) <= 1):
1271 continue
1272 if action == 'Fill Right' and (cols == None or len(cols) <= 1):
1273 continue
1274 else:
1275 popupmenu.add_command(label=action, command=defaultactions[action])
1276 return
1277
1278 if self.columnactions.has_key(coltype):
1279 add_commands(coltype)
1280 add_defaultcommands()
1281
1282 for action in general:
1283 popupmenu.add_command(label=action, command=defaultactions[action])
1284
1285 popupmenu.add_separator()
1286 createSubMenu(popupmenu, 'File', filecommands)
1287 createSubMenu(popupmenu, 'Plot', plotcommands)
1288 if outside == None:
1289 createSubMenu(popupmenu, 'Utils', utilcommands)
1290 popupmenu.bind("<FocusOut>", popupFocusOut)
1291 popupmenu.focus_set()
1292 popupmenu.post(event.x_root, event.y_root)
1293 return popupmenu
1294
1295
1296
1298 """Fill down a column, or multiple columns"""
1299 model = self.model
1300
1301
1302 rowlist.remove(rowlist[0])
1303
1304
1305 for col in collist:
1306 val = self.model.getCellRecord(row, col)
1307 f=val
1308 i=1
1309 for r in rowlist:
1310
1311 if Formula.isFormula(f):
1312 newval = model.copyFormula(f, r, col, offset=i)
1313 model.setFormulaAt(newval, r, col)
1314 else:
1315 model.setValueAt(val, r, col)
1316
1317 i+=1
1318
1319 self.redrawTable()
1320 return
1321
1323 """Fill across a row, or multiple rows"""
1324 model = self.model
1325
1326
1327 frstcol = collist[0]
1328 collist.remove(frstcol)
1329
1330 for row in rowlist:
1331
1332 val = self.model.getCellRecord(absr, frstcol)
1333 f=val
1334 i=1
1335 for c in collist:
1336 if Formula.isFormula(f):
1337 newval = model.copyFormula(f, r, c, offset=i, dim='x')
1338 model.setFormulaAt(newval, r, c)
1339 else:
1340 model.setValueAt(val, r, c)
1341 i+=1
1342 self.redrawTable()
1343 return
1344
1346 """Get values for current multiple cell selection"""
1347 if len(self.multiplerowlist) == 0 or len(self.multiplecollist) == 0:
1348 return None
1349 rows = self.multiplerowlist
1350 cols = self.multiplecollist
1351 model = self.model
1352 if len(rows)<1 or len(cols)<1:
1353 return None
1354
1355 if len(rows) == 1:
1356 rows = self.rowrange
1357 lists = []
1358
1359 for c in cols:
1360 x=[]
1361 for r in rows:
1362
1363 val = model.getValueAt(r,c)
1364 if val == None or val == '':
1365 continue
1366 x.append(val)
1367 lists.append(x)
1368 return lists
1369
1371 """Plot the selected data using pylab - if possible"""
1372 from Plot import pylabPlotter
1373 if not hasattr(self, 'pyplot'):
1374 self.pyplot = pylabPlotter()
1375 plotdata = []
1376 for p in self.getSelectionValues():
1377 x = []
1378 fail = False
1379 for d in p:
1380 try:
1381 x.append(float(d))
1382 except:
1383 fail = True
1384 continue
1385 if fail == False:
1386 plotdata.append(x)
1387
1388 pltlabels = self.getplotlabels()
1389 if len(pltlabels) > 2:
1390 self.pyplot.setDataSeries(pltlabels)
1391 self.pyplot.showlegend = 1
1392 self.pyplot.plotCurrent(data=plotdata, graphtype=graphtype)
1393 return
1394
1411
1413 """Get labels for plot series from col labels"""
1414 pltlabels = []
1415 for col in self.multiplecollist:
1416 pltlabels.append(self.model.getColumnLabel(col))
1417 return pltlabels
1418
1419
1420
1422 """Draw the table grid lines"""
1423 self.delete('gridline','text')
1424 rows=len(self.rowrange)
1425 cols=self.cols
1426 w = self.cellwidth
1427 h = self.rowheight
1428 x_start=self.x_start
1429 y_start=self.y_start
1430 x_pos=x_start
1431
1432 if self.vertlines==1:
1433 for col in range(cols+1):
1434 x=self.col_positions[col]
1435 self.create_line(x,y_start,x,y_start+rows*h, tag='gridline',
1436 fill=self.grid_color, width=self.linewidth)
1437 if self.horizlines==1:
1438 for row in range(startrow, endrow+1):
1439 y_pos=y_start+row*h
1440 self.create_line(x_start,y_pos,self.tablewidth,y_pos, tag='gridline',
1441 fill=self.grid_color, width=self.linewidth)
1442 return
1443
1445 """User has clicked to select a cell"""
1446 self.delete('rowheader')
1447 x_start=self.x_start
1448 y_start=self.y_start
1449 h=self.rowheight
1450 rowpos=0
1451 for row in self.rowrange:
1452 x1,y1,x2,y2 = self.getCellCoords(rowpos,0)
1453 self.create_rectangle(0,y1,x_start-2,y2,
1454 fill='gray75',
1455 outline='white',
1456 width=1,
1457 tag='rowheader')
1458 self.create_text(x_start/2,y1+h/2,
1459 text=row+1,
1460 fill='black',
1461 font=self.thefont,
1462 tag='rowheader')
1463 rowpos+=1
1464 return
1465
1467 """User has clicked to select a cell"""
1468 if col >= self.cols:
1469 return
1470 self.delete('currentrect')
1471 bg = self.selectedcolor
1472 if color == None:
1473 color = 'gray25'
1474 w=3
1475 x1,y1,x2,y2 = self.getCellCoords(row,col)
1476 rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
1477 fill=bg,
1478 outline=color,
1479 width=w,
1480 stipple='gray50',
1481 tag='currentrect')
1482
1483
1484 self.lift('celltext'+str(col)+'_'+str(row))
1485 return
1486
1487 - def drawRect(self, row, col, color=None, tag=None, delete=1):
1488 """Cell is colored"""
1489 if delete==1:
1490 self.delete('cellbg'+str(row)+str(col))
1491 if color==None or color==self.cellbackgr:
1492 return
1493 else:
1494 bg=color
1495 if tag==None:
1496 recttag='fillrect'
1497 else:
1498 recttag=tag
1499 w=1
1500 x1,y1,x2,y2 = self.getCellCoords(row,col)
1501 rect = self.create_rectangle(x1+w/2,y1+w/2,x2-w/2,y2-w/2,
1502 fill=bg,
1503 outline=bg,
1504 width=w,
1505 tag=(recttag,'cellbg'+str(row)+str(col)))
1506 self.lower(recttag)
1507 return
1508
1509 - def drawCellEntry(self, row, col, text=None):
1510 """When the user single/double clicks on a text/number cell, bring up entry window"""
1511
1512 if self.editable == False:
1513 return
1514
1515 h=self.rowheight
1516 model=self.getModel()
1517 cellvalue = self.model.getCellRecord(row, col)
1518 if Formula.isFormula(cellvalue):
1519 return
1520 else:
1521 text = self.model.getValueAt(row, col)
1522 x1,y1,x2,y2 = self.getCellCoords(row,col)
1523 w=x2-x1
1524
1525 txtvar = StringVar()
1526 txtvar.set(text)
1527 def callback(e):
1528 value = txtvar.get()
1529 if value == '=':
1530
1531
1532
1533 self.cellentry.destroy()
1534
1535 self.formula_Dialog(row, col)
1536 return
1537
1538 coltype = self.model.getColumnType(col)
1539 if coltype == 'number':
1540 sta = self.checkDataEntry(e)
1541 if sta == 1:
1542 model.setValueAt(value,row,col)
1543 elif coltype == 'text':
1544 model.setValueAt(value,row,col)
1545
1546 color = self.model.getColorAt(row,col,'fg')
1547 self.drawText(row, col, value, color, align=self.align)
1548 if e.keysym=='Return':
1549 self.delete('entry')
1550
1551
1552 return
1553
1554 self.cellentry=Entry(self.parentframe,width=20,
1555 textvariable=txtvar,
1556 bg=self.entrybackgr,
1557 relief=FLAT,
1558 takefocus=1,
1559 font=self.thefont)
1560 self.cellentry.icursor(END)
1561 self.cellentry.bind('<Return>', callback)
1562 self.cellentry.bind('<KeyRelease>', callback)
1563 self.cellentry.focus_set()
1564 self.entrywin=self.create_window(x1+self.inset,y1+self.inset,
1565 width=w-self.inset*2,height=h-self.inset*2,
1566 window=self.cellentry,anchor='nw',
1567 tag='entry')
1568
1569 return
1570
1571 - def checkDataEntry(self,event=None):
1572 """do validation checks on data entry in a widget"""
1573
1574 import re
1575 value=event.widget.get()
1576 if value!='':
1577 try:
1578 value=re.sub(',','.', value)
1579 value=float(value)
1580
1581 except ValueError:
1582 event.widget.configure(bg='red')
1583 return 0
1584 elif value == '':
1585 return 1
1586 return 1
1587
1588 - def drawText(self, row, col, celltxt, fgcolor=None, align=None):
1589 """Draw the text inside a cell area"""
1590 self.delete('celltext'+str(col)+'_'+str(row))
1591 h=self.rowheight
1592 x1,y1,x2,y2 = self.getCellCoords(row,col)
1593 w=x2-x1
1594 wrap = False
1595
1596 if type(celltxt) is types.FloatType or type(celltxt) is types.IntType:
1597 celltxt=str(celltxt)
1598 length = len(celltxt)
1599 if length == 0:
1600 return
1601
1602 if w<=10:
1603 return
1604
1605 if fgcolor == None or fgcolor == "None":
1606 fgcolor = 'black'
1607 if align == None:
1608 align = 'center'
1609 elif align == 'w':
1610 x1 = x1-w/2+1
1611 elif align == 'e':
1612 x1 = x1+w/2-1
1613
1614 if w < 15:
1615 celltxt = '.'
1616 else:
1617 fontsize = self.fontsize
1618 colname = self.model.getColumnName(col)
1619
1620 scale = 8.5 * float(fontsize)/12
1621 size = length * scale
1622 if size > w:
1623 newlength = w / scale
1624
1625 celltxt = celltxt[0:int(math.floor(newlength))]
1626
1627
1628 if self.isLink(celltxt) == True:
1629 haslink=0
1630 linktext=celltxt['text']
1631 if len(linktext) > w/scale or w<28:
1632 linktext=linktext[0:int(w/fontsize*1.2)-2]+'..'
1633 if celltxt['link']!=None and celltxt['link']!='':
1634 f,s = self.thefont
1635 linkfont = (f, s, 'underline')
1636 linkcolor='blue'
1637 haslink=1
1638 else:
1639 linkfont = self.thefont
1640 linkcolor=fgcolor
1641
1642 rect = self.create_text(x1+w/2,y1+h/2,
1643 text=linktext,
1644 fill=linkcolor,
1645 font=linkfont,
1646 tag=('text','hlink','celltext'+str(col)+'_'+str(row)))
1647 if haslink == 1:
1648 self.tag_bind(rect, '<Double-Button-1>', self.check_hyperlink)
1649
1650
1651 else:
1652 rect = self.create_text(x1+w/2,y1+h/2,
1653 text=celltxt,
1654 fill=fgcolor,
1655 font=self.thefont,
1656 anchor=align,
1657 tag=('text','celltext'+str(col)+'_'+str(row)))
1658 return
1659
1661 """Checks if cell is a hyperlink, without using isinstance"""
1662 try:
1663 if cell.has_key('link'):
1664 return True
1665 except:
1666 return False
1667
1669 """Draw the highlight rect for the currently selected row"""
1670 self.delete('rowrect')
1671 row = self.currentrow
1672 x1,y1,x2,y2 = self.getCellCoords(row,0)
1673 x2 = self.tablewidth
1674 rect = self.create_rectangle(x1,y1,x2,y2,
1675 fill=self.rowselectedcolor,
1676 outline=self.rowselectedcolor,
1677 tag='rowrect')
1678 self.lower('rowrect')
1679 self.lower('fillrect')
1680 self.tablerowheader.drawSelectedRows(self.currentrow)
1681 return
1682
1684 """Draw an outline rect fot the current column selection"""
1685 if delete == 1:
1686 self.delete('colrect')
1687 if col == None:
1688 col=self.currentcol
1689 w=2
1690 x1,y1,x2,y2 = self.getCellCoords(0,col)
1691 y2 = self.rows * self.rowheight
1692 rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2+w/2,
1693 outline='blue',width=w,
1694 tag='colrect')
1695 return
1696
1698 """Draw more than one row selection"""
1699 self.delete('multiplesel')
1700 for r in rowlist:
1701 if r not in self.visiblerows or r > self.rows-1:
1702 continue
1703 x1,y1,x2,y2 = self.getCellCoords(r,0)
1704 x2 = self.tablewidth
1705 rect = self.create_rectangle(x1,y1,x2,y2,
1706 fill=self.multipleselectioncolor,
1707 outline=self.rowselectedcolor,
1708 tag=('multiplesel','rowrect'))
1709 self.lower('multiplesel')
1710 self.lower('fillrect')
1711 return
1712
1714 """Draw an outline box for multiple cell selection"""
1715 self.delete('multicellrect')
1716 rows = self.multiplerowlist
1717 cols = self.multiplecollist
1718 w=2
1719 x1,y1,a,b = self.getCellCoords(rows[0],cols[0])
1720 c,d,x2,y2 = self.getCellCoords(rows[len(rows)-1],cols[len(cols)-1])
1721 rect = self.create_rectangle(x1+w/2,y1+w/2,x2,y2,
1722 outline='blue',width=w,activefill='red',activestipple='gray25',
1723 tag='multicellrect')
1724
1725 return
1726
1760
1762 clr = self.getaColor(self.cellbackgr)
1763 if clr != None:
1764 self.cellbackgr = clr
1765 return
1766
1768 clr = self.getaColor(self.grid_color)
1769 if clr != None:
1770 self.grid_color = clr
1771
1772 return
1773
1775 clr = self.getaColor(self.rowselectedcolor)
1776 if clr != None:
1777 self.rowselectedcolor = clr
1778 return
1779
1781 import tkColorChooser
1782 ctuple, newcolor = tkColorChooser.askcolor(title='pick a color', initialcolor=oldcolor,
1783 parent=self.parentframe)
1784 if ctuple == None:
1785 return None
1786 return str(newcolor)
1787
1788
1789
1791 """Show table options dialog using an instance of prefs"""
1792
1793 if self.prefs == None:
1794 self.loadPrefs()
1795 self.prefswindow=Toplevel()
1796 x,y,w,h = self.getGeometry(self.master)
1797 self.prefswindow.geometry('+%s+%s' %(x+w/2,y+h/2))
1798 self.prefswindow.title('Preferences')
1799 self.prefswindow.resizable(width=FALSE, height=FALSE)
1800
1801 frame1=Frame(self.prefswindow)
1802 frame1.pack(side=LEFT)
1803 frame2=Frame(self.prefswindow)
1804 frame2.pack()
1805 def close_prefsdialog():
1806 self.prefswindow.destroy()
1807 row=0
1808 Checkbutton(frame1, text="Show horizontal lines", variable=self.horizlinesvar,
1809 onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
1810 row=row+1
1811 Checkbutton(frame1, text="Show vertical lines", variable=self.vertlinesvar,
1812 onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
1813 row=row+1
1814 Checkbutton(frame1, text="Alternate Row Color", variable=self.alternaterowsvar,
1815 onvalue=1, offvalue=0).grid(row=row,column=0, columnspan=2, sticky='news')
1816 row=row+1
1817 lblrowheight=Label(frame1,text='Row Height:')
1818 lblrowheight.grid(row=row,column=0,padx=3,pady=2)
1819 rowheightentry=Scale(frame1,from_=12,to=50,resolution=1,orient='horizontal',
1820 relief='ridge',variable=self.rowheightvar)
1821 rowheightentry.grid(row=row,column=1,padx=3,pady=2)
1822 row=row+1
1823 lblcellwidth=Label(frame1,text='Cell Width:')
1824 lblcellwidth.grid(row=row,column=0,padx=3,pady=2)
1825 cellwidthentry=Scale(frame1,from_=20,to=500,resolution=10,orient='horizontal',
1826 relief='ridge',variable=self.cellwidthvar)
1827 cellwidthentry.grid(row=row,column=1,padx=3,pady=2)
1828 row=row+1
1829
1830 lbllinewidth=Label(frame1,text='Line Width:')
1831 lbllinewidth.grid(row=row,column=0,padx=3,pady=2)
1832 linewidthentry=Scale(frame1,from_=0,to=10,resolution=1,orient='horizontal',
1833 relief='ridge',variable=self.linewidthvar)
1834 linewidthentry.grid(row=row,column=1,padx=3,pady=2)
1835 row=row+1
1836
1837 rowhdrwidth=Label(frame1,text='Row Header Width:')
1838 rowhdrwidth.grid(row=row,column=0,padx=3,pady=2)
1839 rowhdrentry=Scale(frame1,from_=0,to=300,resolution=10,orient='horizontal',
1840 relief='ridge',variable=self.rowheaderwidthvar)
1841 rowhdrentry.grid(row=row,column=1,padx=3,pady=2)
1842 row=row+1
1843
1844
1845 fts = self.getFonts()
1846
1847 def setFont():
1848 self.thefont = self.fontbox.getcurselection()
1849 return
1850
1851 import Pmw
1852 self.fontbox = Pmw.ScrolledListBox(frame2,
1853 items=(fts),
1854 labelpos='w',
1855 label_text='Font:',
1856 listbox_height = 6,
1857 selectioncommand = setFont,
1858 usehullsize = 1,
1859 hull_width = 200,
1860 hull_height = 80)
1861 self.fontbox.setvalue(self.prefs.get('celltextfont'))
1862 self.fontbox.grid(row=row,column=0, columnspan=2, sticky='nes', padx=3,pady=2)
1863 row=row+1
1864
1865 lblfontsize=Label(frame2,text='Text Size:')
1866 lblfontsize.grid(row=row,column=0,padx=3,pady=2)
1867 fontsizeentry=Scale(frame2,from_=6,to=50,resolution=1,orient='horizontal',
1868 relief='ridge',variable=self.celltextsizevar)
1869
1870 fontsizeentry.grid(row=row,column=1, sticky='wens',padx=3,pady=2)
1871 row=row+1
1872
1873
1874 lbl=Label(frame2,text='Alignment:')
1875 lbl.grid(row=row,column=0,padx=3,pady=2)
1876 alignentry_button=Menubutton(frame2,textvariable=self.cellalignvar,
1877 relief=RAISED,width=16)
1878 alignentry_menu=Menu(alignentry_button,tearoff=0)
1879 alignentry_button['menu'] = alignentry_menu
1880 alignments=['center','w','e']
1881 for text in alignments:
1882 alignentry_menu.add_radiobutton(label=text,
1883 variable=self.cellalignvar,
1884 value=text,
1885 indicatoron=1)
1886 alignentry_button.grid(row=row,column=1, sticky='nes', padx=3,pady=2)
1887 row=row+1
1888
1889
1890 cellbackgrbutton = Button(frame2, text='table background', bg=self.cellbackgr,
1891 relief='groove', command=self.setcellbackgr)
1892 cellbackgrbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
1893 row=row+1
1894 grid_colorbutton = Button(frame2, text='grid color', bg=self.grid_color,
1895 foreground='black', highlightcolor='white',
1896 relief='groove', command=self.setgrid_color)
1897 grid_colorbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
1898 row=row+1
1899 rowselectedcolorbutton = Button(frame2, text='row highlight color', bg=self.rowselectedcolor,
1900 foreground='black', highlightcolor='white',
1901 relief='groove', command=self.setrowselectedcolor)
1902 rowselectedcolorbutton.grid(row=row,column=0,columnspan=2, sticky='news',padx=3,pady=2)
1903 row=row+1
1904
1905 frame=Frame(self.prefswindow)
1906 frame.pack()
1907
1908 b = Button(frame, text="Apply Settings", command=self.applyPrefs)
1909 b.grid(row=row,column=1,columnspan=2,sticky='news',padx=4,pady=4)
1910
1911
1912 c=Button(frame,text='Close', command=close_prefsdialog)
1913 c.grid(row=row,column=0,sticky='news',padx=4,pady=4)
1914 self.prefswindow.focus_set()
1915 self.prefswindow.grab_set()
1916 self.prefswindow.wait_window()
1917 return self.prefswindow
1918
1923
1925 """Load table specific prefs from the prefs instance used
1926 if they are not present, create them."""
1927
1928 if prefs==None:
1929 prefs=Preferences('Table',{'check_for_update':1})
1930 self.prefs = prefs
1931 defaultprefs = {'horizlines':self.horizlines, 'vertlines':self.vertlines,
1932 'alternaterows':self.alternaterows,
1933 'rowheight':self.rowheight,
1934 'cellwidth':120,
1935 'autoresizecols': 0,
1936 'align': 'center',
1937 'celltextsize':11, 'celltextfont':'Arial',
1938 'cellbackgr': self.cellbackgr, 'grid_color': self.grid_color,
1939 'linewidth' : self.linewidth,
1940 'rowselectedcolor': self.rowselectedcolor,
1941 'rowheaderwidth': self.rowheaderwidth}
1942
1943 for prop in defaultprefs.keys():
1944 try:
1945 self.prefs.get(prop);
1946 except:
1947 self.prefs.set(prop, defaultprefs[prop])
1948 self.defaultprefs = defaultprefs
1949
1950
1951 self.rowheightvar = IntVar()
1952 self.rowheightvar.set(self.prefs.get('rowheight'))
1953 self.rowheight = self.rowheightvar.get()
1954 self.cellwidthvar = IntVar()
1955 self.cellwidthvar.set(self.prefs.get('cellwidth'))
1956 self.cellwidth = self.cellwidthvar.get()
1957 self.cellalignvar = StringVar()
1958 self.cellalignvar.set(self.prefs.get('align'))
1959 self.align = self.cellalignvar.get()
1960 self.linewidthvar = IntVar()
1961 self.linewidthvar.set(self.prefs.get('linewidth'))
1962 self.horizlinesvar = IntVar()
1963 self.horizlinesvar.set(self.prefs.get('horizlines'))
1964 self.vertlinesvar = IntVar()
1965 self.vertlinesvar.set(self.prefs.get('vertlines'))
1966 self.alternaterowsvar = IntVar()
1967 self.alternaterowsvar.set(self.prefs.get('alternaterows'))
1968 self.celltextsizevar = IntVar()
1969 self.celltextsizevar.set(self.prefs.get('celltextsize'))
1970 self.cellbackgr = self.prefs.get('cellbackgr')
1971 self.grid_color = self.prefs.get('grid_color')
1972 self.rowselectedcolor = self.prefs.get('rowselectedcolor')
1973 self.fontsize = self.celltextsizevar.get()
1974 self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
1975 self.rowheaderwidthvar = IntVar()
1976 self.rowheaderwidthvar.set(self.prefs.get('rowheaderwidth'))
1977 self.rowheaderwidth = self.rowheaderwidthvar.get()
1978 return
1979
1981 """Save and set the prefs"""
1982 try:
1983 self.prefs.set('horizlines', self.horizlinesvar.get())
1984 self.horizlines = self.horizlinesvar.get()
1985 self.prefs.set('vertlines', self.vertlinesvar.get())
1986 self.vertlines = self.vertlinesvar.get()
1987 self.prefs.set('alternaterows', self.alternaterowsvar.get())
1988 self.alternaterows = self.alternaterowsvar.get()
1989 self.prefs.set('rowheight', self.rowheightvar.get())
1990 self.rowheight = self.rowheightvar.get()
1991 self.prefs.set('cellwidth', self.cellwidthvar.get())
1992 self.cellwidth = self.cellwidthvar.get()
1993 self.prefs.set('align', self.cellalignvar.get())
1994 self.align = self.cellalignvar.get()
1995 self.prefs.set('linewidth', self.linewidthvar.get())
1996 self.linewidth = self.linewidthvar.get()
1997 self.prefs.set('celltextsize', self.celltextsizevar.get())
1998 self.prefs.set('celltextfont', self.fontbox.getcurselection())
1999 self.prefs.set('cellbackgr', self.cellbackgr)
2000 self.prefs.set('grid_color', self.grid_color)
2001 self.prefs.set('rowselectedcolor', self.rowselectedcolor)
2002 self.prefs.set('rowheaderwidth', self.rowheaderwidth)
2003 self.rowheaderwidth = self.rowheaderwidthvar.get()
2004 self.thefont = (self.prefs.get('celltextfont'), self.prefs.get('celltextsize'))
2005 self.fontsize = self.prefs.get('celltextsize')
2006
2007 except ValueError:
2008 pass
2009 self.prefs.save_prefs()
2010 return
2011
2013 """Apply prefs to the table by redrawing"""
2014 self.savePrefs()
2015 self.redrawTable()
2016 return
2017
2025 bgcolorbutton = Button(frame, text=text,command=SetColor)
2026 return bgcolorbutton
2027
2029 """Check if a hyperlink was clicked"""
2030 row = self.get_row_clicked(event)
2031 col = self.get_col_clicked(event)
2032
2033 recdata = self.model.getValueAt(row, col)
2034 try:
2035 link = recdata['link']
2036 import webbrowser
2037 webbrowser.open(link,autoraise=1)
2038 except:
2039 pass
2040 return
2041
2043 """Show progress bar window for loading of data"""
2044 progress_win=Toplevel()
2045 progress_win.title("Please Wait")
2046
2047
2048 progress_win.grab_set()
2049 progress_win.transient(self.parentframe)
2050 if message==None:
2051 message='Working'
2052 lbl = Label(progress_win,text=message,font='Arial 16')
2053
2054 lbl.grid(row=0,column=0,columnspan=2,sticky='news',padx=6,pady=4)
2055 progrlbl = Label(progress_win,text='Progress:')
2056 progrlbl.grid(row=1,column=0,sticky='news',padx=2,pady=4)
2057 import ProgressBar
2058 self.bar = ProgressBar.ProgressBar(progress_win)
2059 self.bar.frame.grid(row=1,column=1,columnspan=2,padx=2,pady=4)
2060
2061 return progress_win
2062
2064 """Call this method to update the table model"""
2065 self.model = model
2066 self.rows = self.model.getRowCount()
2067 self.cols = self.model.getColumnCount()
2068 self.tablewidth = (self.cellwidth)*self.cols
2069 self.tablecolheader = ColumnHeader(self.parentframe, self)
2070 self.tablerowheader = RowHeader(self.parentframe, self)
2071 self.createTableFrame()
2072 return
2073
2075 """Clears all the data and makes a new table"""
2076 from Dialogs import MultipleValDialog
2077 mpDlg = MultipleValDialog(title='Create new table',
2078 initialvalues=(10, 4),
2079 labels=('rows','columns'),
2080 types=('int','int'),
2081 parent=self.parentframe)
2082
2083 if mpDlg.result == True:
2084 rows = mpDlg.results[0]
2085 cols = mpDlg.results[1]
2086 model = TableModel(rows=rows,columns=cols)
2087 self.updateModel(model)
2088 return
2089
2090 - def load(self, filename=None):
2091 """load from a file"""
2092 if filename == None:
2093 filename = tkFileDialog.askopenfilename(parent=self.master,
2094 defaultextension='.table',
2095 initialdir=os.getcwd(),
2096 filetypes=[("pickle","*.table"),
2097 ("All files","*.*")])
2098 if not os.path.exists(filename):
2099 print 'file does not exist'
2100 return
2101 if filename:
2102 self.model.load(filename)
2103 self.redrawTable()
2104 return
2105
2106 - def save(self, filename=None):
2107 """Save model to pickle file"""
2108 if filename == None:
2109 filename = tkFileDialog.asksaveasfilename(parent=self.master,
2110 defaultextension='.table',
2111 initialdir=os.getcwd(),
2112 filetypes=[("pickle","*.table"),
2113 ("All files","*.*")])
2114 if filename:
2115 self.model.save(filename)
2116 return
2117
2128
2135
2136 @classmethod
2138 """Check the OS we are in"""
2139 ostyp=''
2140 var_s=['OSTYPE','OS']
2141 for var in var_s:
2142 if os.environ.has_key(var):
2143 ostyp=string.lower(os.environ[var])
2144
2145 ostyp=ostyp.lower()
2146 if ostyp.find('windows')!=-1:
2147 ostyp='windows'
2148 elif ostyp.find('darwin')!=-1 or ostyp.find('apple')!=-1:
2149 ostyp='mac'
2150 elif ostyp.find('linux')!=-1:
2151 ostyp='linux'
2152 else:
2153 ostyp='unknown'
2154 try:
2155 info=os.uname()
2156 except:
2157 pass
2158 ostyp=info[0].lower()
2159 if ostyp.find('darwin')!=-1:
2160 ostyp='mac'
2161 return ostyp
2162
2164 """Get frame geometry"""
2165 return frame.winfo_rootx(), frame.winfo_rooty(), frame.winfo_width(), frame.winfo_height()
2166
2168 """Class that takes it's size and rendering from a parent table
2169 and column names from the table model."""
2170
2171 - def __init__(self, parent=None, table=None):
2172 Canvas.__init__(self, parent, bg='gray25', width=500, height=20)
2173 self.thefont='Arial 14'
2174 if table != None:
2175 self.table = table
2176 self.height = 20
2177 self.model = self.table.getModel()
2178 self.config(width=self.table.width)
2179
2180 self.columnlabels = self.model.columnlabels
2181 self.bind('<Button-1>',self.handle_left_click)
2182 self.bind("<ButtonRelease-1>", self.handle_left_release)
2183 self.bind('<B1-Motion>', self.handle_mouse_drag)
2184 self.bind('<Motion>', self.handle_mouse_move)
2185 self.bind('<Shift-Button-1>', self.handle_left_shift_click)
2186 if self.table.ostyp=='mac':
2187
2188 self.bind("<Button-2>", self.handle_right_click)
2189 self.bind('<Shift-Button-1>',self.handle_right_click)
2190 else:
2191 self.bind("<Button-3>", self.handle_right_click)
2192 self.thefont = self.table.thefont
2193 return
2194
2196 cols = self.model.getColumnCount()
2197 self.tablewidth=self.table.tablewidth
2198 self.configure(scrollregion=(0,0, self.table.tablewidth+self.table.x_start, self.height))
2199 self.delete('gridline','text')
2200 self.delete('rect')
2201 self.atdivider = None
2202
2203 h=self.height
2204 x_start=self.table.x_start
2205 if cols == 0:
2206 return
2207 for col in self.table.visiblecols:
2208 colname=self.model.columnNames[col]
2209 if not self.model.columnlabels.has_key(colname):
2210 self.model.columnlabels[colname]=colname
2211 collabel = self.model.columnlabels[colname]
2212 if self.model.columnwidths.has_key(colname):
2213 w = self.model.columnwidths[colname]
2214 else:
2215 w = self.table.cellwidth
2216 x=self.table.col_positions[col]
2217
2218 if len(collabel)>w/10:
2219 collabel=collabel[0:int(w)/12]+'.'
2220 line = self.create_line(x, 0, x, h, tag=('gridline', 'vertline'),
2221 fill='white', width=2)
2222
2223 self.create_text(x+w/2,h/2,
2224 text=collabel,
2225 fill='white',
2226 font=self.thefont,
2227 tag='text')
2228
2229
2230 x=self.table.col_positions[col+1]
2231 self.create_line(x,0, x,h, tag='gridline',
2232 fill='white', width=2)
2233
2234 return
2235
2237 """Does cell selection when mouse is clicked on canvas"""
2238 self.delete('rect')
2239 self.table.delete('entry')
2240 self.table.delete('multicellrect')
2241 colclicked = self.table.get_col_clicked(event)
2242 if colclicked == None:
2243 return
2244
2245 self.table.allrows = True
2246 self.table.setSelectedCol(colclicked)
2247
2248 if self.atdivider == 1:
2249 return
2250 self.drawRect(self.table.currentcol)
2251
2252 self.draggedcol = None
2253 self.drawRect(self.table.currentcol, tag='dragrect',
2254 color='red', outline='white')
2255 if hasattr(self, 'rightmenu'):
2256 self.rightmenu.destroy()
2257
2258 self.table.drawSelectedCol()
2259 return
2260
2262 """When mouse released implement resize or col move"""
2263 self.delete('dragrect')
2264 if self.atdivider == 1:
2265
2266 x=int(self.canvasx(event.x))
2267 col = self.table.currentcol
2268 x1,y1,x2,y2 = self.table.getCellCoords(0,col)
2269 newwidth=x - x1
2270 if newwidth < 5:
2271 newwidth=5
2272 self.table.resizeColumn(col, newwidth)
2273 self.table.delete('resizeline')
2274 self.delete('resizeline')
2275 self.delete('resizesymbol')
2276 self.atdivider = 0
2277 return
2278 self.delete('resizesymbol')
2279
2280 if self.draggedcol != None and self.table.currentcol != self.draggedcol:
2281 self.model.moveColumn(self.table.currentcol, self.draggedcol)
2282 self.table.setSelectedCol(self.draggedcol)
2283 self.table.redrawTable()
2284 self.table.drawSelectedCol(self.table.currentcol)
2285 self.drawRect(self.table.currentcol)
2286 return
2287
2289 """Handle column drag, will be either to move cols or resize"""
2290 x=int(self.canvasx(event.x))
2291 if self.atdivider == 1:
2292 self.table.delete('resizeline')
2293 self.delete('resizeline')
2294 self.table.create_line(x, 0, x, self.table.rowheight*self.table.rows,
2295 width=2, fill='gray', tag='resizeline')
2296 self.create_line(x, 0, x, self.height,
2297 width=2, fill='gray', tag='resizeline')
2298 return
2299 else:
2300 w = self.table.cellwidth
2301 self.draggedcol = self.table.get_col_clicked(event)
2302 x1, y1, x2, y2 = self.coords('dragrect')
2303 x=int(self.canvasx(event.x))
2304 y = self.canvasy(event.y)
2305 self.move('dragrect', x-x1-w/2, 0)
2306
2307 return
2308
2309 - def within(self, val, l, d):
2310 """Utility funtion to see if val is within d of any
2311 items in the list l"""
2312 for v in l:
2313 if abs(val-v) <= d:
2314 return 1
2315 return 0
2316
2318 """Handle mouse moved in header, if near divider draw resize symbol"""
2319 self.delete('resizesymbol')
2320 w=self.table.cellwidth
2321 h=self.height
2322 x_start=self.table.x_start
2323
2324 x=int(self.canvasx(event.x))
2325 if x > self.tablewidth+w:
2326 return
2327
2328 if x!=x_start and self.within(x, self.table.col_positions, 4):
2329 col = self.table.get_col_clicked(event)
2330 if col == None:
2331 return
2332 self.draw_resize_symbol(col)
2333 self.atdivider = 1
2334 else:
2335 self.atdivider = 0
2336 return
2337
2343
2345 self.rightmenu.destroy()
2346 return
2347
2349 """Handle shift click, for selecting multiple cols"""
2350 self.table.delete('colrect')
2351 self.delete('rect')
2352 currcol = self.table.currentcol
2353 colclicked = self.table.get_col_clicked(event)
2354 if colclicked > currcol:
2355 self.table.multiplecollist = range(currcol, colclicked+1)
2356 elif colclicked < currcol:
2357 self.table.multiplecollist = range(colclicked, currcol+1)
2358 else:
2359 return
2360
2361
2362 for c in self.table.multiplecollist:
2363 self.drawRect(c, delete=0)
2364 self.table.drawSelectedCol(c, delete=0)
2365 return
2366
2368 """Add left and right click behaviour for column header"""
2369 colname = self.model.columnNames[self.table.currentcol]
2370 collabel = self.model.columnlabels[colname]
2371 currcol = self.table.currentcol
2372 popupmenu = Menu(self, tearoff = 0)
2373 def popupFocusOut(event):
2374 popupmenu.unpost()
2375 popupmenu.add_command(label="Rename Column", command=self.relabel_Column)
2376 popupmenu.add_command(label="Sort by "+ collabel, command=lambda : self.table.sortTable(currcol))
2377 popupmenu.add_command(label="Sort by "+ collabel +' (descending)', command=lambda : self.table.sortTable(currcol,reverse=1))
2378 popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn)
2379 popupmenu.add_command(label="Add New Column", command=self.table.addColumn)
2380
2381 popupmenu.bind("<FocusOut>", popupFocusOut)
2382
2383 popupmenu.focus_set()
2384 popupmenu.post(event.x_root, event.y_root)
2385 return popupmenu
2386
2388 col=self.table.currentcol
2389 ans = tkSimpleDialog.askstring("New column name?", "Enter new name:")
2390 if ans !=None:
2391 if ans == '':
2392 tkMessageBox.showwarning("Error", "Name should not be blank.")
2393 return
2394 else:
2395 self.model.relabel_Column(col, ans)
2396 self.redraw()
2397 return
2398
2400 """Draw a symbol to show that col can be resized when mouse here"""
2401 self.delete('resizesymbol')
2402 w=self.table.cellwidth
2403 h=self.height
2404
2405
2406 wdth=1
2407 hfac1=0.2
2408 hfac2=0.4
2409 x_start=self.table.x_start
2410 x1,y1,x2,y2 = self.table.getCellCoords(0,col)
2411
2412 self.create_polygon(x2-3,h/4, x2-10,h/2, x2-3,h*3/4, tag='resizesymbol',
2413 fill='white', outline='gray', width=wdth)
2414 self.create_polygon(x2+2,h/4, x2+10,h/2, x2+2,h*3/4, tag='resizesymbol',
2415 fill='white', outline='gray', width=wdth)
2416 return
2417
2418 - def drawRect(self,col, tag=None, color=None, outline=None, delete=1):
2419 """User has clicked to select a col"""
2420 if tag==None:
2421 tag='rect'
2422 if color==None:
2423 color='#0099CC'
2424 if outline==None:
2425 outline='gray25'
2426 if delete == 1:
2427 self.delete(tag)
2428 w=2
2429 x1,y1,x2,y2 = self.table.getCellCoords(0,col)
2430 rect = self.create_rectangle(x1,y1-w,x2,self.height,
2431 fill=color,
2432 outline=outline,
2433 width=w,
2434 stipple='gray50',
2435 tag=tag)
2436 self.lower(tag)
2437 return
2438
2440 """Class that displays the row headings on the table
2441 takes it's size and rendering from the parent table
2442 This also handles row/record selection as opposed to cell
2443 selection"""
2445 Canvas.__init__(self, parent, bg='gray75', width=width, height=None)
2446
2447 if table != None:
2448 self.table = table
2449 self.width = width
2450 self.x_start = 0
2451 self.inset = 1
2452 self.config(height = self.table.height)
2453 self.startrow = self.endrow = None
2454 self.model = self.table.getModel()
2455 self.bind('<Button-1>',self.handle_left_click)
2456 self.bind("<ButtonRelease-1>", self.handle_left_release)
2457 self.bind("<Control-Button-1>", self.handle_left_ctrl_click)
2458 self.bind('<Button-3>',self.handle_right_click)
2459 self.bind('<B1-Motion>', self.handle_mouse_drag)
2460
2461 return
2462
2464 """Redraw row header"""
2465
2466 self.height = self.table.rowheight * self.table.rows+10
2467 self.configure(scrollregion=(0,0, self.width, self.height))
2468 self.delete('rowheader','text')
2469 self.delete('rect')
2470 w = float(self.width)
2471 h = self.table.rowheight
2472 x = self.x_start+w/2
2473 if align == 'w':
2474 x = x-w/2+3
2475 elif align == 'e':
2476 x = x+w/2-3
2477 for row in self.table.visiblerows:
2478 if showkeys == True:
2479 text = self.model.getRecName(row)
2480 else:
2481 text = row+1
2482 x1,y1,x2,y2 = self.table.getCellCoords(row,0)
2483 self.create_rectangle(0,y1,w-1,y2,
2484 fill='gray75',
2485 outline='white',
2486 width=1,
2487 tag='rowheader')
2488 self.create_text(x,y1+h/2,
2489 text=text,
2490 fill='black',
2491 font=self.table.thefont,
2492 tag='text', anchor=align)
2493 return
2494
2496 """Set width"""
2497 self.width = w
2498 self.redraw()
2499 return
2500
2502 self.delete('rect')
2503 return
2504
2517
2521
2523 """Handle ctrl clicks - for multiple row selections"""
2524 rowclicked = self.table.get_row_clicked(event)
2525 multirowlist = self.table.multiplerowlist
2526 if 0 <= rowclicked < self.table.rows:
2527 if rowclicked not in multirowlist:
2528 multirowlist.append(rowclicked)
2529 else:
2530 multirowlist.remove(rowclicked)
2531 self.table.drawMultipleRows(multirowlist)
2532 self.drawSelectedRows(multirowlist)
2533 return
2534
2538
2539 '''def handle_mouse_drag(self, event):
2540 """Handle mouse drag for mult row selection"""
2541 rowover = self.table.get_row_clicked(event)
2542 colover = self.table.get_col_clicked(event)
2543 if colover == None or rowover == None:
2544 return'''
2545
2547 """Handle mouse moved with button held down, multiple selections"""
2548 if hasattr(self, 'cellentry'):
2549 self.cellentry.destroy()
2550 rowover = self.table.get_row_clicked(event)
2551 colover = self.table.get_col_clicked(event)
2552 if rowover == None:
2553 return
2554 if rowover >= self.table.rows or self.startrow > self.table.rows:
2555 return
2556 else:
2557 self.endrow = rowover
2558
2559 if self.endrow != self.startrow:
2560 if self.endrow < self.startrow:
2561 rowlist=range(self.endrow, self.startrow+1)
2562 else:
2563 rowlist=range(self.startrow, self.endrow+1)
2564 self.drawSelectedRows(rowlist)
2565 self.table.multiplerowlist = rowlist
2566 self.table.drawMultipleRows(rowlist)
2567 else:
2568 self.table.multiplerowlist = []
2569 self.table.multiplerowlist.append(rowover)
2570 self.drawSelectedRows(rowover)
2571 self.table.drawMultipleRows(self.table.multiplerowlist)
2572 return
2573
2575 """Draw selected rows, accepts a list or integer"""
2576 self.delete('rect')
2577 if type(rows) is not ListType:
2578 rowlist=[]
2579 rowlist.append(rows)
2580 else:
2581 rowlist = rows
2582 for r in rowlist:
2583 if r not in self.table.visiblerows:
2584 continue
2585 self.drawRect(r, delete=0)
2586 return
2587
2589 """Draw a rect representing row selection"""
2590 if tag==None:
2591 tag='rect'
2592 if color==None:
2593 color='#0099CC'
2594 if outline==None:
2595 outline='gray25'
2596 if delete == 1:
2597 self.delete(tag)
2598 w=0
2599 i = self.inset
2600 x1,y1,x2,y2 = self.table.getCellCoords(row, 0)
2601 rect = self.create_rectangle(0+i,y1+i,self.width-i,y2,
2602 fill=color,
2603 outline=outline,
2604 width=w,
2605 tag=tag)
2606 self.lift('text')
2607 return
2608
2623