From aeb5cfcda90274154f907dabb2c01fc13bcc6c31 Mon Sep 17 00:00:00 2001 From: zhangyimingdatiancai <2654488395@qq.com> Date: Sun, 1 Dec 2024 12:56:39 +0800 Subject: [PATCH] New functions coordinates_to_cell_name and set_sheet_row has been added (#1) - Update unit tests and docs for the function - Sort function by alphabet order --- excelize.py | 71 ++++++++++++++++++++-- main.go | 150 ++++++++++++++++++++++++++++------------------- test_excelize.py | 15 +++++ types_c.h | 5 ++ types_go.py | 7 +++ 5 files changed, 183 insertions(+), 65 deletions(-) diff --git a/excelize.py b/excelize.py index 4ed84b0..14b4d95 100644 --- a/excelize.py +++ b/excelize.py @@ -16,17 +16,18 @@ import types_go from types_py import * from ctypes import ( - CDLL, + byref, + c_bool, + c_char_p, c_char, c_int, - c_char_p, - POINTER, - byref, c_ubyte, - string_at, cast, + CDLL, create_string_buffer, + POINTER, pointer, + string_at, ) import os import platform @@ -844,7 +845,7 @@ def set_sheet_background_from_bytes( Args: sheet (str): The worksheet name - extension (str): The cell reference + extension (str): The image extension picture (bytes): The contents buffer of the file Returns: @@ -861,6 +862,64 @@ def set_sheet_background_from_bytes( ).decode(ENCODE) return None if err == "" else Exception(err) + def set_sheet_row( + self, + sheet: str, + cell: str, + values: list[None | int | str | bool | datetime | date], + ) -> Exception | None: + """ + Writes cells to row by given worksheet name, starting cell reference and + cell values list. + + Args: + sheet (str): The worksheet name + cell (str): The cell reference + values (bytes): The cell values + + Returns: + Exception | None: Returns None if no error occurred, + otherwise returns an Exception with the message. + """ + lib.SetSheetRow.restype = c_char_p + vals = (types_go._Interface * len(values))() + for i, value in enumerate(values): + vals[i] = py_value_to_c_interface(value) + err = lib.SetSheetRow( + self.file_index, + sheet.encode(ENCODE), + cell.encode(ENCODE), + byref(vals), + len(vals), + ).decode(ENCODE) + return None if err == "" else Exception(err) + + +def coordinates_to_cell_name( + col: int, row: int, *abs: bool +) -> Tuple[str, Exception | None]: + """ + Converts [X, Y] coordinates to alpha-numeric cell name or returns an error. + + Args: + col (int): The column number. + row (int): The row number. + *abs (bool): Optional boolean indicating whether to use absolute + references. If provided and True, the cell name will use absolute + references (e.g., $A$1). + + Returns: + Tuple[str, Exception | None]: A tuple containing the cell name as a + string and an Exception if an error occurred, otherwise None. + """ + lib.CoordinatesToCellName.restype = types_go._CoordinatesToCellNameResult + options = False + if len(abs) > 0: + options = abs[0] + res = lib.CoordinatesToCellName(col, row, c_bool(options)) + err = res.err.decode(ENCODE) + return res.cell.decode(ENCODE), None if err == "" else Exception(err) + def new_file() -> File: """ diff --git a/main.go b/main.go index 4e4912d..30aa6e1 100644 --- a/main.go +++ b/main.go @@ -398,6 +398,18 @@ func cInterfaceToGo(val C.struct_Interface) interface{} { } } +// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell name +// or returns an error. +// +//export CoordinatesToCellName +func CoordinatesToCellName(col, row int, abs bool) C.struct_CoordinatesToCellNameResult { + cell, err := excelize.CoordinatesToCellName(col, row, abs) + if err != nil { + return C.struct_CoordinatesToCellNameResult{cell: C.CString(cell), err: C.CString(err.Error())} + } + return C.struct_CoordinatesToCellNameResult{cell: C.CString(cell), err: C.CString(errNil)} +} + // Close closes and cleanup the open temporary file for the spreadsheet. // //export Close @@ -621,6 +633,25 @@ func GetRows(idx int, sheet *C.char, opts *C.struct_Options) C.struct_GetRowsRes return ret } +// GetStyle provides a function to get style definition by given style index. +// +//export GetStyle +func GetStyle(idx, styleID int) C.struct_GetStyleResult { + f, ok := files.Load(idx) + if !ok { + return C.struct_GetStyleResult{err: C.CString(errFilePtr)} + } + style, err := f.(*excelize.File).GetStyle(styleID) + if err != nil { + return C.struct_GetStyleResult{err: C.CString(err.Error())} + } + cVal, err := goValueToC(reflect.ValueOf(*style), reflect.ValueOf(&C.struct_Style{})) + if err != nil { + return C.struct_GetStyleResult{err: C.CString(err.Error())} + } + return C.struct_GetStyleResult{style: cVal.Elem().Interface().(C.struct_Style), err: C.CString(errNil)} +} + // NewFile provides a function to create new file by default template. // //export NewFile @@ -635,6 +666,46 @@ func NewFile() int { return idx } +// NewSheet provides the function to create a new sheet by given a worksheet +// name and returns the index of the sheets in the workbook after it appended. +// Note that when creating a new workbook, the default worksheet named +// `Sheet1` will be created. +// +//export NewSheet +func NewSheet(idx int, sheet *C.char) C.struct_NewSheetResult { + f, ok := files.Load(idx) + if !ok { + return C.struct_NewSheetResult{idx: C.int(-1), err: C.CString(errFilePtr)} + } + idx, err := f.(*excelize.File).NewSheet(C.GoString(sheet)) + if err != nil { + return C.struct_NewSheetResult{idx: C.int(idx), err: C.CString(err.Error())} + } + return C.struct_NewSheetResult{idx: C.int(idx), err: C.CString(errNil)} +} + +// NewStyle provides a function to create the style for cells by given options. +// Note that the color field uses RGB color code. +// +//export NewStyle +func NewStyle(idx int, style *C.struct_Style) C.struct_NewStyleResult { + var s excelize.Style + goVal, err := cValueToGo(reflect.ValueOf(*style), reflect.TypeOf(excelize.Style{})) + if err != nil { + return C.struct_NewStyleResult{style: C.int(0), err: C.CString(err.Error())} + } + s = goVal.Elem().Interface().(excelize.Style) + f, ok := files.Load(idx) + if !ok { + return C.struct_NewStyleResult{style: C.int(0), err: C.CString(errFilePtr)} + } + styleID, err := f.(*excelize.File).NewStyle(&s) + if err != nil { + return C.struct_NewStyleResult{style: C.int(styleID), err: C.CString(err.Error())} + } + return C.struct_NewStyleResult{style: C.int(styleID), err: C.CString(errNil)} +} + // OpenFile take the name of a spreadsheet file and returns a populated // spreadsheet file struct for it. // @@ -715,46 +786,6 @@ func SaveAs(idx int, name *C.char, opts *C.struct_Options) *C.char { return C.CString(errNil) } -// NewSheet provides the function to create a new sheet by given a worksheet -// name and returns the index of the sheets in the workbook after it appended. -// Note that when creating a new workbook, the default worksheet named -// `Sheet1` will be created. -// -//export NewSheet -func NewSheet(idx int, sheet *C.char) C.struct_NewSheetResult { - f, ok := files.Load(idx) - if !ok { - return C.struct_NewSheetResult{idx: C.int(-1), err: C.CString(errFilePtr)} - } - idx, err := f.(*excelize.File).NewSheet(C.GoString(sheet)) - if err != nil { - return C.struct_NewSheetResult{idx: C.int(idx), err: C.CString(err.Error())} - } - return C.struct_NewSheetResult{idx: C.int(idx), err: C.CString(errNil)} -} - -// NewStyle provides a function to create the style for cells by given options. -// Note that the color field uses RGB color code. -// -//export NewStyle -func NewStyle(idx int, style *C.struct_Style) C.struct_NewStyleResult { - var s excelize.Style - goVal, err := cValueToGo(reflect.ValueOf(*style), reflect.TypeOf(excelize.Style{})) - if err != nil { - return C.struct_NewStyleResult{style: C.int(0), err: C.CString(err.Error())} - } - s = goVal.Elem().Interface().(excelize.Style) - f, ok := files.Load(idx) - if !ok { - return C.struct_NewStyleResult{style: C.int(0), err: C.CString(errFilePtr)} - } - styleID, err := f.(*excelize.File).NewStyle(&s) - if err != nil { - return C.struct_NewStyleResult{style: C.int(styleID), err: C.CString(err.Error())} - } - return C.struct_NewStyleResult{style: C.int(styleID), err: C.CString(errNil)} -} - // SetActiveSheet provides a function to set the default active sheet of the // workbook by a given index. Note that the active index is different from the // ID returned by function GetSheetMap(). It should be greater than or equal @@ -812,25 +843,6 @@ func SetCellValue(idx int, sheet, cell *C.char, value *C.struct_Interface) *C.ch return C.CString(errNil) } -// GetStyle provides a function to get style definition by given style index. -// -//export GetStyle -func GetStyle(idx, styleID int) C.struct_GetStyleResult { - f, ok := files.Load(idx) - if !ok { - return C.struct_GetStyleResult{err: C.CString(errFilePtr)} - } - style, err := f.(*excelize.File).GetStyle(styleID) - if err != nil { - return C.struct_GetStyleResult{err: C.CString(err.Error())} - } - cVal, err := goValueToC(reflect.ValueOf(*style), reflect.ValueOf(&C.struct_Style{})) - if err != nil { - return C.struct_GetStyleResult{err: C.CString(err.Error())} - } - return C.struct_GetStyleResult{style: cVal.Elem().Interface().(C.struct_Style), err: C.CString(errNil)} -} - // SetSheetBackgroundFromBytes provides a function to set background picture by // given worksheet name, extension name and image data. Supported image types: // BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ. @@ -848,5 +860,25 @@ func SetSheetBackgroundFromBytes(idx int, sheet, extension *C.char, picture *C.u return C.CString(errNil) } +// SetSheetRow writes an array to row by given worksheet name, starting +// cell reference and a pointer to array type 'slice'. This function is +// concurrency safe. +// +//export SetSheetRow +func SetSheetRow(idx int, sheet, cell *C.char, row *C.struct_Interface, length int) *C.char { + f, ok := files.Load(idx) + if !ok { + return C.CString(errFilePtr) + } + cells := make([]interface{}, length) + for i, val := range unsafe.Slice(row, length) { + cells[i] = cInterfaceToGo(val) + } + if err := f.(*excelize.File).SetSheetRow(C.GoString(sheet), C.GoString(cell), &cells); err != nil { + C.CString(err.Error()) + } + return C.CString(errNil) +} + func main() { } diff --git a/test_excelize.py b/test_excelize.py index 7755f70..64ab91c 100644 --- a/test_excelize.py +++ b/test_excelize.py @@ -229,6 +229,21 @@ def test_style(self): self.assertIsNone(f.save(excelize.Options(password=""))) self.assertIsNone(f.close()) + def test_add_chart(self): + f = excelize.new_file() + for idx, row in enumerate( + [ + [None, "Apple", "Orange", "Pear"], + ["Small", 2, 3, 3], + ["Normal", 5, 2, 4], + ["Large", 6, 7, 8], + ] + ): + cell, err = excelize.coordinates_to_cell_name(1, idx + 1, False) + self.assertIsNone(err) + self.assertIsNone(f.set_sheet_row("Sheet1", cell, row)) + self.assertIsNone(f.save_as("TestAddChart.xlsx")) + def test_type_convert(self): class _T2(Structure): _fields_ = [ diff --git a/types_c.h b/types_c.h index 9d23fa4..4188035 100644 --- a/types_c.h +++ b/types_c.h @@ -91,6 +91,11 @@ struct Style { bool NegRed; }; +struct CoordinatesToCellNameResult { + char* cell; + char* err; +}; + struct OptionsResult { int idx; char* err; diff --git a/types_go.py b/types_go.py index 107ba5d..19b45b6 100644 --- a/types_go.py +++ b/types_go.py @@ -116,6 +116,13 @@ class _Style(Structure): ] +class _CoordinatesToCellNameResult(Structure): + _fields_ = [ + ("cell", c_char_p), + ("err", c_char_p), + ] + + class _GetCellValueResult(Structure): _fields_ = [ ("val", c_char_p),