I have looked into many stackoverflow questions but none of them seemed to solve my problem. I am using Python and Openpyxl to fill a whole row with red given a certain condition. I did all the importations necessary :
from openpyxl.styles import PatternFill, NamedStyle, Color
from openpyxl.styles.colors import RED
And my code is the following :
for cell in sheet[i]:
cell.style = NamedStyle(fill=PatternFill(patternType='solid',
fill_type='solid',
fgColor=Color(RED)))
When I ask to print the first occurence of cell it gives me
<Cell 'Divers'.A4>
which is what I am looking for.
However, the following error comes every time : "Style Normal exists already". There is absolutely no cell formatting or style whatsoever in the rest of the code but the Excel file cells are indeed filled with yellow already.
Any idea on how to solve this ? Thanks in advance for any help.
If using a NamedStyle, you're required to pass a name.
red_foreground = NamedStyle(
name="RedForeground",
fill=PatternFill(
patternType='solid',
fill_type='solid',
fgColor=Color(RED)
)
)
Since you're assigning this NamedStyle to more than one cell, it makes sense to register it to your workbook.
wb.add_named_style(red_foreground)
Then you can update it's application to cells, like so:
for cell in sheet[i]:
cell.style = "RedForeground"
Reference:
Creating NamedStyle
NamedStyle Constructor
I also have this problem, and finally found that it was because there were 2 styles, of which had the same name. This is usually caused when you use copy.copy(style). Then after change one of the style.name = 'newname', it will work.
This code would solve already existing named styles.
for index,cur_style in enumerate(excel_workbook._named_styles):
if cur_style.name == 'my_new_style':
excel_workbook._named_styles[index] = my_new_style
my_new_style.bind(excel_workbook)
break
else:
excel_workbook.add_named_style(my_new_style)
However, in your case, you should use some other name than "Normal", because "Normal" is the default named style, just find another name and you can use the code I pasted
There is another way to solve traceback by adding existing styles:
if not 'Style_A' in wb.named_styles:
wb.add_named_style(Style_A)
Related
After trying to use openpyxl to try to know which styles is applied in order to get the the actual background color of a cell after the conditional formatting has been applied and realized that I would have to write a formula parser (and it makes no sense to re-write excel and I would have to deal with chained formula cell values, etc).
I am now reaching the PyUno interface to get access via a libreoffice instance running headless and reaching the XSheetConditionalEntry object trough the PyOO interface.
Looks that I have reached the exact same place, I have the cell and the formula; but no way of knowing which of the conditional formatting styles applies or not:
def processFile(filename):
soffice = subprocess.Popen(officeCommand, shell=True)
desktop = pyoo.Desktop(pipe='hello')
doc = desktop.open_spreadsheet(filename)
sheet = doc.sheets['STOP FS 2023']
cell = sheet[5,24]
cellUno = cell._get_target()
print(f"{cellUno.getPropertyValue('CellBackColor')=}")
print(f"{cellUno.getPropertyValue('CellStyle')=}")
for currentConditionalFormat in cellUno.getPropertyValue('ConditionalFormat'):
print(f"{currentConditionalFormat.getStyleName()=}")
print(f"{currentConditionalFormat.getOperator()=}")
getting the following results
cellUno.getPropertyValue('CellBackColor')=-1
cellUno.getPropertyValue('CellStyle')='Default'
currentConditionalFormat.getStyleName()='ConditionalStyle_4'
currentConditionalFormat.getOperator()=<Enum instance com.sun.star.sheet.ConditionOperator ('BETWEEN')>
currentConditionalFormat.getStyleName()='ConditionalStyle_3'
currentConditionalFormat.getOperator()=<Enum instance com.sun.star.sheet.ConditionOperator ('NONE')>
currentConditionalFormat.getStyleName()='ConditionalStyle_2'
currentConditionalFormat.getOperator()=<Enum instance com.sun.star.sheet.ConditionOperator ('NONE')>
currentConditionalFormat.getStyleName()='ConditionalStyle_1'
currentConditionalFormat.getOperator()=<Enum instance com.sun.star.sheet.ConditionOperator ('NONE')>
The style that is being applied is the ConditoinalStyle_3
This post has helped a bit but it is intended to work inside of a macro, and looks like heir forum sign up is broken, as I would would have tried to ask the same question over there.
I am struggling with the following using openpyxl version 3.0.7
I want to copy the style of one cell to another. That means, background colour, font, etc.
However, I don't know how I can do that.
My initial idea was basically to do
sheet["E1"].font = sheet["D1"].font.
That bit shoots out TypeError: unhashable type: 'StyleProxy'.
Using just .style doesn't really do all that much so it's not suitable.
I found a few solutions online like the one from here. However, I don't know how I can apply it to my specific needs seeing as I struggle to even transfer the font from one cell to another.
Shortly after typing this out, I found the solution.
from copy import copy
wb = openpyxl.load_workbook('C:\\path...\\')
sheet = wb["Name of the worksheet"]
sheet["E1"].font = copy(sheet["D1"].font)
sheet["E1"].border = copy(sheet["D1"].border)
sheet["E1"].fill = copy(sheet["D1"].fill)
sheet["E1"].number_format = copy(sheet["D1"].number_format)
sheet["E1"].protection = copy(sheet["D1"].protection)
sheet["E1"].alignment = copy(sheet["D1"].alignment)
The only thing left to do would be to do this in a loop. That would be achievable by doing something like
for i in range(....):
sheet["E" + str(i)].font= copy(sheet["D" +str(i)].font)
etc.
Where can I find a list of all possible PatternFill fill types for openpyxl? The best I could do was
from openpyxl.styles import PatternFill
PatternFill(fill_type='diagonal_stripe')
to produce an error that lists them all out for me.
ValueError: Value must be one of {'gray0625', 'lightGrid', 'lightHorizontal', 'lightTrellis', 'lightVertical', 'darkUp', 'solid', 'darkVertical', 'mediumGray', 'darkTrellis', 'lightGray', 'darkGrid', 'darkHorizontal', 'gray125', 'lightUp', 'darkDown', 'darkGray', 'lightDown'}
This is information I was looking for! Where can I find this kind of thing in the documentation?
You can find it documented under the patternType attribute of PatternFill.
I agree with myself; the documentation is not very helpful in showing you that fill_type is an alias for patternType:
Aliases can be used when either the desired attribute name is not allowed or confusing in Python (eg. “type”) or a more descriptve name is desired (eg. “underline” for “u”)
FYI: It likewise appears that start_color and end_color are aliases for fgColor and bgColor, respectively. (Apparently you start coloring in the foreground and end in the background...?)
The code snippet came from the official documentation of xlwings here and it is the setup for my question.
import xlwings as xw
sht = xw.Book().sheets[0]
sht.range('A1').value = [['Foo1', 'Foo2'], [1, 2]]
chart = sht.charts.add()
chart.set_source_data(sht.range('A1').expand())
chart.chart_type = 'line'
chart.name
Running print(chart.api) outputs the tuple below.
(<xlwings._xlwindows.COMRetryObjectWrapper at 0x1fcd60c9a90>, <xlwings._xlwindows.COMRetryObjectWrapper at 0x1fcd60c9f28>)
If I want to use the api attribute to do some basic chart manipulation like remove the legend and add a title, it only works if I do it to chart.api[1]. For instance the code below works fine. It removes the chart legend and adds a title.
chart.api[1].HasLegend = 0
chart.api[1].SetElement(2)
chart.api[1].ChartTitle.Text = 'A title'
However, anything I do to chart.api[0] yields an error, (for instance print(chart.api[0].HasLegend) yields an error). I can't understand what kind of object this is or how it is useful. I can't find anything regarding this in the official documentation.
Finally my question is: what is the object at the index 0 above? Please, help me grok what it is.
There is another post that addresses your question about the object at the index 0.
set chart name in Xlwings
The expression chart.api returns a tuple with two COM wrappers. I'm
not really sure why there are two COM wrappers, but it seems that you
need the second one to access the chart. Hence the use of chart.api[1]
here.
I can't seem to find a way to change the length f the entire table in a word-document. I have only seen examples of ways to change the columns in the table, not the actual table itself.
Would be great if someone could tell me how to do it :)
Here is my code:
from docx import Document
document = Document()
table = document.add_table(rows=4, cols=2)
table.style = 'Table Grid'
The Table class has methods to add rows.
https://python-docx.readthedocs.io/en/latest/api/table.html#docx.table.Table.add_row
Found a solution to my problem. I got this from ANOTHER user here # stack. Can't seem to find the link tho....
The original code is NOT mine, I only modified it a little.
def ChangeWidthOfTable(table,width,column):
for columnVarible in range(0,column):
for cell in table.columns[columnVarible].cells:
cell.width = Cm(width)