6
6
from itertools import izip
7
7
from urlparse import urlparse
8
8
import csv
9
+ import xlwt
9
10
10
11
try :
11
12
next
@@ -1881,6 +1882,89 @@ def _trim_excel_header(row):
1881
1882
return row
1882
1883
1883
1884
1885
+ class CellStyleConverter (object ):
1886
+ """
1887
+ Utility Class which converts a style dict to xlrd or openpyxl style
1888
+ """
1889
+
1890
+ @staticmethod
1891
+ def to_xls (style_dict ):
1892
+ """
1893
+ converts a style_dict to an xlwt style object
1894
+ Parameters
1895
+ ----------
1896
+ style_dict: style dictionary to convert
1897
+ """
1898
+ def style_to_xlwt (item , firstlevel = True , field_sep = ',' , line_sep = ';' ):
1899
+ """helper wich recursively generate an xlwt easy style string
1900
+ for example:
1901
+
1902
+ hstyle = {"font": {"bold": True},
1903
+ "border": {"top": "thin",
1904
+ "right": "thin",
1905
+ "bottom": "thin",
1906
+ "left": "thin"},
1907
+ "align": {"horiz": "center"}}
1908
+ will be converted to
1909
+ font: bold on; \
1910
+ border: top thin, right thin, bottom thin, left thin; \
1911
+ align: horiz center;
1912
+ """
1913
+ if hasattr (item , 'items' ):
1914
+ if firstlevel :
1915
+ it = ["%s: %s" % (key , style_to_xlwt (value , False ))
1916
+ for key , value in item .items ()]
1917
+ out = "%s " % (line_sep ).join (it )
1918
+ return out
1919
+ else :
1920
+ it = ["%s %s" % (key , style_to_xlwt (value , False ))
1921
+ for key , value in item .items ()]
1922
+ out = "%s " % (field_sep ).join (it )
1923
+ return out
1924
+ else :
1925
+ item = "%s" % item
1926
+ item = item .replace ("True" , "on" )
1927
+ item = item .replace ("False" , "off" )
1928
+ return item
1929
+
1930
+ if style_dict :
1931
+ xlwt_stylestr = style_to_xlwt (style_dict )
1932
+ return xlwt .easyxf (xlwt_stylestr , field_sep = ',' , line_sep = ';' )
1933
+ else :
1934
+ return xlwt .XFStyle ()
1935
+
1936
+ @staticmethod
1937
+ def to_xlsx (style_dict ):
1938
+ """
1939
+ converts a style_dict to an openpyxl style object
1940
+ Parameters
1941
+ ----------
1942
+ style_dict: style dictionary to convert
1943
+ """
1944
+
1945
+ from openpyxl .style import Style
1946
+ xls_style = Style ()
1947
+ for key , value in style_dict .items ():
1948
+ for nk , nv in value .items ():
1949
+ if key == "borders" :
1950
+ (xls_style .borders .__getattribute__ (nk )
1951
+ .__setattr__ ('border_style' , nv ))
1952
+ else :
1953
+ xls_style .__getattribute__ (key ).__setattr__ (nk , nv )
1954
+
1955
+ return xls_style
1956
+
1957
+
1958
+ def _conv_value (val ):
1959
+ #convert value for excel dump
1960
+ if isinstance (val , np .int64 ):
1961
+ val = int (val )
1962
+ if isinstance (val , np .bool8 ):
1963
+ val = bool (val )
1964
+
1965
+ return val
1966
+
1967
+
1884
1968
class ExcelWriter (object ):
1885
1969
"""
1886
1970
Class for writing DataFrame objects into excel sheets, uses xlwt for xls,
@@ -1897,11 +1981,12 @@ def __init__(self, path):
1897
1981
self .use_xlsx = False
1898
1982
import xlwt
1899
1983
self .book = xlwt .Workbook ()
1900
- self .fm_datetime = xlwt .easyxf (num_format_str = 'YYYY-MM-DD HH:MM:SS' )
1984
+ self .fm_datetime = xlwt .easyxf (
1985
+ num_format_str = 'YYYY-MM-DD HH:MM:SS' )
1901
1986
self .fm_date = xlwt .easyxf (num_format_str = 'YYYY-MM-DD' )
1902
1987
else :
1903
1988
from openpyxl .workbook import Workbook
1904
- self .book = Workbook (optimized_write = True )
1989
+ self .book = Workbook () # optimized_write=True)
1905
1990
self .path = path
1906
1991
self .sheets = {}
1907
1992
self .cur_sheet = None
@@ -1912,66 +1997,86 @@ def save(self):
1912
1997
"""
1913
1998
self .book .save (self .path )
1914
1999
1915
- def writerow (self , row , sheet_name = None ):
2000
+ def write_cells (self , cells , sheet_name = None , startrow = 0 , startcol = 0 ):
1916
2001
"""
1917
- Write the given row into Excel an excel sheet
2002
+ Write given formated cells into Excel an excel sheet
1918
2003
1919
2004
Parameters
1920
2005
----------
1921
- row : list
1922
- Row of data to save to Excel sheet
2006
+ cells : generator
2007
+ cell of formated data to save to Excel sheet
1923
2008
sheet_name : string, default None
1924
2009
Name of Excel sheet, if None, then use self.cur_sheet
2010
+ startrow: upper left cell row to dump data frame
2011
+ startcol: upper left cell column to dump data frame
1925
2012
"""
1926
2013
if sheet_name is None :
1927
2014
sheet_name = self .cur_sheet
1928
2015
if sheet_name is None : # pragma: no cover
1929
2016
raise Exception ('Must pass explicit sheet_name or set '
1930
2017
'cur_sheet property' )
1931
2018
if self .use_xlsx :
1932
- self ._writerow_xlsx ( row , sheet_name )
2019
+ self ._writecells_xlsx ( cells , sheet_name , startrow , startcol )
1933
2020
else :
1934
- self ._writerow_xls (row , sheet_name )
2021
+ self ._writecells_xls (cells , sheet_name , startrow , startcol )
2022
+
2023
+ def _writecells_xlsx (self , cells , sheet_name , startrow , startcol ):
2024
+ from openpyxl .cell import get_column_letter
1935
2025
1936
- def _writerow_xls (self , row , sheet_name ):
1937
2026
if sheet_name in self .sheets :
1938
- sheet , row_idx = self .sheets [sheet_name ]
2027
+ wks = self .sheets [sheet_name ]
1939
2028
else :
1940
- sheet = self .book .add_sheet (sheet_name )
1941
- row_idx = 0
1942
- sheetrow = sheet .row (row_idx )
1943
- for i , val in enumerate (row ):
1944
- if isinstance (val , (datetime .datetime , datetime .date )):
1945
- if isinstance (val , datetime .datetime ):
1946
- sheetrow .write (i , val , self .fm_datetime )
1947
- else :
1948
- sheetrow .write (i , val , self .fm_date )
1949
- elif isinstance (val , np .int64 ):
1950
- sheetrow .write (i , int (val ))
1951
- elif isinstance (val , np .bool8 ):
1952
- sheetrow .write (i , bool (val ))
1953
- else :
1954
- sheetrow .write (i , val )
1955
- row_idx += 1
1956
- if row_idx == 1000 :
1957
- sheet .flush_row_data ()
1958
- self .sheets [sheet_name ] = (sheet , row_idx )
1959
-
1960
- def _writerow_xlsx (self , row , sheet_name ):
2029
+ wks = self .book .create_sheet ()
2030
+ wks .title = sheet_name
2031
+ self .sheets [sheet_name ] = wks
2032
+
2033
+ for cell in cells :
2034
+ colletter = get_column_letter (startcol + cell .col + 1 )
2035
+ xcell = wks .cell ("%s%s" % (colletter , startrow + cell .row + 1 ))
2036
+ xcell .value = _conv_value (cell .val )
2037
+ if cell .style :
2038
+ style = CellStyleConverter .to_xlsx (cell .style )
2039
+ for field in style .__fields__ :
2040
+ xcell .style .__setattr__ (field ,
2041
+ style .__getattribute__ (field ))
2042
+
2043
+ if isinstance (cell .val , datetime .datetime ):
2044
+ style .num_format_str = "YYYY-MM-DD HH:SS"
2045
+ elif isinstance (cell .val , datetime .date ):
2046
+ style .num_format_str = "YYYY-MM-DD"
2047
+
2048
+ #merging requires openpyxl latest (works on 1.5.7)
2049
+ if cell .mergestart is not None and cell .mergeend is not None :
2050
+ cletterstart = get_column_letter (startcol + cell .col + 1 )
2051
+ cletterend = get_column_letter (startcol + cell .mergeend + 1 )
2052
+
2053
+ wks .merge_cells ('%s%s:%s%s' % (cletterstart ,
2054
+ startrow + cell .row + 1 ,
2055
+ cletterend ,
2056
+ startrow + cell .mergestart + 1 ))
2057
+
2058
+ def _writecells_xls (self , cells , sheet_name , startrow , startcol ):
1961
2059
if sheet_name in self .sheets :
1962
- sheet , row_idx = self .sheets [sheet_name ]
2060
+ wks = self .sheets [sheet_name ]
1963
2061
else :
1964
- sheet = self .book .create_sheet ()
1965
- sheet .title = sheet_name
1966
- row_idx = 0
1967
-
1968
- conv_row = []
1969
- for val in row :
1970
- if isinstance (val , np .int64 ):
1971
- val = int (val )
1972
- elif isinstance (val , np .bool8 ):
1973
- val = bool (val )
1974
- conv_row .append (val )
1975
- sheet .append (conv_row )
1976
- row_idx += 1
1977
- self .sheets [sheet_name ] = (sheet , row_idx )
2062
+ wks = self .book .add_sheet (sheet_name )
2063
+ self .sheets [sheet_name ] = wks
2064
+
2065
+ for cell in cells :
2066
+ val = _conv_value (cell .val )
2067
+ style = CellStyleConverter .to_xls (cell .style )
2068
+ if isinstance (val , datetime .datetime ):
2069
+ style .num_format_str = "YYYY-MM-DD HH:SS"
2070
+ elif isinstance (val , datetime .date ):
2071
+ style .num_format_str = "YYYY-MM-DD"
2072
+
2073
+ if cell .mergestart is not None and cell .mergeend is not None :
2074
+ wks .write_merge (startrow + cell .row ,
2075
+ startrow + cell .mergestart ,
2076
+ startcol + cell .col ,
2077
+ startcol + cell .mergeend ,
2078
+ val , style )
2079
+ else :
2080
+ wks .write (startrow + cell .row ,
2081
+ startcol + cell .col ,
2082
+ val , style )
0 commit comments