Skip to content

Commit

Permalink
Add 4 new functions: move_sheet, new_conditional_style, set_condition…
Browse files Browse the repository at this point in the history
…al_format and set_default_font

- Update unit tests and docs for the function
  • Loading branch information
xuri committed Dec 25, 2024
1 parent e49e57c commit 3da5bc3
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 40 deletions.
35 changes: 0 additions & 35 deletions .github/workflows/codeql-analysis.yml

This file was deleted.

102 changes: 101 additions & 1 deletion excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def py_value_to_c(py_instance, ctypes_instance):
# Pointer of the Go data type, for example: *excelize.Options or *string
value = getattr(py_instance, py_field_name)
c_type = get_c_field_type(ctypes_instance, c_field_name)._type_
if value:
if value is not None:
if any(is_py_primitive_type(arg) for arg in py_field_args):
# Pointer of the Go basic data type, for example: *string
setattr(
Expand Down Expand Up @@ -1379,6 +1379,55 @@ def merge_cell(
).decode(ENCODE)
return None if err == "" else Exception(err)

def move_sheet(self, source: str, target: str) -> Optional[Exception]:
"""
Moves a sheet to a specified position in the workbook. The function
moves the source sheet before the target sheet. After moving, other
sheets will be shifted to the left or right. If the sheet is already at
the target position, the function will not perform any action. Not that
this function will be ungroup all sheets after moving.
Args:
source (str): The source worksheet name
target (str): The target worksheet name
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, move Sheet2 before Sheet1:
.. code-block:: python
err = f.move_sheet("Sheet2", "Sheet1")
"""
lib.MoveSheet.restype = c_char_p
err = lib.MoveSheet(
self.file_index,
source.encode(ENCODE),
target.encode(ENCODE),
).decode(ENCODE)
return None if err == "" else Exception(err)

def new_conditional_style(self, style: Style) -> Tuple[int, Optional[Exception]]:
"""
Create style for conditional format by given style format. The
parameters are the same with the new_style function.
Args:
style (Style): The style options
Returns:
Tuple[int, Optional[Exception]]: A tuple containing the style index
and an exception if any error occurs.
"""
lib.NewConditionalStyle.restype = types_go._NewStyleResult
options = py_value_to_c(style, types_go._Style())
res = lib.NewConditionalStyle(self.file_index, byref(options))
err = res.err.decode(ENCODE)
return res.style, None if err == "" else Exception(err)

def new_sheet(self, sheet: str) -> Tuple[int, Optional[Exception]]:
"""
Create a new sheet by given a worksheet name and returns the index of
Expand Down Expand Up @@ -1949,6 +1998,57 @@ def set_col_width(
).decode(ENCODE)
return None if err == "" else Exception(err)

def set_conditional_format(
self,
sheet: str,
range_ref: str,
opts: List[ConditionalFormatOptions],
) -> Optional[Exception]:
"""
Create conditional formatting rule for cell value. Conditional
formatting is a feature of Excel which allows you to apply a format to a
cell or a range of cells based on certain criteria.
Args:
sheet (str): The worksheet name
range_ref (str): The top-left and right-bottom cell range reference
opts (List[ConditionalFormatOptions]): The conditional format options
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.SetConditionalFormat.restype = c_char_p
vals = (types_go._ConditionalFormatOptions * len(opts))()
for i, value in enumerate(opts):
vals[i] = py_value_to_c(value, types_go._ConditionalFormatOptions())
err = lib.SetConditionalFormat(
self.file_index,
sheet.encode(ENCODE),
range_ref.encode(ENCODE),
byref(vals),
len(vals),
).decode(ENCODE)
return None if err == "" else Exception(err)

def set_default_font(self, font_name: str) -> Optional[Exception]:
"""
Set the default font name in the workbook. The spreadsheet generated by
excelize default font is Calibri.
Args:
font_name (str): The font name
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.SetDefaultFont.restype = c_char_p
err = lib.SetDefaultFont(self.file_index, font_name.encode(ENCODE)).decode(
ENCODE
)
return None if err == "" else Exception(err)

def set_defined_name(self, defined_name: DefinedName) -> Optional[Exception]:
"""
Set the defined names of the workbook or worksheet. If not specified
Expand Down
85 changes: 85 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func cToGoArray(cArray reflect.Value, cArrayLen int) reflect.Value {
val := cArray.Interface().(*C.struct_ChartSeries)
arr := unsafe.Slice(val, cArrayLen)
return reflect.ValueOf(arr)
case "main._Ctype_struct_ConditionalFormatOptions":
val := cArray.Interface().(*C.struct_ConditionalFormatOptions)
arr := unsafe.Slice(val, cArrayLen)
return reflect.ValueOf(arr)
case "main._Ctype_struct_PivotTableField":
val := cArray.Interface().(*C.struct_PivotTableField)
arr := unsafe.Slice(val, cArrayLen)
Expand Down Expand Up @@ -1132,6 +1136,47 @@ func MergeCell(idx int, sheet, topLeftCell, bottomRightCell *C.char) *C.char {
return C.CString(errNil)
}

// MoveSheet moves a sheet to a specified position in the workbook. The function
// moves the source sheet before the target sheet. After moving, other sheets
// will be shifted to the left or right. If the sheet is already at the target
// position, the function will not perform any action. Not that this function
// will be ungroup all sheets after moving.
//
//export MoveSheet
func MoveSheet(idx int, source, target *C.char) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString("")
}
if err := f.(*excelize.File).MoveSheet(C.GoString(source), C.GoString(target)); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// NewConditionalStyle provides a function to create style for conditional
// format by given style format. The parameters are the same with the NewStyle
// function.
//
//export NewConditionalStyle
func NewConditionalStyle(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).NewConditionalStyle(&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)}
}

