Styles when importing pd.Dataframe to excel.openpyxl library - python

I need help.You need to add pd.DataFrame to excel with a certain style.The openpyxl library. When executing the code specified below, the data is added to the sheet.I usually find the answers to my questions on this site or YouTube, but this problem has put me at a dead end.Even after reading the pandas.io.formats.style.Styler.to_excel documentation, I didn't understand anything. This is my first question on this site.If I made a mistake with the design of the question, then let me know so that I can work on the errors.I need a specific cell format (font, size, borders, etc.).I poked around a little and found this option.How do I add more style to indexes and headers? This option makes the auto-height of the line fit the size of the text, maybe there is still an option for the auto-width of the column?
my result
the result I wantThanks!!
properties = {"border": "2px solid black", "font-size": "28px"}
with pd.ExcelWriter(f'{name}.xlsx', mode="a", if_sheet_exists='replace', engine="openpyxl") as writer:
vkd.style.set_properties(**properties).to_excel(writer, sheet_name="Остаток по датам коровы",startcol=1,startrow=1)

Related

Is there a method to check if a portion of text inside a xlsx cell is bold?

Using openpyxl we can properly check if a cell is fully bold/not bold, but we cannot work with richtext so having two words, one bolded and one not, will make the check fail.
This can be done correctly with xlrd, but it doesn't support xlsx files. Converting from xlsx to xls is risky, especially in my use case, since I have a big file with many languages and I think i could lose information.
How can I check if cells substrings are bold inside a xlsx file?
TL;DR: not yet, but upcoming openpyxl v3.1 will be able to satisfy your request.
I took a quick tour through the late 2022 state of python-excel affair with respect to this very feature, which relies on being able to manage Rich Text objects as cell contents:
pylightxl was new to me, so I quickly browsed the project for a bit. But as the name indicates, it is geared towards a small, maintainable feature set. Only cell values are in scope, formatting is intentionally skipped.
xlrd supports rich text, though only legacy .xls, as you pointed out in your question already.
xlsxwriter luckily is able to construct cells containing rich text with mixed formatting (example), but unfortunately only deals with writing files.
openpyxl finally currently does not support rich text... but: with the upcoming release 3.1, it will, thanks to merge request !409 that was completed earlier this year.
example, using openpyxl 3.0.10:
have this sample:
then use:
import openpyxl
test_file = "test_spreadsheet.xlsx"
obj = openpyxl.load_workbook(test_file)
sheet_ref = obj.active
cell1 = sheet_ref.cell(row = 3, column = 2)
cell_font1 = cell1.font
print(cell_font1.b)
cell = sheet_ref.cell(row = 4, column = 2)
cell_font = cell.font
print(cell_font.b)
result:
$ python test.py
False
True
so you could build it yourself:
def is_text_in_cell_bold(cell_obj):
if (cell_obj.font.b):
return True
return False

Python Excel copy cell style from one to another openpyxl

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.

Can python-docx preserve font color and styles when importing documents?

Essentially what I need to do is write a program that takes in many .docx files and puts them all in one, ordered in a certain way. I have importing working via:
import docx, os, glob
finaldocname = 'Midterm-All-Questions.docx'
finaldoc=docx.Document()
docstoworkon = glob.glob('*.docx')
if finaldocname in docstoworkon:
docstoworkon.remove(finaldocname) #dont process final doc if it exists
for f in docstoworkon:
doc=docx.Document(f)
fullText=[]
for para in doc.paragraphs:
fullText.append(para.text) #generates a long text list
# finaldoc.styles = doc.styles
for l in fullText:
# if l=='u\'\\n\'':
if '#' in l:
print('We got here!')
if '#1 ' not in l: #check last two characters to see if this is the first question
finaldoc.add_section() #only add a page break between questions
finaldoc.add_paragraph(l)
# finaldoc.add_page_break
# finaldoc.add_page_break
finaldoc.save(finaldocname)
But I need to preserve text styles, like font colors, sizes, italics, etc., and they aren't in this method since it just gets the raw text and dumps it. I can't find anything on the python-docx documentation about preserving text styles or importing in something other than raw text. Does anyone know how to go about this?
Styles are a bit difficult to work with in python-docx but it can be done.
See this explanation first to understand some of the problems with styles and Word.
The Long Way
When you read in a file as a Document() it will bring in all of the paragraphs and within each of these are the runs. These runs are chunks of text with the same style attached to them.
You can find out how many paragraphs or runs there are by doing len() on the object or you can iterate through them like you did in your example with paragraphs.
You can inspect the style of any given paragraph but runs may have different styles than the paragraph as a whole, so I would skip to the run itself and inspect the style there using paragraphs[0].runs[0].style which will give you a style object. You can inspect the font object beyond that which will tell you a number of attributes like size, italic, bold, etc.
Now to the long solution:
You first should create a new blank paragraph, then you should go and add_run() one by one with your text from your original. For each of these you can define a style attribute but it would have to be a named style as described in the first link. You cannot apply a stlye object directly as it won't copy the attributes over. But there is a way around that: check the attributes that you care about copying to the output and then ensure your new run applies the same attributes.
doc_out = docx.Document()
for para in doc.paragraphs:
p = doc_out.add_paragraph()
for run in para.runs:
r = p.add_run(run.text)
if run.bold:
r.bold = True
if run.italic:
r.italic = True
# etc
Obviously this is inefficient and not a great solution, but it will work to ensure you have copied the style appropriately.
Add New Styles
There is a way to add styles by name but because it isn't likely that the Word document you are getting the text and styles from is using named styles (rather than just applying bold, etc. to the words that you want), it is probably going to be a long road to adding a lot of slightly different styles or sometimes even the same ones.
Unfortunately that is the best answer I have for you on how to do this. Working with Word, Outlook, and Excel documents is not great in Python, especially for what you are trying to do.

Style Normal exists already - Python - OpenPyxl

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)

Python Reportlab combine paragraph

I hope you can help me trying to combine a paragraph, my style is called "cursiva" and works perfectly also I have other's but it's the same if I change cursiva to other one. the issue is that If I use this coude o get this.
As you can see guys it shows with a line break and I need it shows togetter.
The problem is that i need to make it like this (one, one) togetter because I need to use two styles, the issue here is that I'm using arial narrrow so if I use italic or bold I need to use each one by separate because the typography does not alow me to use "< i >italic text< /i > ", so I need to use two different styles that actually works fine by separate.
how can I achive this?
cursiva = ParagraphStyle('cursiva')
cursiva.fontSize = 8
cursiva.fontName= "Arialni"
incertidumbre=[]
incertidumbre.extend([Paragraph("one", cursiva), Paragraph("one", cursiva)])
Thank you guys
The question you are asking is actually caused by a workaround for a different problem, namely that you don't know how to register font families in Reportlab. Because that is what is needed to make <i> and <b> work.
So you probably already managed to add a custom font, so the first part should look familiar, the final line is probably the missing link. It is registering the combination of these fonts a family.
from reportlab.pdfbase.pdfmetrics import registerFontFamily
pdfmetrics.registerFont(TTFont('Arialn', 'Arialn.ttf'))
pdfmetrics.registerFont(TTFont('Arialnb', 'Arialnb.ttf'))
pdfmetrics.registerFont(TTFont('Arialni', 'Arialni.ttf'))
pdfmetrics.registerFont(TTFont('Arialnbi', 'Arialnbi.ttf'))
registerFontFamily('Arialn',normal='Arialn',bold='Arialnb',italic='Arialni',boldItalic='Arialnbi')

Categories