With openpyxl, you can use built-in styles defined in Excel like such:
wb = load_workbook("My_Template.xlsx")
ws = wb["My_super_Worksheet"]
ws["B2"].value = '=Hyperlink("https://stackoverflow.com", "SO")'
ws["B2"].style = "Hyperlink"
However, how would you use custom-styles defined in the Excel file? Something that could look like that:
wb = load_workbook("My_Template.xlsx")
ws = wb["My_super_Worksheet"]
wb.register_style("My_Custome_style") # defined as custom style in My_Template.xlsx
ws["B2"].value = '=Hyperlink("https://stackoverflow.com", "SO")'
ws["B2"].style = ""My_Custome_style""
The only way I have found was to re-define the styles in your program as Named Styles and save it but this would involve defining again the style instead of re-using what already exists.
Thanks a lot.
There is a workaround that does not answer to the question but can help solve the issue.
Setting a format in some given cells (for example in a worksheet called "STYLES")
Retrieving all the styles and registering them with openpyxl
Using the styles with their names
In Excel, that would give something like this:
In Python, you can add the following function and call it:
from copy import copy
import logging
from openpyxl.styles import NamedStyle
from openpyxl.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet
from typing import Dict
def _register_styles(wb:Workbook, ws_style: Worksheet) -> Dict[str, NamedStyle]:
"""Parse a column of cells and register the styles
(the style names are the values of cells)
Args:
wb: The workbook
ws_style: The worksheet with the styles defined
Returns:
Dictionary of styles, by names
"""
list_styles = dict() # type: Dict[str, NamedStyle]
# Register styles of a config cells
col, row = "A", 2
cell = ws_style[col + str(row)] # or in Python 3.6+: f"{col}{row}"
while cell.value:
style = NamedStyle(name=cell.value)
style.font = copy(cell.font)
style.fill = copy(cell.fill)
style.border = copy(cell.border)
style.alignment = copy(cell.alignment)
style.number_format = copy(cell.number_format)
try:
wb.add_named_style(style)
except ValueError as e:
logging.warning("W006: style creation skipped because {}".format(e))
list_styles[cell.value] = style
row += 1
cell = ws_style[col + str(row)] # or in Python 3.6+: f"{col}{row}"
return list_styles
wb = load_workbook("My_Template.xlsx")
_register_styles(wb, wb["STYLES"])
NOTE: if you use Python prior to version 3.4, just remove the type hints at the beginning of the function prototype:
def _register_styles(wb, ws_style):
Related
I need to copy cells like hyperlinks from one Excel file to another. I can't find anything relating to this problem. I can copy cells values but it's not what I need.
I tried to modify some examples of coping cells from one book to another, but it wasn't success
To copy the value in each cell from a source workbook (in this example 'foo1.xlsx') to a new workbook (destination workbook) and have the destination cells link back to the source cells
from openpyxl import load_workbook, Workbook
from openpyxl.worksheet.hyperlink import Hyperlink
source_path = "foo1.xlsx"
source_sheet = 'Sheet1'
source_wb = load_workbook(source_path)
source_ws = source_wb[source_sheet]
### Create a new workbook and worksheet to copy data to and rename the
### sheet to 'Sheet1'
destination_wb = Workbook()
destination_ws = destination_wb.active
destination_ws.title = 'Sheet1'
### Loop thru the rows and cells in the source sheet
for row in source_ws.iter_rows():
for source_cell in row:
cell_coord = source_cell.coordinate
# Skipping empty cells.
# Otherwise these cells in the destination workbook will be
# filled with the source filename.
if source_cell.value is None:
continue
### Create hyperlink to source cell
hyperlink = Hyperlink(target=source_path,
ref=cell_coord,
location = f'{source_sheet}!{cell_coord}')
### Copy source cell value to the destination sheet
destination_ws.cell(source_cell.row, source_cell.column).value = source_cell.value
### Update destination cell with hyperlink to source cell
destination_ws.cell(source_cell.row, source_cell.column).hyperlink = hyperlink
### Save new workbook specifying file name
destination_wb.save('foo2.xlsx')
###################################################
Change code to put full path to cell...
Instead of adding origin cell and hyperlink to it, set the cell value to the link path. Change the 8 lines from and including
### Create hyperlink to source cell
to
### Set full path to the original cell
destination_ws.cell(source_cell.row, source_cell.column).value = \
f'{source_path}#{source_sheet}!{cell_coord}'
moken's solution is more convenient and reliable.
This is the code to store hyperlink to a cell from a different file:
import os
from openpyxl import load_workbook, Workbook
def main():
input_workbook_path = r"c:\excel_books\input book.xlsx"
output_workbook_path = r"c:\excel_books\output book.xlsx"
input_wb = load_workbook(input_workbook_path)
output_wb = Workbook()
sheet_in = input_wb["Sheet1"]
sheet_out = output_wb["Sheet"]
cell_index = "B12"
anchor = "CLICK HERE"
# =HYPERLINK("[c:\excel_books\input book.xlsx]Sheet1!B12","CLICK HERE")
external_cell_link = f'=HYPERLINK("[{input_workbook_path}]{sheet_in.title}!{cell_index}", "{anchor}")'
sheet_out["A2"].value = external_cell_link
output_wb.save(output_workbook_path)
if __name__ == '__main__':
main()
This code is for getting the value from a cell from a different file
import os
from openpyxl import load_workbook, utils, Workbook
def construct_link(workbook_absolute_path, sheet_name, cell_index):
"""
The function onstructs full path to the cell in the external
book, e.g. - ='c:\excel_books\[input book.xlsx]Sheet1'!C1
"""
# Adding square brackets arround filename in the path.
# Before - c:\excel_books\input book.xlsx
# After - c:\excel_books\[input book.xlsx]
filename = os.path.basename(workbook_absolute_path)
dirname = os.path.dirname(workbook_absolute_path)
full_path = os.path.join(dirname, f"[{filename}]")
return f"={utils.quote_sheetname(full_path + sheet_name)}!{cell_index}"
def main():
input_workbook_path = r"c:\excel_books\input book.xlsx"
output_workbook_path = r"c:\excel_books\output book.xlsx"
input_wb = load_workbook(input_workbook_path)
output_wb = Workbook()
sheet_in = input_wb["Sheet1"]
sheet_out = output_wb["Sheet"]
external_cell_link = construct_link(
input_workbook_path,
sheet_in.title,
"C1")
sheet_out["A2"].value = external_cell_link
output_wb.save(output_workbook_path)
if __name__ == '__main__':
main()
This link might be helpful - Control when external references (links) are updated
So, given:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
ws['A1'] = dttm
The result in excel is that the correct date-time is written to the cell (you can see it where you'd input formulas). BUT, the cell display format is only MM/DD/YYYY.
I need the cell to display like "6/23/2014 13:56" instead of just "6/23/2014".
How can I explicitly format the cell to accomplish this?
Thanks!
Edit
#alecxe This solution works and is exactly what I asked for. I would like to be able to save styles like the solution by #Woodham. Unfortunately it raises a typeError (see comment). Any suggestions?
The simplest way to format a cell is using .number_format = "format" as in:
value = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
cell = ws['A1']
cell.value = value
cell.number_format = 'YYYY MMM DD'
This is tested in openpyxl (2.2.2)
For openpyxl 2.4.5 you'll no longer have access to NumberFormat and Style and will have to use NamedStyle. Here's some sample usage:
from openpyxl.styles import NamedStyle
date_style = NamedStyle(name='datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = date_style
Alternatively with the new NamedStyle class, you can set the style by the string name once NamedStyle has been instantiated:
from openpyxl.styles import NamedStyle
NamedStyle(name='custom_datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = 'custom_datetime'
Documentation here: https://openpyxl.readthedocs.io/en/stable/styles.html
I believe you will need to set a openpyxl.styles.Style on the cell(s) that you want to format.
Looking at the documentation here, something like this should work:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
s = Style(number_format=NumberFormat('dd-mm-yyyy h:mm:ss'))
ws['A1'] = dttm
ws['A1'].styles = s
Update:
Style class is no longer used, for the solution refer to this answer.
For openpyxl 2.3.4 the NumberFormat cannot be imported, but this code works to set the style:
from openpyxl.styles import Style
…
date_style = Style(number_format="DD/MM/YYYY HH:MM:MM")
ws['A1'].style = date_style
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle
xlsx_file = args.xlsx_file.name
# openning:
wb = load_workbook(filename = xlsx_file)
# create date style:
date_style = NamedStyle(name='date_style', number_format='DD.MM.YYYY HH:MM:MM')
# apply the style to the column H of the default sheet:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
cell.style = date_style
# saving:
wb.save(xlsx_file)
Edit: the above works for me, but somehow does not work on my coleagues machine. Converting the cell to string fixed that:
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
xlsx_file = 'file.xlsx'
date_format = '%Y-%b-%d'
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to column H of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
The same wrapped in a script:
#!/usr/bin/env python3
import argparse
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
# ==============
## parsing args:
desc="""
Applies python date format to a given column of the xlsx file (default sheet) and converts the column to a Excel text format.
Dependencies:
pip3 install --user --upgrade openpyxl
"""
parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s 0.01')
parser.add_argument('-f', '--file',
help = "xlsx file",
dest = 'xlsx_file',
type = argparse.FileType('r'),
)
parser.add_argument('-c', '--column',
help = "column (starting from A) (default to %(default)s)",
dest = 'column',
type = str,
default = "A",
)
parser.add_argument('-d', '--date-format',
help = "date format to use, e.g. %%d.%%m.%%Y (default to %(default)s)",
dest = 'date_format',
type = str,
default = '%Y-%b-%d',
)
args = parser.parse_args()
# =========
## program:
xlsx_file = args.xlsx_file.name
column_number = sum(
[ ord(char) - 97 + i*26 for i,char in enumerate(
list( args.column.lower() )
) ]
)
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to a given column of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[column_number]
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(args.date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
I found that this worked. Although number_format is used it seems to recognise the date format specified when put into the excel wb.
import datetime
date = datetime.date(2020, 2, 24) # python datetime format is yyyy mm dd
ws.cell(row=[row_ref], column=[col_ref], value=date)
ws.cell(row=[row_ref], column=[col_ref]).number_format = 'dd/mm/yy'
I need to open an xlsx document and color it. But I don't understand why it shows cell error it. My algorithm works like this:
Open xlsx, writer = pd.ExcelWriter(path, engine='xlsxwriter')
worksheet = writer.sheets['Sheet1']
col_style = Font(name = "Reem Kufi", size = 12, color = "DB3B22", italic =True)
for i in range(2,40):
worksheet.cell(row = i, column = 3).font = col_style
Error:- 'Worksheet' object has no attribute 'cell'
you will need to use Openpyxl's loadworkbook instead of ExcelWriter to achieve what you are looking for. Updated code here. Note that I have only changed the initial open file and sheet using the new code and required libraries and not changed rest of your code.
from openpyxl.styles import Font
from openpyxl import load_workbook
writer = load_workbook(filename='YourFile.xlsx')
worksheet = writer['Sheet1']
col_style = Font(name = "Reem Kufi", size = 12, color = "DB3B22", italic =True)
for i in range(2,40):
worksheet.cell(row = i, column = 3).font = col_style
writer.save('YourFile.xlsx')
Can someone please provide the below java code in Python
public static void testActualResultsStoring(String sheetname, int rownumber, String ActualResults)
{
try {
InputStream inp = new FileInputStream(a);
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheet(sheetname);
Row row = sheet.getRow(rownumber);
int num = row.getLastCellNum();
Cell cell = row.createCell(num++);
cell.setCellValue(ActualResults);
FileOutputStream fileOut = new FileOutputStream(a);
wb.write(fileOut);
fileOut.close();
} catch (Exception e) {
e.printStackTrace();
}
Thanks in advance!
Lot of adjustments, naturally, porting from one language to another. Included notes in the code, to try and help explain. However, code should run, assuming you have some file called "test.xlsx" with a sheet called "Sheet1".
from openpyxl import Workbook
from openpyxl import load_workbook
#Import libraries
def testActualResultsStoring(sheetname, rownumber, ActualResults):
try:
#Define file directory
fileDir = 'test.xlsx'
#wb file needs to load off of string that has file directory
wb = load_workbook(fileDir)
#Find sheet based on provided sheet name
sheet = wb[sheetname]
#No ++ in python, so we'll just find num and add 1 to it here. Note, max_column considers the entire sheet, so a more complicated answer is in order if the sheet has columns that aren't 100% filled.
num = sheet.max_column + 1
#No need to define a cell variable, can just assign value while calling it.
sheet.cell(row=rownumber, column=num, value=ActualResults)
#Save and close file
wb.save(fileDir)
wb.close()
#Except case
except(exception):
exception.printStackTrace()
#Call on method
testActualResultsStoring('Sheet1', 1, '1')
In Billing Roster - SOW.xlsx I have new column data one is named as SOW and other is named SOW Description (Match value for SOW).
And now when i open ACFC_Resource_Allocation.xlsx excel and for an example if select a value in D2 (SOW) cell from the dropdown i should get a matching value into E2 cell after the selection from dropdown.
I only have an idea than a vlookup from Excel like below should solve my case. Not sure how to achieve in python.
=VLOOKUP(D2,'[Billing Roster - SOW.xlsx]SOW List'!$A$1:$B$14,1,FALSE)
Tried below code
from openpyxl import *
from openpyxl.styles import *
import webbrowser
import pandas
from openpyxl.worksheet.datavalidation import DataValidation
# Read all Excels into pandas dataframes
sowexcel = pandas.read_excel('Billing Roster - SOW.xlsx')
#Load the existing Resource Allocation Excel
wb = load_workbook('ACFC_Resource_Allocation.xlsx')
allocationsheet = wb.active
def load():
maxrow = allocationsheet.max_row
sow_list = sowexcel['SOW #'].tolist()
column_sow = ','.join(sow_list)
validator_sow = DataValidation(type='list', formula1='"{}"'.format(column_sow), allow_blank=True)
allocationsheet.add_data_validation(validator_sow)
validator_sow.add('D2:D%s' %maxrow)
# save the file
wb.save('ACFC_Resource_Allocation.xlsx')
wb.close()
# Driver code
if __name__ == "__main__":
load()
file_open = webbrowser.open('ACFC_Resource_Allocation.xlsx')