// NewFile provides a function to create new file by default template.
//
//export NewFile
Expand Down Expand Up @@ -1544,6 +1589,46 @@ func SetColWidth(idx int, sheet, startCol, endCol *C.char, width float64) *C.cha
return C.CString(errNil)
}

// SetConditionalFormat provides a function to create conditional formatting
// rule for cell value. Conditional formatting is a feature of Excel which
// allows you to apply a format to a cell or a range of cells based on certain
// criteria.
//
//export SetConditionalFormat
func SetConditionalFormat(idx int, sheet, rangeRef *C.char, opts *C.struct_ConditionalFormatOptions, length int) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString("")
}
options := make([]excelize.ConditionalFormatOptions, length)
for i, val := range unsafe.Slice(opts, length) {
goVal, err := cValueToGo(reflect.ValueOf(val), reflect.TypeOf(excelize.ConditionalFormatOptions{}))
if err != nil {
return C.CString(err.Error())
}
options[i] = goVal.Elem().Interface().(excelize.ConditionalFormatOptions)
}
if err := f.(*excelize.File).SetConditionalFormat(C.GoString(sheet), C.GoString(rangeRef), options); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// SetDefaultFont provides the default font name currently set in the
// workbook. The spreadsheet generated by excelize default font is Calibri.
//
//export SetDefaultFont
func SetDefaultFont(idx int, fontName *C.char) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).SetDefaultFont(C.GoString(fontName)); err != nil {
C.CString(err.Error())
}
return C.CString(errNil)
}

// SetDefinedName provides a function to set the defined names of the workbook
// or worksheet. If not specified scope, the default scope is workbook.
//
Expand Down
33 changes: 33 additions & 0 deletions test_excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def test_app_props(self):
self.assertEqual(props.application, "Go Excelize")
self.assertIsNone(err)

def test_default_font(self):
f = excelize.new_file()
font_name = "Arial"
self.assertIsNone(f.set_default_font(font_name))

def test_style(self):
f = excelize.new_file()
s = excelize.Style(
Expand Down Expand Up @@ -257,6 +262,7 @@ def test_style(self):
],
)

self.assertIsNone(f.move_sheet("Sheet2", "Sheet1"))
self.assertIsNone(f.ungroup_sheets())
self.assertIsNone(f.update_linked_value())
self.assertIsNone(f.save())
Expand Down Expand Up @@ -807,6 +813,33 @@ def test_cell_rich_text(self):
self.assertIsNone(f.save_as(os.path.join("test", "TestCellRichText.xlsx")))
self.assertIsNone(f.close())

def test_conditional_format(self):
f = excelize.new_file()
format, err = f.new_conditional_style(
excelize.Style(
font=excelize.Font(color="9A0511"),
fill=excelize.Fill(type="pattern", color=["FEC7CE"], pattern=1),
)
)
self.assertIsNone(err)
self.assertIsNone(
f.set_conditional_format(
"Sheet1",
"A1:A10",
[
excelize.ConditionalFormatOptions(
type="cell",
criteria="between",
format=format,
min_value="6",
max_value="8",
)
],
)
)
self.assertIsNone(f.save_as(os.path.join("test", "TestConditionalFormat.xlsx")))
self.assertIsNone(f.close())

def test_column_name_to_number(self):
col, err = excelize.column_name_to_number("Z")
self.assertEqual(col, 26)
Expand Down
37 changes: 33 additions & 4 deletions types_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ struct GraphicOptions
// Picture maps the format settings of the picture.
struct Picture
{
char *Extension;
char *Extension;
int FileLen;
unsigned char *File;
struct GraphicOptions *Format;
unsigned char InsertType;
unsigned char *File;
struct GraphicOptions *Format;
unsigned char InsertType;
};

// RichTextRun directly maps the settings of the rich text run.
Expand All @@ -221,6 +221,35 @@ struct Comment
struct RichTextRun *Paragraph;
};

// ConditionalFormatOptions directly maps the conditional format settings of the cells.
struct ConditionalFormatOptions
{
char *Type;
bool AboveAverage;
bool Percent;
int *Format;
char *Criteria;
char *Value;
char *MinType;
char *MidType;
char *MaxType;
char *MinValue;
char *MidValue;
char *MaxValue;
char *MinColor;
char *MidColor;
char *MaxColor;
char *BarColor;
char *BarBorderColor;
char *BarDirection;
bool BarOnly;
bool BarSolid;
char *IconStyle;
bool ReverseIcons;
bool IconsOnly;
bool StopIfTrue;
};

// FormControl directly maps the form controls information.
struct FormControl
{
Expand Down
Loading

0 comments on commit 3da5bc3

Please sign in to comment.