Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
ea6041cd
Commit
ea6041cd
authored
Oct 22, 2019
by
Raymond Hettinger
Committed by
GitHub
Oct 22, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-38539: Finish rename of ss1.py to spreadsheet.py (GH-16896)
parent
a4aeb336
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
0 additions
and
829 deletions
+0
-829
Tools/demo/ss1.py
Tools/demo/ss1.py
+0
-829
No files found.
Tools/demo/ss1.py
deleted
100755 → 0
View file @
a4aeb336
#!/usr/bin/env python3
"""
SS1 -- a spreadsheet-like application.
"""
import
os
import
re
import
sys
from
xml.parsers
import
expat
from
xml.sax.saxutils
import
escape
LEFT
,
CENTER
,
RIGHT
=
"LEFT"
,
"CENTER"
,
"RIGHT"
def
ljust
(
x
,
n
):
return
x
.
ljust
(
n
)
def
center
(
x
,
n
):
return
x
.
center
(
n
)
def
rjust
(
x
,
n
):
return
x
.
rjust
(
n
)
align2action
=
{
LEFT
:
ljust
,
CENTER
:
center
,
RIGHT
:
rjust
}
align2xml
=
{
LEFT
:
"left"
,
CENTER
:
"center"
,
RIGHT
:
"right"
}
xml2align
=
{
"left"
:
LEFT
,
"center"
:
CENTER
,
"right"
:
RIGHT
}
align2anchor
=
{
LEFT
:
"w"
,
CENTER
:
"center"
,
RIGHT
:
"e"
}
def
sum
(
seq
):
total
=
0
for
x
in
seq
:
if
x
is
not
None
:
total
+=
x
return
total
class
Sheet
:
def
__init__
(
self
):
self
.
cells
=
{}
# {(x, y): cell, ...}
self
.
ns
=
dict
(
cell
=
self
.
cellvalue
,
cells
=
self
.
multicellvalue
,
sum
=
sum
,
)
def
cellvalue
(
self
,
x
,
y
):
cell
=
self
.
getcell
(
x
,
y
)
if
hasattr
(
cell
,
'recalc'
):
return
cell
.
recalc
(
self
.
ns
)
else
:
return
cell
def
multicellvalue
(
self
,
x1
,
y1
,
x2
,
y2
):
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
seq
=
[]
for
y
in
range
(
y1
,
y2
+
1
):
for
x
in
range
(
x1
,
x2
+
1
):
seq
.
append
(
self
.
cellvalue
(
x
,
y
))
return
seq
def
getcell
(
self
,
x
,
y
):
return
self
.
cells
.
get
((
x
,
y
))
def
setcell
(
self
,
x
,
y
,
cell
):
assert
x
>
0
and
y
>
0
assert
isinstance
(
cell
,
BaseCell
)
self
.
cells
[
x
,
y
]
=
cell
def
clearcell
(
self
,
x
,
y
):
try
:
del
self
.
cells
[
x
,
y
]
except
KeyError
:
pass
def
clearcells
(
self
,
x1
,
y1
,
x2
,
y2
):
for
xy
in
self
.
selectcells
(
x1
,
y1
,
x2
,
y2
):
del
self
.
cells
[
xy
]
def
clearrows
(
self
,
y1
,
y2
):
self
.
clearcells
(
0
,
y1
,
sys
.
maxsize
,
y2
)
def
clearcolumns
(
self
,
x1
,
x2
):
self
.
clearcells
(
x1
,
0
,
x2
,
sys
.
maxsize
)
def
selectcells
(
self
,
x1
,
y1
,
x2
,
y2
):
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
return
[(
x
,
y
)
for
x
,
y
in
self
.
cells
if
x1
<=
x
<=
x2
and
y1
<=
y
<=
y2
]
def
movecells
(
self
,
x1
,
y1
,
x2
,
y2
,
dx
,
dy
):
if
dx
==
0
and
dy
==
0
:
return
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
assert
x1
+
dx
>
0
and
y1
+
dy
>
0
new
=
{}
for
x
,
y
in
self
.
cells
:
cell
=
self
.
cells
[
x
,
y
]
if
hasattr
(
cell
,
'renumber'
):
cell
=
cell
.
renumber
(
x1
,
y1
,
x2
,
y2
,
dx
,
dy
)
if
x1
<=
x
<=
x2
and
y1
<=
y
<=
y2
:
x
+=
dx
y
+=
dy
new
[
x
,
y
]
=
cell
self
.
cells
=
new
def
insertrows
(
self
,
y
,
n
):
assert
n
>
0
self
.
movecells
(
0
,
y
,
sys
.
maxsize
,
sys
.
maxsize
,
0
,
n
)
def
deleterows
(
self
,
y1
,
y2
):
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
self
.
clearrows
(
y1
,
y2
)
self
.
movecells
(
0
,
y2
+
1
,
sys
.
maxsize
,
sys
.
maxsize
,
0
,
y1
-
y2
-
1
)
def
insertcolumns
(
self
,
x
,
n
):
assert
n
>
0
self
.
movecells
(
x
,
0
,
sys
.
maxsize
,
sys
.
maxsize
,
n
,
0
)
def
deletecolumns
(
self
,
x1
,
x2
):
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
self
.
clearcells
(
x1
,
x2
)
self
.
movecells
(
x2
+
1
,
0
,
sys
.
maxsize
,
sys
.
maxsize
,
x1
-
x2
-
1
,
0
)
def
getsize
(
self
):
maxx
=
maxy
=
0
for
x
,
y
in
self
.
cells
:
maxx
=
max
(
maxx
,
x
)
maxy
=
max
(
maxy
,
y
)
return
maxx
,
maxy
def
reset
(
self
):
for
cell
in
self
.
cells
.
values
():
if
hasattr
(
cell
,
'reset'
):
cell
.
reset
()
def
recalc
(
self
):
self
.
reset
()
for
cell
in
self
.
cells
.
values
():
if
hasattr
(
cell
,
'recalc'
):
cell
.
recalc
(
self
.
ns
)
def
display
(
self
):
maxx
,
maxy
=
self
.
getsize
()
width
,
height
=
maxx
+
1
,
maxy
+
1
colwidth
=
[
1
]
*
width
full
=
{}
# Add column heading labels in row 0
for
x
in
range
(
1
,
width
):
full
[
x
,
0
]
=
text
,
alignment
=
colnum2name
(
x
),
RIGHT
colwidth
[
x
]
=
max
(
colwidth
[
x
],
len
(
text
))
# Add row labels in column 0
for
y
in
range
(
1
,
height
):
full
[
0
,
y
]
=
text
,
alignment
=
str
(
y
),
RIGHT
colwidth
[
0
]
=
max
(
colwidth
[
0
],
len
(
text
))
# Add sheet cells in columns with x>0 and y>0
for
(
x
,
y
),
cell
in
self
.
cells
.
items
():
if
x
<=
0
or
y
<=
0
:
continue
if
hasattr
(
cell
,
'recalc'
):
cell
.
recalc
(
self
.
ns
)
if
hasattr
(
cell
,
'format'
):
text
,
alignment
=
cell
.
format
()
assert
isinstance
(
text
,
str
)
assert
alignment
in
(
LEFT
,
CENTER
,
RIGHT
)
else
:
text
=
str
(
cell
)
if
isinstance
(
cell
,
str
):
alignment
=
LEFT
else
:
alignment
=
RIGHT
full
[
x
,
y
]
=
(
text
,
alignment
)
colwidth
[
x
]
=
max
(
colwidth
[
x
],
len
(
text
))
# Calculate the horizontal separator line (dashes and dots)
sep
=
""
for
x
in
range
(
width
):
if
sep
:
sep
+=
"+"
sep
+=
"-"
*
colwidth
[
x
]
# Now print The full grid
for
y
in
range
(
height
):
line
=
""
for
x
in
range
(
width
):
text
,
alignment
=
full
.
get
((
x
,
y
))
or
(
""
,
LEFT
)
text
=
align2action
[
alignment
](
text
,
colwidth
[
x
])
if
line
:
line
+=
'|'
line
+=
text
print
(
line
)
if
y
==
0
:
print
(
sep
)
def
xml
(
self
):
out
=
[
'<spreadsheet>'
]
for
(
x
,
y
),
cell
in
self
.
cells
.
items
():
if
hasattr
(
cell
,
'xml'
):
cellxml
=
cell
.
xml
()
else
:
cellxml
=
'<value>%s</value>'
%
escape
(
cell
)
out
.
append
(
'<cell row="%s" col="%s">
\
n
%s
\
n
</cell>'
%
(
y
,
x
,
cellxml
))
out
.
append
(
'</spreadsheet>'
)
return
'
\
n
'
.
join
(
out
)
def
save
(
self
,
filename
):
text
=
self
.
xml
()
with
open
(
filename
,
"w"
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
if
text
and
not
text
.
endswith
(
'
\
n
'
):
f
.
write
(
'
\
n
'
)
def
load
(
self
,
filename
):
with
open
(
filename
,
'rb'
)
as
f
:
SheetParser
(
self
).
parsefile
(
f
)
class
SheetParser
:
def
__init__
(
self
,
sheet
):
self
.
sheet
=
sheet
def
parsefile
(
self
,
f
):
parser
=
expat
.
ParserCreate
()
parser
.
StartElementHandler
=
self
.
startelement
parser
.
EndElementHandler
=
self
.
endelement
parser
.
CharacterDataHandler
=
self
.
data
parser
.
ParseFile
(
f
)
def
startelement
(
self
,
tag
,
attrs
):
method
=
getattr
(
self
,
'start_'
+
tag
,
None
)
if
method
:
method
(
attrs
)
self
.
texts
=
[]
def
data
(
self
,
text
):
self
.
texts
.
append
(
text
)
def
endelement
(
self
,
tag
):
method
=
getattr
(
self
,
'end_'
+
tag
,
None
)
if
method
:
method
(
""
.
join
(
self
.
texts
))
def
start_cell
(
self
,
attrs
):
self
.
y
=
int
(
attrs
.
get
(
"row"
))
self
.
x
=
int
(
attrs
.
get
(
"col"
))
def
start_value
(
self
,
attrs
):
self
.
fmt
=
attrs
.
get
(
'format'
)
self
.
alignment
=
xml2align
.
get
(
attrs
.
get
(
'align'
))
start_formula
=
start_value
def
end_int
(
self
,
text
):
try
:
self
.
value
=
int
(
text
)
except
(
TypeError
,
ValueError
):
self
.
value
=
None
end_long
=
end_int
def
end_double
(
self
,
text
):
try
:
self
.
value
=
float
(
text
)
except
(
TypeError
,
ValueError
):
self
.
value
=
None
def
end_complex
(
self
,
text
):
try
:
self
.
value
=
complex
(
text
)
except
(
TypeError
,
ValueError
):
self
.
value
=
None
def
end_string
(
self
,
text
):
self
.
value
=
text
def
end_value
(
self
,
text
):
if
isinstance
(
self
.
value
,
BaseCell
):
self
.
cell
=
self
.
value
elif
isinstance
(
self
.
value
,
str
):
self
.
cell
=
StringCell
(
self
.
value
,
self
.
fmt
or
"%s"
,
self
.
alignment
or
LEFT
)
else
:
self
.
cell
=
NumericCell
(
self
.
value
,
self
.
fmt
or
"%s"
,
self
.
alignment
or
RIGHT
)
def
end_formula
(
self
,
text
):
self
.
cell
=
FormulaCell
(
text
,
self
.
fmt
or
"%s"
,
self
.
alignment
or
RIGHT
)
def
end_cell
(
self
,
text
):
self
.
sheet
.
setcell
(
self
.
x
,
self
.
y
,
self
.
cell
)
class
BaseCell
:
__init__
=
None
# Must provide
"""Abstract base class for sheet cells.
Subclasses may but needn't provide the following APIs:
cell.reset() -- prepare for recalculation
cell.recalc(ns) -> value -- recalculate formula
cell.format() -> (value, alignment) -- return formatted value
cell.xml() -> string -- return XML
"""
class
NumericCell
(
BaseCell
):
def
__init__
(
self
,
value
,
fmt
=
"%s"
,
alignment
=
RIGHT
):
assert
isinstance
(
value
,
(
int
,
float
,
complex
))
assert
alignment
in
(
LEFT
,
CENTER
,
RIGHT
)
self
.
value
=
value
self
.
fmt
=
fmt
self
.
alignment
=
alignment
def
recalc
(
self
,
ns
):
return
self
.
value
def
format
(
self
):
try
:
text
=
self
.
fmt
%
self
.
value
except
:
text
=
str
(
self
.
value
)
return
text
,
self
.
alignment
def
xml
(
self
):
method
=
getattr
(
self
,
'_xml_'
+
type
(
self
.
value
).
__name__
)
return
'<value align="%s" format="%s">%s</value>'
%
(
align2xml
[
self
.
alignment
],
self
.
fmt
,
method
())
def
_xml_int
(
self
):
if
-
2
**
31
<=
self
.
value
<
2
**
31
:
return
'<int>%s</int>'
%
self
.
value
else
:
return
'<long>%s</long>'
%
self
.
value
def
_xml_float
(
self
):
return
'<double>%r</double>'
%
self
.
value
def
_xml_complex
(
self
):
return
'<complex>%r</complex>'
%
self
.
value
class
StringCell
(
BaseCell
):
def
__init__
(
self
,
text
,
fmt
=
"%s"
,
alignment
=
LEFT
):
assert
isinstance
(
text
,
str
)
assert
alignment
in
(
LEFT
,
CENTER
,
RIGHT
)
self
.
text
=
text
self
.
fmt
=
fmt
self
.
alignment
=
alignment
def
recalc
(
self
,
ns
):
return
self
.
text
def
format
(
self
):
return
self
.
text
,
self
.
alignment
def
xml
(
self
):
s
=
'<value align="%s" format="%s"><string>%s</string></value>'
return
s
%
(
align2xml
[
self
.
alignment
],
self
.
fmt
,
escape
(
self
.
text
))
class
FormulaCell
(
BaseCell
):
def
__init__
(
self
,
formula
,
fmt
=
"%s"
,
alignment
=
RIGHT
):
assert
alignment
in
(
LEFT
,
CENTER
,
RIGHT
)
self
.
formula
=
formula
self
.
translated
=
translate
(
self
.
formula
)
self
.
fmt
=
fmt
self
.
alignment
=
alignment
self
.
reset
()
def
reset
(
self
):
self
.
value
=
None
def
recalc
(
self
,
ns
):
if
self
.
value
is
None
:
try
:
self
.
value
=
eval
(
self
.
translated
,
ns
)
except
:
exc
=
sys
.
exc_info
()[
0
]
if
hasattr
(
exc
,
"__name__"
):
self
.
value
=
exc
.
__name__
else
:
self
.
value
=
str
(
exc
)
return
self
.
value
def
format
(
self
):
try
:
text
=
self
.
fmt
%
self
.
value
except
:
text
=
str
(
self
.
value
)
return
text
,
self
.
alignment
def
xml
(
self
):
return
'<formula align="%s" format="%s">%s</formula>'
%
(
align2xml
[
self
.
alignment
],
self
.
fmt
,
escape
(
self
.
formula
))
def
renumber
(
self
,
x1
,
y1
,
x2
,
y2
,
dx
,
dy
):
out
=
[]
for
part
in
re
.
split
(
r'(\
w+)
', self.formula):
m = re.match('
^
([
A
-
Z
]
+
)([
1
-
9
][
0
-
9
]
*
)
$
', part)
if m is not None:
sx, sy = m.groups()
x = colname2num(sx)
y = int(sy)
if x1 <= x <= x2 and y1 <= y <= y2:
part = cellname(x+dx, y+dy)
out.append(part)
return FormulaCell("".join(out), self.fmt, self.alignment)
def translate(formula):
"""Translate a formula containing fancy cell names to valid Python code.
Examples:
B4 -> cell(2, 4)
B4:Z100 -> cells(2, 4, 26, 100)
"""
out = []
for part in re.split(r"(
\
w+(?::
\
w+)?)", formula):
m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part)
if m is None:
out.append(part)
else:
x1, y1, x2, y2 = m.groups()
x1 = colname2num(x1)
if x2 is None:
s = "cell(%s, %s)" % (x1, y1)
else:
x2 = colname2num(x2)
s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2)
out.append(s)
return "".join(out)
def cellname(x, y):
"Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'
A1
')."
assert x > 0 # Column 0 has an empty name, so can'
t
use
that
return
colnum2name
(
x
)
+
str
(
y
)
def
colname2num
(
s
):
"Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)."
s
=
s
.
upper
()
n
=
0
for
c
in
s
:
assert
'A'
<=
c
<=
'Z'
n
=
n
*
26
+
ord
(
c
)
-
ord
(
'A'
)
+
1
return
n
def
colnum2name
(
n
):
"Translate a column number to name (e.g. 1->'A', etc.)."
assert
n
>
0
s
=
""
while
n
:
n
,
m
=
divmod
(
n
-
1
,
26
)
s
=
chr
(
m
+
ord
(
'A'
))
+
s
return
s
import
tkinter
as
Tk
class
SheetGUI
:
"""Beginnings of a GUI for a spreadsheet.
TO DO:
- clear multiple cells
- Insert, clear, remove rows or columns
- Show new contents while typing
- Scroll bars
- Grow grid when window is grown
- Proper menus
- Undo, redo
- Cut, copy and paste
- Formatting and alignment
"""
def
__init__
(
self
,
filename
=
"sheet1.xml"
,
rows
=
10
,
columns
=
5
):
"""Constructor.
Load the sheet from the filename argument.
Set up the Tk widget tree.
"""
# Create and load the sheet
self
.
filename
=
filename
self
.
sheet
=
Sheet
()
if
os
.
path
.
isfile
(
filename
):
self
.
sheet
.
load
(
filename
)
# Calculate the needed grid size
maxx
,
maxy
=
self
.
sheet
.
getsize
()
rows
=
max
(
rows
,
maxy
)
columns
=
max
(
columns
,
maxx
)
# Create the widgets
self
.
root
=
Tk
.
Tk
()
self
.
root
.
wm_title
(
"Spreadsheet: %s"
%
self
.
filename
)
self
.
beacon
=
Tk
.
Label
(
self
.
root
,
text
=
"A1"
,
font
=
(
'helvetica'
,
16
,
'bold'
))
self
.
entry
=
Tk
.
Entry
(
self
.
root
)
self
.
savebutton
=
Tk
.
Button
(
self
.
root
,
text
=
"Save"
,
command
=
self
.
save
)
self
.
cellgrid
=
Tk
.
Frame
(
self
.
root
)
# Configure the widget lay-out
self
.
cellgrid
.
pack
(
side
=
"bottom"
,
expand
=
1
,
fill
=
"both"
)
self
.
beacon
.
pack
(
side
=
"left"
)
self
.
savebutton
.
pack
(
side
=
"right"
)
self
.
entry
.
pack
(
side
=
"left"
,
expand
=
1
,
fill
=
"x"
)
# Bind some events
self
.
entry
.
bind
(
"<Return>"
,
self
.
return_event
)
self
.
entry
.
bind
(
"<Shift-Return>"
,
self
.
shift_return_event
)
self
.
entry
.
bind
(
"<Tab>"
,
self
.
tab_event
)
self
.
entry
.
bind
(
"<Shift-Tab>"
,
self
.
shift_tab_event
)
self
.
entry
.
bind
(
"<Delete>"
,
self
.
delete_event
)
self
.
entry
.
bind
(
"<Escape>"
,
self
.
escape_event
)
# Now create the cell grid
self
.
makegrid
(
rows
,
columns
)
# Select the top-left cell
self
.
currentxy
=
None
self
.
cornerxy
=
None
self
.
setcurrent
(
1
,
1
)
# Copy the sheet cells to the GUI cells
self
.
sync
()
def
delete_event
(
self
,
event
):
if
self
.
cornerxy
!=
self
.
currentxy
and
self
.
cornerxy
is
not
None
:
self
.
sheet
.
clearcells
(
*
(
self
.
currentxy
+
self
.
cornerxy
))
else
:
self
.
sheet
.
clearcell
(
*
self
.
currentxy
)
self
.
sync
()
self
.
entry
.
delete
(
0
,
'end'
)
return
"break"
def
escape_event
(
self
,
event
):
x
,
y
=
self
.
currentxy
self
.
load_entry
(
x
,
y
)
def
load_entry
(
self
,
x
,
y
):
cell
=
self
.
sheet
.
getcell
(
x
,
y
)
if
cell
is
None
:
text
=
""
elif
isinstance
(
cell
,
FormulaCell
):
text
=
'='
+
cell
.
formula
else
:
text
,
alignment
=
cell
.
format
()
self
.
entry
.
delete
(
0
,
'end'
)
self
.
entry
.
insert
(
0
,
text
)
self
.
entry
.
selection_range
(
0
,
'end'
)
def
makegrid
(
self
,
rows
,
columns
):
"""Helper to create the grid of GUI cells.
The edge (x==0 or y==0) is filled with labels; the rest is real cells.
"""
self
.
rows
=
rows
self
.
columns
=
columns
self
.
gridcells
=
{}
# Create the top left corner cell (which selects all)
cell
=
Tk
.
Label
(
self
.
cellgrid
,
relief
=
'raised'
)
cell
.
grid_configure
(
column
=
0
,
row
=
0
,
sticky
=
'NSWE'
)
cell
.
bind
(
"<ButtonPress-1>"
,
self
.
selectall
)
# Create the top row of labels, and configure the grid columns
for
x
in
range
(
1
,
columns
+
1
):
self
.
cellgrid
.
grid_columnconfigure
(
x
,
minsize
=
64
)
cell
=
Tk
.
Label
(
self
.
cellgrid
,
text
=
colnum2name
(
x
),
relief
=
'raised'
)
cell
.
grid_configure
(
column
=
x
,
row
=
0
,
sticky
=
'WE'
)
self
.
gridcells
[
x
,
0
]
=
cell
cell
.
__x
=
x
cell
.
__y
=
0
cell
.
bind
(
"<ButtonPress-1>"
,
self
.
selectcolumn
)
cell
.
bind
(
"<B1-Motion>"
,
self
.
extendcolumn
)
cell
.
bind
(
"<ButtonRelease-1>"
,
self
.
extendcolumn
)
cell
.
bind
(
"<Shift-Button-1>"
,
self
.
extendcolumn
)
# Create the leftmost column of labels
for
y
in
range
(
1
,
rows
+
1
):
cell
=
Tk
.
Label
(
self
.
cellgrid
,
text
=
str
(
y
),
relief
=
'raised'
)
cell
.
grid_configure
(
column
=
0
,
row
=
y
,
sticky
=
'WE'
)
self
.
gridcells
[
0
,
y
]
=
cell
cell
.
__x
=
0
cell
.
__y
=
y
cell
.
bind
(
"<ButtonPress-1>"
,
self
.
selectrow
)
cell
.
bind
(
"<B1-Motion>"
,
self
.
extendrow
)
cell
.
bind
(
"<ButtonRelease-1>"
,
self
.
extendrow
)
cell
.
bind
(
"<Shift-Button-1>"
,
self
.
extendrow
)
# Create the real cells
for
x
in
range
(
1
,
columns
+
1
):
for
y
in
range
(
1
,
rows
+
1
):
cell
=
Tk
.
Label
(
self
.
cellgrid
,
relief
=
'sunken'
,
bg
=
'white'
,
fg
=
'black'
)
cell
.
grid_configure
(
column
=
x
,
row
=
y
,
sticky
=
'NSWE'
)
self
.
gridcells
[
x
,
y
]
=
cell
cell
.
__x
=
x
cell
.
__y
=
y
# Bind mouse events
cell
.
bind
(
"<ButtonPress-1>"
,
self
.
press
)
cell
.
bind
(
"<B1-Motion>"
,
self
.
motion
)
cell
.
bind
(
"<ButtonRelease-1>"
,
self
.
release
)
cell
.
bind
(
"<Shift-Button-1>"
,
self
.
release
)
def
selectall
(
self
,
event
):
self
.
setcurrent
(
1
,
1
)
self
.
setcorner
(
sys
.
maxsize
,
sys
.
maxsize
)
def
selectcolumn
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
self
.
setcurrent
(
x
,
1
)
self
.
setcorner
(
x
,
sys
.
maxsize
)
def
extendcolumn
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
if
x
>
0
:
self
.
setcurrent
(
self
.
currentxy
[
0
],
1
)
self
.
setcorner
(
x
,
sys
.
maxsize
)
def
selectrow
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
self
.
setcurrent
(
1
,
y
)
self
.
setcorner
(
sys
.
maxsize
,
y
)
def
extendrow
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
if
y
>
0
:
self
.
setcurrent
(
1
,
self
.
currentxy
[
1
])
self
.
setcorner
(
sys
.
maxsize
,
y
)
def
press
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
if
x
>
0
and
y
>
0
:
self
.
setcurrent
(
x
,
y
)
def
motion
(
self
,
event
):
x
,
y
=
self
.
whichxy
(
event
)
if
x
>
0
and
y
>
0
:
self
.
setcorner
(
x
,
y
)
release
=
motion
def
whichxy
(
self
,
event
):
w
=
self
.
cellgrid
.
winfo_containing
(
event
.
x_root
,
event
.
y_root
)
if
w
is
not
None
and
isinstance
(
w
,
Tk
.
Label
):
try
:
return
w
.
__x
,
w
.
__y
except
AttributeError
:
pass
return
0
,
0
def
save
(
self
):
self
.
sheet
.
save
(
self
.
filename
)
def
setcurrent
(
self
,
x
,
y
):
"Make (x, y) the current cell."
if
self
.
currentxy
is
not
None
:
self
.
change_cell
()
self
.
clearfocus
()
self
.
beacon
[
'text'
]
=
cellname
(
x
,
y
)
self
.
load_entry
(
x
,
y
)
self
.
entry
.
focus_set
()
self
.
currentxy
=
x
,
y
self
.
cornerxy
=
None
gridcell
=
self
.
gridcells
.
get
(
self
.
currentxy
)
if
gridcell
is
not
None
:
gridcell
[
'bg'
]
=
'yellow'
def
setcorner
(
self
,
x
,
y
):
if
self
.
currentxy
is
None
or
self
.
currentxy
==
(
x
,
y
):
self
.
setcurrent
(
x
,
y
)
return
self
.
clearfocus
()
self
.
cornerxy
=
x
,
y
x1
,
y1
=
self
.
currentxy
x2
,
y2
=
self
.
cornerxy
or
self
.
currentxy
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
for
(
x
,
y
),
cell
in
self
.
gridcells
.
items
():
if
x1
<=
x
<=
x2
and
y1
<=
y
<=
y2
:
cell
[
'bg'
]
=
'lightBlue'
gridcell
=
self
.
gridcells
.
get
(
self
.
currentxy
)
if
gridcell
is
not
None
:
gridcell
[
'bg'
]
=
'yellow'
self
.
setbeacon
(
x1
,
y1
,
x2
,
y2
)
def
setbeacon
(
self
,
x1
,
y1
,
x2
,
y2
):
if
x1
==
y1
==
1
and
x2
==
y2
==
sys
.
maxsize
:
name
=
":"
elif
(
x1
,
x2
)
==
(
1
,
sys
.
maxsize
):
if
y1
==
y2
:
name
=
"%d"
%
y1
else
:
name
=
"%d:%d"
%
(
y1
,
y2
)
elif
(
y1
,
y2
)
==
(
1
,
sys
.
maxsize
):
if
x1
==
x2
:
name
=
"%s"
%
colnum2name
(
x1
)
else
:
name
=
"%s:%s"
%
(
colnum2name
(
x1
),
colnum2name
(
x2
))
else
:
name1
=
cellname
(
*
self
.
currentxy
)
name2
=
cellname
(
*
self
.
cornerxy
)
name
=
"%s:%s"
%
(
name1
,
name2
)
self
.
beacon
[
'text'
]
=
name
def
clearfocus
(
self
):
if
self
.
currentxy
is
not
None
:
x1
,
y1
=
self
.
currentxy
x2
,
y2
=
self
.
cornerxy
or
self
.
currentxy
if
x1
>
x2
:
x1
,
x2
=
x2
,
x1
if
y1
>
y2
:
y1
,
y2
=
y2
,
y1
for
(
x
,
y
),
cell
in
self
.
gridcells
.
items
():
if
x1
<=
x
<=
x2
and
y1
<=
y
<=
y2
:
cell
[
'bg'
]
=
'white'
def
return_event
(
self
,
event
):
"Callback for the Return key."
self
.
change_cell
()
x
,
y
=
self
.
currentxy
self
.
setcurrent
(
x
,
y
+
1
)
return
"break"
def
shift_return_event
(
self
,
event
):
"Callback for the Return key with Shift modifier."
self
.
change_cell
()
x
,
y
=
self
.
currentxy
self
.
setcurrent
(
x
,
max
(
1
,
y
-
1
))
return
"break"
def
tab_event
(
self
,
event
):
"Callback for the Tab key."
self
.
change_cell
()
x
,
y
=
self
.
currentxy
self
.
setcurrent
(
x
+
1
,
y
)
return
"break"
def
shift_tab_event
(
self
,
event
):
"Callback for the Tab key with Shift modifier."
self
.
change_cell
()
x
,
y
=
self
.
currentxy
self
.
setcurrent
(
max
(
1
,
x
-
1
),
y
)
return
"break"
def
change_cell
(
self
):
"Set the current cell from the entry widget."
x
,
y
=
self
.
currentxy
text
=
self
.
entry
.
get
()
cell
=
None
if
text
.
startswith
(
'='
):
cell
=
FormulaCell
(
text
[
1
:])
else
:
for
cls
in
int
,
float
,
complex
:
try
:
value
=
cls
(
text
)
except
(
TypeError
,
ValueError
):
continue
else
:
cell
=
NumericCell
(
value
)
break
if
cell
is
None
and
text
:
cell
=
StringCell
(
text
)
if
cell
is
None
:
self
.
sheet
.
clearcell
(
x
,
y
)
else
:
self
.
sheet
.
setcell
(
x
,
y
,
cell
)
self
.
sync
()
def
sync
(
self
):
"Fill the GUI cells from the sheet cells."
self
.
sheet
.
recalc
()
for
(
x
,
y
),
gridcell
in
self
.
gridcells
.
items
():
if
x
==
0
or
y
==
0
:
continue
cell
=
self
.
sheet
.
getcell
(
x
,
y
)
if
cell
is
None
:
gridcell
[
'text'
]
=
""
else
:
if
hasattr
(
cell
,
'format'
):
text
,
alignment
=
cell
.
format
()
else
:
text
,
alignment
=
str
(
cell
),
LEFT
gridcell
[
'text'
]
=
text
gridcell
[
'anchor'
]
=
align2anchor
[
alignment
]
def
test_basic
():
"Basic non-gui self-test."
a
=
Sheet
()
for
x
in
range
(
1
,
11
):
for
y
in
range
(
1
,
11
):
if
x
==
1
:
cell
=
NumericCell
(
y
)
elif
y
==
1
:
cell
=
NumericCell
(
x
)
else
:
c1
=
cellname
(
x
,
1
)
c2
=
cellname
(
1
,
y
)
formula
=
"%s*%s"
%
(
c1
,
c2
)
cell
=
FormulaCell
(
formula
)
a
.
setcell
(
x
,
y
,
cell
)
## if os.path.isfile("sheet1.xml"):
## print "Loading from sheet1.xml"
## a.load("sheet1.xml")
a
.
display
()
a
.
save
(
"sheet1.xml"
)
def
test_gui
():
"GUI test."
if
sys
.
argv
[
1
:]:
filename
=
sys
.
argv
[
1
]
else
:
filename
=
"sheet1.xml"
g
=
SheetGUI
(
filename
)
g
.
root
.
mainloop
()
if
__name__
==
'__main__'
:
#test_basic()
test_gui
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment