Python3 Openpyxl patternfill not detecting multiple colors - excel

I am trying to add 2 different colors to my cell rows using openpyxl engine for my xlsm file having macros based on the positive or negative values. But I end up having the most used color for all the cells.
Iam using openpyxl==3.0.6
Here is my code.
from openpyxl import Workbook
from openpyxl import load_workbook
from openpyxl.styles import Protection,PatternFill
wb = load_workbook(path,keep_vba=True)
ws = wb['Sheet1']
CheckColor = [-1,2,5,-5,5,-9,10,11,-1,-8,1,5,3]
yellow = PatternFill(patternType='solid',fgColor='FFF2CC')
pink = PatternFill(patternType='solid',fgColor='FF9999')
try:
for row in range(30,43):
for color in CheckColor:
if color > 0:
print("printing yellow color")
ws.cell(row = row, column = 2).value = 10
ws.cell(row = row, column = 2).fill = yellow
ws.cell(row = row, column = 3).value = 30
ws.cell(row = row, column = 3).fill = yellow
ws.cell(row = row, column = 4).value = 40
ws.cell(row = row, column = 4).fill = yellow
else:
print("printing pink")
ws.cell(row = row, column = 2).value = 10
ws.cell(row = row, column = 2).fill = pink
ws.cell(row = row, column = 3).value = 30
ws.cell(row = row, column = 3).fill = pink
ws.cell(row = row, column = 4).value = 40
ws.cell(row = row, column = 4).fill = pink
wb.save(path)
except Exception as e:
print(e)

As #Warcupine states, you select the first row (30) then create a new loop
for color in CheckColor:
which loops through the values in the CheckColor list for all of row 30. In other words you set all 13 colour options to row 30 ending with the last colour 3. Then move on to the next row (31) and do the same thing again. Thus all rows end up with the last colour in the list, 3 which being greater than 0 fills yellow.
You want to loop the rows and select the next value in the list for each. There is a couple of ways to do this, using enum allows you a count from 0 - 12 that can be used to set the colour index.
Code extract
...
CheckColor = [-1, 2, 5, -5, 5, -9, 10, 11, -1, -8, 1, 5, 3]
yellow = PatternFill(patternType='solid', fgColor='FFF2CC')
pink = PatternFill(patternType='solid', fgColor='FF9999')
try:
for enum, row in enumerate(range(30, 43)):
color = CheckColor[enum] # For each loop of the rows enum increments and sets color to the next CheckColor index
if color > 0:
print("printing yellow color")
ws.cell(row=row, column=2).value = 10
ws.cell(row=row, column=2).fill = yellow
ws.cell(row=row, column=3).value = 30
ws.cell(row=row, column=3).fill = yellow
...

Related

How to Create/Add multiple charts in ChartSheet using openpyxl?

There is option in excel sheet which allows user to export charts to chartsheet. Manually one can add any number of charts but while using openpyxl module I could only add one chart, when I try to add more than one chart its not showing up in the chartsheet, only thing i can see is my old graph which I just added at the beginning.
here is the sample code I have used.
import openpyxl as op
wb = op.load_workbook('input_excel.xlsx')
ws= wb.get_sheet_by_name('RawData')
chart_sheet_1=wb.create_chartsheet('for_graphs')
chart_1 = op.chart.ScatterChart()
chart_2 = op.chart.ScatterChart()
data_set_1 = op.chart.Reference(ws,1,3,1,51)
data_set_2 = op.chart.Reference(ws,2,3,2,51)
data_set_3 = op.chart.Reference(ws,3,3,3,51)
series_graphs_1 = op.chart.Series(data_set_3,data_set_1)
series_graphs_2 = op.chart.Series(data_set_3,data_set_2)
chart_1.series.append(series_graphs_1)
chart_2.series.append(series_graphs_2)
chart_sheet_1.add_chart(chart_1)
chart_sheet_1.add_chart(chart_2)
As shown above i am adding two charts in shown fashion is that wrong or is there any other ways to add two or more charts in one chartsheet using openpyxl or any other modules??????????. Thanks in advance.
I was just looking for some openpyxl solutions, their site lacks a lot of information if you compare it against xlswriter. The problem with xlswriter is that it can't open excel files, just create new ones.
And even that this question is old, I would like to answer it in case anybody else comes looking for answers.
This works for python 3 and openpyxl 3.0.9 (latest version as today)
For openpyxl you can add multiple charts into a single sheet, it has a trick: you need to first write the first chart, and comment the rest of the charts, afterwards, you can uncomment them, and it will no longer produce errors or warnings... perhaps a bug?...
Leaving some simple code in here, remember to comment and uncomment when you have the initial file.
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Font, Color, colors, PatternFill
from openpyxl.chart import ScatterChart, Reference, Series
from numpy import random
import numpy as np
data0 = random.randint(100, size=(100)) # Some random vectors
data1 = random.randint(100, size=(100))
data2 = random.randint(100, size=(100))
data3 = random.randint(100, size=(100))
filename = 'your_file.xlsx'
wb = Workbook()
ws = wb.worksheets[0]
ws['A1'].font = Font(bold = True)
ws.cell(row = 1, column = 1).value = 'Title goes here'
yellowFill = PatternFill(start_color = 'FFFF00', end_color = 'FFFF00', fill_type = 'solid')
redFill = PatternFill(start_color = 'FF0000', end_color = 'FF0000', fill_type = 'solid')
ws['A4'].font = Font(bold = True)
ws['A4'].fill = yellowFill
ws.cell(row = 4, column = 1).value = 'x'
ws.cell(row = 4, column = 2).value = 'Rnd_1'
ws['B4'].font = Font(color = 'FFFFFF', bold = True)
ws['B4'].fill = redFill
ws.cell(row = 4, column = 3).value = 'Rnd_2'
ws['C4'].font = Font(color = 'FFFFFF', bold = True)
ws['C4'].fill = redFill
ws.cell(row = 4, column = 4).value = 'Rnd_3'
ws['D4'].font = Font(color = 'FFFFFF', bold = True)
ws['D4'].fill = redFill
ws.cell(row = 4, column = 5).value = 'Rnd_4'
ws['E4'].font = Font(color = 'FFFFFF', bold = True)
ws['E4'].fill = redFill
row_i = 5
for i in range(99):
ws.cell(row = row_i, column = 1).value = i
ws.cell(row = row_i, column = 2).value = data0[i]
ws.cell(row = row_i, column = 3).value = data1[i]
ws.cell(row = row_i, column = 4).value = data2[i]
ws.cell(row = row_i, column = 5).value = data3[i]
row_i += 1
xvalues = Reference(ws, min_col = 1, min_row = 5, max_row = row_i) # just using the same x axis for all the rest of the charts
chart_1 = ScatterChart()
chart_1.y_axis.title = 'y value_1'
yvalues_1 = Reference(ws, min_col = 2, min_row = 5, max_row = row_i)
series_1 = Series(yvalues_1, xvalues, title = 'Data 1')
chart_1.series.append(series_1)
ws.add_chart(chart_1, 'H1')
#comment the following, and uncomment the second time
chart_2 = ScatterChart()
chart_2.y_axis.title = 'y value_2'
yvalues_2 = Reference(ws, min_col = 3, min_row = 5, max_row = row_i)
series_2 = Series(yvalues_2, xvalues, title = 'Data 2')
chart_2.series.append(series_2)
ws.add_chart(chart_2, 'H17')
chart_3 = ScatterChart()
chart_3.y_axis.title = 'y value_3'
yvalues_3 = Reference(ws, min_col = 4, min_row = 5, max_row = row_i)
series_3 = Series(yvalues_3, xvalues, title = 'Data 3')
chart_3.series.append(series_3)
ws.add_chart(chart_3, 'R1')
chart_4 = ScatterChart()
chart_4.y_axis.title = 'y value_4'
yvalues_4 = Reference(ws, min_col = 4, min_row = 5, max_row = row_i)
series_4 = Series(yvalues_4, xvalues, title = 'Data 4')
chart_4.series.append(series_4)
ws.add_chart(chart_4, 'R17')
# comment up to here
wb.save(filename)

Matching Quantities to Font Change by Code

I previously asked a question regarding how to change font of matching items from an array on Sheet2. Now I want to go a little further and return the matching quantities to Sheet1 that matches the red fonted items. I really want to use vba to become more familiar and learn.
Here is my code below:
Dim i, j, x As Integer
Dim box1, box2, box3, box4, box5, box6, box7, box8, box9 As String
Dim c As Range 'Define two ranges so that we can loop through both sheets to check the boxes
Dim d As Range
Sheets(1).Range("B11:B30, F11:F30").Font.ColorIndex = 0 'Remove the cell styles to apply new ones
box1 = Sheets(1).Cells.Range("C2")
box2 = Sheets(1).Cells.Range("C4")
box3 = Sheets(1).Cells.Range("C6")
box4 = Sheets(1).Cells.Range("F2")
box5 = Sheets(1).Cells.Range("F4")
box6 = Sheets(1).Cells.Range("F6")
box7 = Sheets(1).Cells.Range("I2")
box8 = Sheets(1).Cells.Range("I4")
box9 = Sheets(1).Cells.Range("I6")
Qty = Sheets(1).Cells.Range("D10")
'This refers to the checkbox
For i = 1 To 10 'Loop to find the checked box in sheet2
'box 1 items and quantites
If Sheets(2).Cells(1, i) = box1 And Qty Then 'Check for checked box
For Each c In Sheets(2).Range(Sheets(2).Cells(2, i), Sheets(2).Cells(6, i))
If Sheets(2).Range(Sheets(2).Cells(2, i), Sheets(2).Cells(6, i)) = Sheets(1).Range("D11:D30").Value And Sheets(2).Range(Sheets(2).Cells(2, i), Sheets(2).Cells(6, i)) <> "" Then
x = x
Else
x = x + 1
End If
For Each d In Sheets(1).Range("B11:B30, F11:F30")
If c = d Then
Sheets(1).Cells(d.Row, d.Column).Font.ColorIndex = 3 'changes matching item to red font
End If
Next d
Next c
Please note I'm just working on returning the quantities for box 1 so far. That's why I haven't posted the full code. Please see picture for better understanding of my lists I'm pulling from:Box Lists

VBA: Change font color on every nth cell based on cell value

I have managed to get code working that will grey out an entire row based on the value of a cell.
Now I need:
If cell value in AV < 100, change the font color in every 6th column, starting column 6.
Then if value in AW < 100, change the font color in every 6th column, starting column 7.
Then if value in AX < 100, change the font color in every 6th column, starting column 8.
I need to do this with every value in Range AV:AZ.
I'd really appreciate some help.
This is the code I managed to get working to grey out an entire row:
Range("AU1").Select
For i = 12 To LRow 'Do for Row 12 to end
If Cells(i, 47).value < 100 Then
Cells(i, 47).EntireRow.Font.Color = RGB(150, 150, 150)
End If
Next i
You can do it by removing the "EntireRow" and use of Offset instead
Range("AU1").Select
For i = 12 To LRow 'Do for Row 12 to end
If Cells(i, 47).value < 100 Then
For j = 1 to 10 ' this must be tuned to your need.
Cells(i, 47).Offset(0, 6*j +1).Font.Color = RGB(150, 150, 150)
Next j
End If
Next i

Search text string for a match and change font color

It's been 6 years since I've worked with Excel and i'm a little bit rusty. Here's my scenario:
I am exporting a list of issues to Excel. I need to be able differentiate the associated Link numbers in a cell (mulitple values) from each other. Example, i have two columns,
Key = the number for a ticket
Linked Issues = The Keys associated
I need a statement that would scan the Key column and find a match in the Linked Issues column. Then once the match is found the matching text will assume the font color of the Key.
Where this get complicated is each cell of the Linked Issues column could look something like this iss-3913, iss-3923, iss-1649. So essentially the scan would be for a match within the string. Any help is appreciated.
I am sorry, I don't have time to finish this right now, but wWould something like this help with maybe a loop for each cell in the first column?
Edit: Finished now, second edit to update to B5 and Z5, edit 3 fixed goof with column reference and updated to use variables to assign what column to look in.
Sub colortext()
start_row = 5
key_col = 2
linked_col = 26
i = start_row 'start on row one
Do While Not IsEmpty(Cells(i, key_col)) 'Do until empty cell
o = start_row 'start with row one for second column
Do While Not IsEmpty(Cells(o, linked_col)) 'Do until empty cell
If Not InStr(1, Cells(o, linked_col), Cells(i, key_col)) = 0 Then 'if cell contents found in cell
With Cells(o, linked_col).Characters(Start:=InStr(1, Cells(o, linked_col), Cells(i, key_col)), Length:=Len(Cells(i, key_col))).Font
.Color = Cells(i, key_col).Font.Color 'change color of this part of the cell
End With
End If
o = o + 1 'increment the cell in second column
Loop
i = i + 1 'increment the cell in the first column
Loop
End Sub
or maybe
Something like this?
Excel VBA: change font color for specific char in a cell range
This is an old post but I thought I would provide my work around to the conditional formating issue I was having.
Sub colorkey()
start_row = 5
key_col = 2
flag_col = 4
i = start_row 'start on row one
Do While Not IsEmpty(Cells(i, key_col)) 'Do until empty cell
Tval = Cells(i, flag_col).Value
Select Case Tval
Case "Requirement"
'cval = green
cVal = 10
Case "New Feature"
'cval = orange
cVal = 46
Case "Test"
'cval = lt blue
cVal = 28
Case "Epic"
'cval = maroon
cVal = 30
Case "Story"
'cval = dk blue
cVal = 49
Case "Theme"
'cval = grey
cVal = 48
Case "Bug"
'cval = red
cVal = 3
Case "NOT MAPPED"
'cval = Maroon
cVal = 1
End Select
Cells(i, key_col).Font.ColorIndex = cVal
i = i + 1 'increment the cell in the first column
Loop
End Sub
Sub colorlinked()
start_row = 5
key_col = 2
linked_col = 26
i = start_row 'start on row one
Do While Not IsEmpty(Cells(i, key_col)) 'Do until empty cell
o = start_row 'start with row one for second column
Do While Not IsEmpty(Cells(o, linked_col)) 'Do until empty cell
If Not InStr(1, Cells(o, linked_col), Cells(i, key_col)) = 0 Then 'if cell contents found in cell
With Cells(o, linked_col).Characters(Start:=InStr(1, Cells(o, linked_col), Cells(i, key_col)), Length:=Len(Cells(i, key_col))).Font
.Color = Cells(i, key_col).Font.Color 'change color of this part of the cell
End With
End If
o = o + 1 'increment the cell in second column
Loop
i = i + 1 'increment the cell in the first column
Loop
MsgBox "Finished Scanning"
End Sub

Alternating coloring groups of rows in Excel

I have an Excel Spreadsheet like this
id | data for id
| more data for id
id | data for id
id | data for id
| more data for id
| even more data for id
id | data for id
| more data for id
id | data for id
id | data for id
| more data for id
Now I want to group the data of one id by alternating the background color of the rows
var color = white
for each row
if the first cell is not empty and color is white
set color to green
if the first cell is not empty and color is green
set color to white
set background of row to color
Can anyone help me with a macro or some VBA code
Thanks
I use this formula to get the input for a conditional formatting:
=IF(B2=B1,E1,1-E1)) [content of cell E2]
Where column B contains the item that needs to be grouped and E is an auxiliary column. Every time that the upper cell (B1 on this case) is the same as the current one (B2), the upper row content from column E is returned. Otherwise, it will return 1 minus that content (that is, the outupt will be 0 or 1, depending on the value of the upper cell).
I think this does what you are looking for. Flips color when the cell in column A changes value. Runs until there is no value in column B.
Public Sub HighLightRows()
Dim i As Integer
i = 1
Dim c As Integer
c = 3 'red
Do While (Cells(i, 2) <> "")
If (Cells(i, 1) <> "") Then 'check for new ID
If c = 3 Then
c = 4 'green
Else
c = 3 'red
End If
End If
Rows(Trim(Str(i)) + ":" + Trim(Str(i))).Interior.ColorIndex = c
i = i + 1
Loop
End Sub
Based on Jason Z's answer, which from my tests seems to be wrong (at least on Excel 2010), here's a bit of code that happens to work for me :
Public Sub HighLightRows()
Dim i As Integer
i = 2 'start at 2, cause there's nothing to compare the first row with
Dim c As Integer
c = 2 'Color 1. Check http://dmcritchie.mvps.org/excel/colors.htm for color indexes
Do While (Cells(i, 1) <> "")
If (Cells(i, 1) <> Cells(i - 1, 1)) Then 'check for different value in cell A (index=1)
If c = 2 Then
c = 34 'color 2
Else
c = 2 'color 1
End If
End If
Rows(Trim(Str(i)) + ":" + Trim(Str(i))).Interior.ColorIndex = c
i = i + 1
Loop
End Sub
Do you have to use code?
if the table is static, then why not use the auto formatting capability?
It may also help if you "merge cells" of the same data. so maybe if you merge the cells of the "data, more data, even more data" into one cell, you can more easily deal with classic "each row is a row" case.
I'm barrowing this and tried to modify it for my use. I have order numbers in column a and some orders take multiple rows. Just want to alternate the white and gray per order number. What I have here alternates each row.
ChangeBackgroundColor()
' ChangeBackgroundColor Macro
'
' Keyboard Shortcut: Ctrl+Shift+B
Dim a As Integer
a = 1
Dim c As Integer
c = 15 'gray
Do While (Cells(a, 2) <> "")
If (Cells(a, 1) <> "") Then 'check for new ID
If c = 15 Then
c = 2 'white
Else
c = 15 'gray
End If
End If
Rows(Trim(Str(a)) + ":" + Trim(Str(a))).Interior.ColorIndex = c
a = a + 1
Loop
End Sub
If you select the Conditional Formatting menu option under the Format menu item, you will be given a dialog that lets you construct some logic to apply to that cell.
Your logic might not be the same as your code above, it might look more like:
Cell Value is | equal to | | and | White .... Then choose the color.
You can select the add button and make the condition as large as you need.
I have reworked Bartdude's answer, for Light Grey / White based upon a configurable column, using RGB values. A boolean var is flipped when the value changes and this is used to index the colours array via the integer values of True and False. Works for me on 2010. Call the sub with the sheet number.
Public Sub HighLightRows(intSheet As Integer)
Dim intRow As Integer: intRow = 2 ' start at 2, cause there's nothing to compare the first row with
Dim intCol As Integer: intCol = 1 ' define the column with changing values
Dim Colr1 As Boolean: Colr1 = True ' Will flip True/False; adding 2 gives 1 or 2
Dim lngColors(2 + True To 2 + False) As Long ' Indexes : 1 and 2
' True = -1, array index 1. False = 0, array index 2.
lngColors(2 + False) = RGB(235, 235, 235) ' lngColors(2) = light grey
lngColors(2 + True) = RGB(255, 255, 255) ' lngColors(1) = white
Do While (Sheets(intSheet).Cells(intRow, 1) <> "")
'check for different value in intCol, flip the boolean if it's different
If (Sheets(intSheet).Cells(intRow, intCol) <> Sheets(intSheet).Cells(intRow - 1, intCol)) Then Colr1 = Not Colr1
Sheets(intSheet).Rows(intRow).Interior.Color = lngColors(2 + Colr1) ' one colour or the other
' Optional : retain borders (these no longer show through when interior colour is changed) by specifically setting them
With Sheets(intSheet).Rows(intRow).Borders
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(220, 220, 220)
End With
intRow = intRow + 1
Loop
End Sub
Optional bonus : for SQL data, colour any NULL values with the same yellow as used in SSMS
Public Sub HighLightNULLs(intSheet As Integer)
Dim intRow As Integer: intRow = 2 ' start at 2 to avoid the headings
Dim intCol As Integer
Dim lngColor As Long: lngColor = RGB(255, 255, 225) ' pale yellow
For intRow = intRow To Sheets(intSheet).UsedRange.Rows.Count
For intCol = 1 To Sheets(intSheet).UsedRange.Columns.Count
If Sheets(intSheet).Cells(intRow, intCol) = "NULL" Then Sheets(intSheet).Cells(intRow, intCol).Interior.Color = lngColor
Next intCol
Next intRow
End Sub
I use this rule in Excel to format alternating rows:
Highlight the rows you wish to apply an alternating style to.
Press "Conditional Formatting" -> New Rule
Select "Use a formula to determine which cells to format" (last entry)
Enter rule in format value: =MOD(ROW(),2)=0
Press "Format", make required formatting for alternating rows, eg. Fill -> Color.
Press OK, Press OK.
If you wish to format alternating columns instead, use =MOD(COLUMN(),2)=0
Voila!

Resources