I have googled a while for the methods to create excel files (*.xlsx, not the csv files) using programming languages, such as C++ or PHP. here is an example, http://www.the-art-of-web.com/php/dataexport/
But Ideally, I want to be able to specify the colours for each cell. For example, in VBScript using COM object Excel.Application, the code looks like:
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = False
objExcel.DisplayAlerts = False
Set objWorkbook = objExcel.Workbooks.Add
Set objWorksheet = objWorkbook.Worksheets(1)
With objWorksheet
.Cells(1,1).Interior.Color=RGB(245,245,245)
...
...
...
End With
objWorkbook.SaveAs("sample.xlsx")
objExcel.Quit
How can I do this without the use of COM object? I need the program to work platform-independent so COM object is not a good choice.
Update: here is an interesting post, that generates the VBS file and double click the VBS file will give you a nice picture in Excel by drawing cells with different colors.
You can modify cell backgrounds using python and the XLSX Writer module. While it is not the C++ or PHP language you mentioned, Python is cross platform and the code below works on the Fedora box I tested with.
First, you need python and the module mentioned above. You can install the module by doing this:
pip install xlsxwriter
Next, we are going to modify the first tutorial to provide a little color to the "Total" value.
import xlsxwriter
# Create a workbook and add a worksheet.
workbook = xlsxwriter.Workbook('Expenses01.xlsx')
worksheet = workbook.add_worksheet()
# Some data we want to write to the worksheet.
expenses = (
['Rent', 1000],
['Gas', 100],
['Food', 300],
['Gym', 50],
)
# Start from the first cell. Rows and columns are zero indexed.
row = 0
col = 0
green_format = workbook.add_format()
green_format.set_pattern(1) # This is optional when using a solid fill.
green_format.set_bg_color('#008000')
# Iterate over the data and write it out row by row.
for item, cost in (expenses):
worksheet.write(row, col, item)
worksheet.write(row, col + 1, cost)
row += 1
# Write a total using a formula.
worksheet.write(row, 0, 'Total')
worksheet.write(row, 1, '=SUM(B1:B4)', green_format)
workbook.close()
The things that were added are these lines. This utilizes the set_bg_color function.
green_format = workbook.add_format()
green_format.set_pattern(1) # This is optional when using a solid fill.
green_format.set_bg_color('green')
And the =SUM row was modified to use this green format:
worksheet.write(row, 1, '=SUM(B1:B4)', green_format)
This code creates a green cell at B5.
A note about the color: I provided the color 'green', because that is one of the few colors that are mapped to an RGB value. If you need to use something other than these colors, you can provide an RGB value. #008000 maps to green
green_format.set_bg_color('#008000')
This line is functionally identical to the one provided above.
If you want to use PHP, there are at least two different toolkits, which will generate Excel files without the use of COM Object :
PhpExcel and ExcelWriterXML.
For ExcelWriterXML for instance, changing the background color is $format->bgColor('Black'); , see complete example on the site.
Another way is to produce xlsx files (which are XML inside) through a template engine like TinyButStrong.
Related
Essentially I am generating a number of charts with xlsxwriter and trying to ensure gaps appear in the chart where I have #N/A's. Unfortunately it is not possible unless the show #N/A as empty cell setting is selected (I dont believe it is a supported feature as it is relatively new for excel).
I am currently using the following code to produce the above:
from xlsxwriter.workbook import Workbook
workbook = Workbook('chart.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
headings = ['Number', 'Value']
line_chart = workbook.add_chart({'type': 'line'})
line_chart.add_series({
'categories': '={1,2,3,4,5,6}',
'values': '={2,#N/A,2,3,4,#N/A}'
})
worksheet.insert_chart('F2', line_chart)
workbook.close()
I understand that if I changed it to the following, the problem would be fixed, however my aim is to use the above approach. If its not possible thats fine.
from xlsxwriter.workbook import Workbook
workbook = Workbook('chart.xlsx')
worksheet = workbook.add_worksheet()
bold = workbook.add_format({'bold': True})
headings = ['Number', 'Value']
data = [
[1, 2, 3, 4, 5, 6],
[2,'', 2, 3, 4,'=NA()'],
]
worksheet.write_row('A1', headings, bold)
worksheet.write_column('A2', data[0])
worksheet.write_column('B2', data[1])
line_chart = workbook.add_chart({'type': 'line'})
line_chart.add_series({
'categories': '=Sheet1!$A$2:$A$7',
'values': '=Sheet1!$B$2:$B$7',
})
worksheet.insert_chart('F2', line_chart)
workbook.close()
Unfortunately it is not possible unless the show #N/A as empty cell setting is selected (I dont believe it is a supported feature as it is relatively new for excel).
That is correct. It looks like that feature was added recently in Excel 16 (the XML element is <c16r3:dataDisplayOptions16>). So it isn't supported in XlsxWriter.
Note, it is possible to set the first 3 of those options using chart.show_blanks_as():
chart.show_blanks_as('span')
The available options are:
gap: Blank data is shown as a gap. The default.
zero: Blank data is displayed as zero.
span: Blank data is connected with a line.
I've looked over the Openpyxl docs, styles, cell module, and source code, but I'm not seeing what I need. Maybe I missed it (as new to Python).
I need to format certain cells within a spreadsheet(ss) differently from the rest of the ss. Am I able to specify an individual cell as shrink-to-fit, align right, align bottom, font color = grey, etc., while keeping the rest of the SS in the original style? Is this a Cell style that should be set or is there another resource to see what attributes Openpyxl will allow on an individual cells?
Here's a code snippet where the 'al' variable is working, but not the 'br', and I don't know why.
# Cell Alignment
al = Alignment(horizontal='center', vertical='center')
br = Alignment(horizontal='right', vertical='bottom')
for row in sheet['A1':'I43']:
for cell in row:
if cell == 'Hz':
cell.alignment = br #Help: not working
else:
cell.alignment = al
Thanks,
Phil
Ok, I think I've got it. So far, I'm accomplishing individual cell formatting using the NamedStyle object.
Here is my code:
# Set 'Named Styles', which are mutable, when need to apply formatting to different cells at once
headerrows = NamedStyle(name='headerrows')
headerrows.font = Font(bold=True, underline='none', sz=12)
headerrows.alignment = al
rooms = NamedStyle(name="rooms")
rooms.font = Font(bold=True, size=12)
rooms.border = Border(left=bold, top=bold, right=bold, bottom=bold)
sheet['A1'].style = rooms
sheet['A2'].style = headerrows
sheet['A3'].style = 'headerrows'
sheet['A4'].style = 'rooms'
There is one other question from Sep 2017 that addresses this same problem but does not have an answer: create a pivotchart with python win32com
I have tried several approaches to get this working so I want to explain these and hopefully get some insight from someone on how to get this working. This does not appear to be a well worn path so I do not have high hopes.
Environment details:
Windows 10
Office 2013
Anaconda 3.6
I use
win32com.client.gencache.EnsureDispatch('Excel.Application')
but I can also use
win32com.client.DispatchEx('Excel.Application')
or
win32com.client.dynamic.Dispatch('Excel.Application')
Each one returns this CLSID win32com.gen_py.00020813-0000-0000-C000-000000000046x0x1x8
Each one also has the same error.
I also ran this command as per documentation here:
C:\Users\home_dir>python AppData\Local\Continuum\anaconda3\pkgs\pywin32-223-py36hfa6e2cd_1\Lib\site-packages\win32com\client\makepy.py -i "Microsoft Excel 15.0 Object Library"
Output generated from makepy.py:
Microsoft Excel 15.0 Object Library {00020813-0000-0000-C000-000000000046}, lcid=0, major=1, minor=8
>>> # Use these commands in Python code to auto generate .py support
>>> from win32com.client import gencache
>>> gencache.EnsureModule('{00020813-0000-0000-C000-000000000046}', 0, 1, 8)
This version of gencache did not work successfully:
Excel = win32com.client.gencache.EnsureModule('{00020813-0000-0000-C000-000000000046}', 0, 1, 8)
Source Data
There are 100 rows and 18 columns in a Pandas Dataframe that I write to Excel using ExcelWriter
ew = pd.ExcelWriter('c:\devworkspace\SQL-Pandas-Excel\SampleData.xlsx')
sample_data_df.to_excel(ew, sheet_name='Source Data')
ew.save()
ew.close()
Excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
win32c = win32com.client.constants
wb = Excel.Workbooks.Open('c:\devworkspace\SQL-Pandas-Excel\SampleData.xlsx')
src_sheet = wb.Worksheets('Source Data')
rng_row = 100
rng_col = 18
rng_beg = src_sheet.Cells(1,2)
rng_end = src_sheet.Cells(rng_row,rng_col)
pvt_src_rng = src_sheet.Range(rng_beg, rng_end)
pvt_src_rng.Select()
pvt_src = "%s!R1C2:R%dC%d"%(src_sheet.Name,rng_row+1,rng_col+1) #add 1 for header and df index
Pivot Cache
I use PivotCaches().Create() as opposed to .Add() so I can specify Version=win32c.xlPivotTableVersion15 which is the correct version for office 2013. Otherwise it appeared to default to version 11.
pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=pvt_src, Version=win32c.xlPivotTableVersion15)
This change moved the dial but did not solve my problem - I am still getting an error and the chart is not getting created:
The formatting on the pivot table is enhanced when I applied this
change.
The root error code changed from -2147024809 which is "The
parameter is incorrect" To root error code -2146827284
Which it cannot translate to human readable message:
print(win32api.FormatMessage(error.excepinfo[5]))
error: (317, 'FormatMessageW', 'The system cannot find message text for message number 0x%1 in the message file for %2.')
Searching on this error code 2146827284 the discussions appear to be related to the excel object being busy. But Excel.Visible is set to 0 - also the default - so it is running in headless mode.
Adding Sheets, Creating the Pivot Table, Adding Fields is successful
These are all wrapped in try-except in the actual code - removed for brevity.
pvt_sheet = wb.Sheets.Add(After=src_sheet)
pvt_sheet.Name = 'Pivot Sheet'
pvt_rng_beg = pvt_sheet.Cells(2,2)
pvt_rng_end = pvt_sheet.Cells(2,2)
pvt_dest_rng = pvt_sheet.Range(pvt_rng_beg, pvt_rng_end)
pt = pc.CreatePivotTable(TableDestination=pvt_dest_rng,TableName='PivotTable1')
pt.AddFields(RowFields="claimant_type_desc" , ColumnFields="claim_cause_desc" )
pt.AddDataField(Field=pt.PivotFields("total_direct_payment"), Caption="Total Incurred")
I can add a sheet or a chart as the "ChartDestination" but neither option alters the outcome. I can validate that the object is getting added successfully.
#chrt_sheet = wb.Charts.Add(After=pvt_sheet)
chrt_sheet = wb.Sheets.Add(After=pvt_sheet)
chrt_sheet.Name = 'Pivot Chart'
ChartDestination argument is the only required argument the other arguments are optional: XlChartType, Left, Top, Width, Height
Docs here:
Based on examples I pass the name of the sheet or chart object as a string 'Pivot Sheet'. That should work.
pch = pc.CreatePivotChart(ChartDestination='Pivot Chart')
Because the parameter is defined as a Variant then I explicitly assign the string to a Variant object. I tried a range of different variant types but that did not yield a different outcome.
chrt_sheet_name_variant = win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, chrt_sheet.Name)
print(chrt_sheet_name_variant.value)
print(chrt_sheet_name_variant.varianttype)
print(chrt_sheet_name_variant.__class__)
print(type(chrt_sheet_name_variant))
pch = pc.CreatePivotChart(ChartDestination=chrt_sheet_name_variant)
#Output:
#Pivot Chart
#16392
#<class 'win32com.client.VARIANT'>
#<class 'win32com.client.VARIANT'>
This is the error that is generated:
File "C:\Users\xxxxxx\AppData\Local\Temp\gen_py\3.6\00020813-0000-0000-C000-000000000046x0x1x8\PivotCache.py", line 36, in CreatePivotChart
, XlChartType, Left, Top, Width, Height
com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146827284), None)
Thank you Boussif for finding the way on this.
Here is my working solution:
Generate a Pivot Chart from your Pivot Table
Using the Workbook object that we created earlier we can "insert" a Chart to our workbook and assign a name to our chart - name appears in the tab.
Because the Pivot Table is an object in the Pivot Cache - which is an object in the Workbook, the Chart object is added as a Pivot Chart and uses the Pivot Table as its the data.
Read the relevant documentation here:
https://learn.microsoft.com/en-us/office/vba/api/excel.workbook.charts
https://learn.microsoft.com/en-us/office/vba/api/excel.chart(object)
chrt_sheet = wb.Charts.Add()
chrt_sheet.Name = 'Pivot Chart'
Specify the Chart Type
The chart type corresponds to the same Chart Types that are available in the Excel program.
Choose from a list of enumerated types.
For example a 3D Column Chart has a value of xl3DColumn coded as follows:
win32c.xl3DColumn
Use the win32c object ew defined earlier to reference all constant values including enumerated lists.
Documented here:
https://learn.microsoft.com/en-us/office/vba/api/excel.chart.charttype
https://learn.microsoft.com/en-us/office/vba/api/excel.xlcharttype
chrt_sheet.ChartType = win32c.xl3DColumn
Set the Chart Title
The sheet object will not have a ChartTitle property until to set HasTitle Property to True
chrt_sheet.HasTitle = True
chrt_sheet.ChartTitle.Text = "Chart Title"
Set the Color Scheme
ChartColor values 10 - 26 correspond to the Change Colors menu on the DESIGN tab of the CHART TOOLS ribbon.
chrt_sheet.ChartColor = 13
Specify a Chart Layout
Layout is optional
Layouts are the same as the list of Layouts to choose from in the Excel program.
In this case they are not provided as an enumerated list. So you have to provide a number for the layout. It will generally be from 1 to 10. Layouts are different depending on the respective Chart Type. To find out which layout to choose you will need to run the Excel program and try each one. If you hover your mouse over the Layout option it will display a "Tool Tip" with the name of the Layout. For example: 'Layout 7'. You would provide the number associated with your respective layout.
you use the ApplyLayout method to choose the Layout:
ApplyLayout(Layout, Chart Type)
Documented here:
https://learn.microsoft.com/en-us/office/vba/api/excel.chart.applylayout
chrt_sheet.ApplyLayout(7, win32c.xl3DColumn)
Specify a Chart Style
Style is optional
Documentation states that 1 to 48 are valid values. However the correct range depends on which Chart Type is chosen.
Depending on the Chart Type 201 to 352 is also valid.
To get the numbers numbers that match the style selections that are available for your Chart Type:
From the Developer Tab - Run a macro
From the Chart Design Tab - Select all the styles
From the Devloper Tab - Stop running the macro
From the Developer Tab - Edit the macro
This will reveal the correct values for your case
For the 3D Column Chart Type, the range is 286 to 297
Relevant documentation here:
https://learn.microsoft.com/en-us/office/vba/api/excel.chart.chartstyle
xlChartStyle = 294
chrt_sheet.ClearToMatchStyle
chrt_sheet.ChartStyle = xlChartStyle
Chart Style Experiment
If you are curious and want to see what each one looks like then run this code
hint: Remove the comments '#'
#import time
#from time import sleep
#for xlChartStyle in range(286, 297):
# try:
# chrt_sheet.ClearToMatchStyle
# chrt_sheet.ChartStyle = xlChartStyle
# chrt_sheet.ChartTitle.Text = "Chart Style = "+str(xlChartStyle)
# sleep(1)
# except pythoncom.com_error as error:
# print("Chart Style = %s" % str(xlChartStyle))
Format Axis Labels
There are three Axis dimensions specified by the XlAxisType Enumeration
X axis = xlCategory
Y Axis = xlValue
Z Axis = xlSeriesAxis (3D Charts only)
In this example we are also removing the Z Axis Tick Labels
chrt_sheet.Axes(win32c.xlCategory).AxisTitle.Text = "X Axis Title"
chrt_sheet.Axes(win32c.xlSeries).TickLabelPosition = win32c.xlNone
chrt_sheet.Axes(win32c.xlSeries).HasTitle = True
chrt_sheet.Axes(win32c.xlSeries).AxisTitle.Text = "Z Axis Title"
chrt_sheet.Axes(win32c.xlValue).AxisTitle.Text = "Y Axis Title"
I am using xlrd, xlwt, and xlutils on the Windows Vista OS with Python 2.7. I have a set of DNA sequences in an excel worksheet that are 100 characters long, with each sequence in a single cell. I am trying to highlight characters at specific positions within each of these sequences in excel (bold them or change color), but have not found a way to format individual characters within a cell. Applying a style applies it to the entire cell to my knowledge. Therefore I am trying to break the sequences down into individual components where some parts of the sequence will be modified with a style while others won't, and to then collate these back together into a single string in a single cell.
Code:
rb = open_workbook('Mybook', formatting_info=True)
rs = rb.sheet_by_index(0)
wb = copy(rb)
ws = wb.get_sheet(0)
minus35style = xlwt.easyxf('font: bold 1') # style I'd like for just one character
for b in range(0, 368, 1):
rscellin = rs.cell(b,9)
f = rscellin.value
tominus35 = str(f[0:34])
minus35 = str(f[35:36])
ws.write(b, 14, tominus35)
ws.write(b, 14, minus35, minus35style)
wb.save('Mybook')
My problem is that adding a style changes the whole cell, and I want just certain characters to be modified. Additionally, subsequent writing to the same cell overwrites what was there previously. Is there a way to modify individual characters in a single cell, or to add differently formatted text to an existing cell that already has text in it?
Please let me know if you require additional information that I've overlooked. I appreciate your time in advance.
Brett
Recent versions of xlwt include the ability to use Rich Text within a cell. Where normally you would use ws.write, use ws.write_rich_text instead. The first two parameters are the row index and column index, as usual; but the next parameter is a sequence of components. Each component can either be a "naked" text value or a (text, font) pair. The naked text values will use the font from the cell's overall style, which can be specified using the optional fourth parameter.
For the (text, font) pairs, it is simplest to generate fonts using the new easyfont feature, which is kind of like easyxf but only for fonts. Here is an example:
import xlwt
wb = xlwt.Workbook()
ws = wb.add_sheet('Sheet1')
font0 = xlwt.easyfont('')
font1 = xlwt.easyfont('bold true')
font2 = xlwt.easyfont('color_index red')
style = xlwt.easyxf('font: color_index blue')
seg1 = ('bold', font1)
seg2 = ('red', font2)
seg3 = ('plain', font0)
seg4 = ('boldagain', font1)
ws.write_rich_text(2, 5, (seg1, seg2, seg3, seg4))
ws.write_rich_text(4, 1, ('xyz', seg2, seg3, '123'), style)
wb.save('rich_text.xls')
You should be able to adapt the above for your purposes. Note that you still have to write or overwrite the whole cell at once; you can't go back and update only part of a cell later.
I know this is very basic, but I have been using Python for 2 weeks now and banging my head against the wall trying to fix this. I have checked almost every single thread in here and tried to make sense of the Python 2.7 documentation and also researched whatever excerpts from Programming with Win32 by Hammod I could find.
Below is a section of my code. I am pulling in a named range from excel into a Python Gui. The data comes into row=4, column=1 as I coded it. The problem is I need to parse this named range so that the range (which is four float points) is split amongst row=4 clomuns 1,2,3,4. I know this is basic. I wouldnt have posted if I could find the answer anywhere else. If someone could just point me in the right direction i would really appreciate it.
xl = win32com.client.Dispatch('excel.application')
wbs = xl.Workbooks
wb = wbs.Open('C:\Users\Owner\Desktop\AutoPrime.xls')
xl.visible = 1
xlSheet = xl.Sheets(1)
xlSheet.Range('Fronts').Value
#Frame
Ftreas = Frame(F, relief="groove", border=2)
Ftreas.pack(side="left")
lp2a = Label(Ftreas, text=xl.ActiveSheet.Range('Fronts').Value, justify='center')
lp2a.grid(row=4,column=1, sticky='EW')
While I have not worked with Phyton before, I know that your line xlSheet.Range('Fronts') will return a Range object. You can access individual cells of a Range object with the default property, e.g.:
rng = xlSheet.Range('Fronts')
val1 = rng(1, 1).Value
val2 = rng(2, 1).Value //this returns the cell in row 2/column 1 of the range
arr = rng.Cells //this will return a 4x1 array of values