Creating a “color scale” using vba (avoiding conditional formatting)

半腔热情 提交于 2019-12-21 05:47:15

问题


I'm looking for a way to apply a color scale to a set of cells via VBA code but not by applying some conditional formatting... I want to apply them as static colors (InteriorColor)

I've searched plenty of excel sites, google and stackoverflow and found nothing :(

For my situation if you look at the following picture:

You can see I've given it a color scale, in this example though I have done the color scale via Conditional formatting. I want to create the color scale via VBA but it must avoid using conditional formatting, I want to assign interior colors to the cells so that the colors are static which makes them visible on all mobile excel viewers, faster, won't change if I was to remove any numbers/rows.

Here are some example data Just save it in a csv and open it in excel to see the data in excel :P:

Data 1 (Yes there are blanks),Data 2,Data 3,Data 4,Data 5,Data 6
155.7321504,144.6395913,1,-4,-9.3844,0.255813953
113.0646481,120.1609771,5,-2,-2.5874,0.088082902
126.7759917,125.3691519,2,0,-0.0004,0.107843137
,0,7,,,0.035714286
123.0716084,118.0409686,4,0,0.3236,0.118881119
132.4137536,126.5740362,3,-2,-3.8814,0.090909091
70,105.9874422,6,-1,-0.3234,0.103896104

I do use the following in python but obviously I can't use this code in VBA, the following code successfully assigns hex colors to the numbers from a predefined array of 50 colors so it's pretty accurate.

def mapValues(values):
    nValues = np.asarray(values, dtype="|S8")
    mask = (nValues != '')
    maskedValues = [float(i.split('%')[0]) for i in nValues[mask]]
    colorMap = np.array(['#F8696B', '#F86E6C', '#F8736D', '#F8786E', '#F97E6F', '#F98370', '#F98871', '#FA8E72', '#FA9373', '#FA9874', '#FA9E75', '#FBA376', '#FBA877', '#FBAD78', '#FCB379', '#FCB87A', '#FCBD7B', '#FCC37C', '#FDC87D', '#FDCD7E', '#FDD37F', '#FED880', '#FEDD81', '#FEE382', '#FEE883', '#FCEB84', '#F6E984', '#F0E784', '#E9E583', '#E3E383', '#DCE182', '#D6E082', '#D0DE82', '#C9DC81', '#C3DA81', '#BDD881', '#B6D680', '#B0D580', '#AAD380', '#A3D17F', '#9DCF7F', '#96CD7E', '#90CB7E', '#8ACA7E', '#83C87D', '#7DC67D', '#77C47D', '#70C27C', '#6AC07C', '#63BE7B'])
    _, bins = np.histogram(maskedValues, 49)
    try:
        mapped = np.digitize(maskedValues, bins)
    except:
        mapped = int(0)
    nValues[mask] = colorMap[mapped - 1]
    nValues[~mask] = "#808080"
    return nValues.tolist()

Anyone have any ideas or has anyone done this before with VBA.


回答1:


The following function CalcColorScale will return a color given any two colors and the scale.The scale is the value of your current data relative to the range of data. e.g. if your data is from 0 to 200 then a data value 100 would be scale 50%(.5)

The image shows the result of scaling between red and blue

Public Sub Test()
    ' Sets cell A1 to background purple
    Sheet1.Range("A1").Interior.Color = CalcColorScale(rgbRed, rgbBlue, 0.5)
End Sub

' color1: The starting color as a long
' color2: The end color as a long
' dScale: This is the percentage in decimal of the color.
Public Function CalcColorScale(color1 As Long, color2 As Long, dScale As    Double) As Long

    ' Convert the colors to red, green, blue components
    Dim r1 As Long, g1 As Long, b1 As Long
    r1 = color1 Mod 256
    g1 = (color1 \ 256) Mod 256
    b1 = (color1 \ 256 \ 256) Mod 256

    Dim r2 As Long, g2 As Long, b2 As Long
    r2 = color2 Mod 256
    g2 = (color2 \ 256) Mod 256
    b2 = (color2 \ 256 \ 256) Mod 256

    CalcColorScale = RGB(CalcColorScaleRGB(r1, r2, dScale) _
                        , CalcColorScaleRGB(g1, g2, dScale) _
                        , CalcColorScaleRGB(b1, b2, dScale))
End Function

' Calculates the R,G or B for a color between two colors based the percentage between them
' e.g .5 would be halfway between the two colors
 Public Function CalcColorScaleRGB(color1 As Long, color2 As Long, dScale As Double) As Long
    If color2 < color1 Then
        CalcColorScaleRGB = color1 - (Abs(color1 - color2) * dScale)
    ElseIf color2 > color1 Then
        CalcColorScaleRGB = color1 + (Abs(color1 - color2) * dScale)
    Else
        CalcColorScaleRGB = color1
    End If
End Function



回答2:


You could always use the python script to generate the hex colors based of csv data and then simply read the csv file holding the generated hex colors and convert rgb then set the interiorcolor to that of the rgb outcome.

Sub HexExample()
    Dim i as Long
    Dim LastRow as Long
    LastRow = Range("A" & Rows.Count).End(xlUp).Row
    For i = 1 To LastRow
        Cells(i, "B").Interior.Color = HexConv(Cells(i, "A"))
    Next
End Sub

Public Function HexConv(ByVal HexColor As String) As String
    Dim Red As String
    Green As String
    Blue As String
    HexColor = Replace(HexColor, "#", "")
    Red = Val("&H" & Mid(HexColor, 1, 2))
    Green = Val("&H" & Mid(HexColor, 3, 2))
    Blue = Val("&H" & Mid(HexColor, 5, 2))

    HexConv = RGB(Red, Green, Blue)
End Function 



回答3:


Maybe this is what you are looking for:

Sub a()
    Dim vCM As Variant

    vCM = Array("F8696B", "FED880", "63BE7B") ' as many as you need
    ' Array's lower bound is 0 unless it is set to another value using Option Base
    ActiveCell.Interior.Color = Application.WorksheetFunction.Hex2Dec(CStr(vCM(2))) ' off-green in the active cell
End Sub

If you deside to forgo the Hex and use the color values then the above becomes this

Sub b()
    Dim vCM As Variant

    vCM = Array(16279915, 16701568, 6536827) ' as many as you need
    ' Array's lower bound is 0 unless it is set to another value using Option Base
    ActiveCell.Interior.Color = vCM(2) ' 6536827 setts an off-green in the active cell
End Sub

In case you do not know how to get the color values, here is the manual process:

  1. Apply an interior color to a cell. Make sure the cell is selected.

  2. In the VBE's Immediate window, execute ?ActiveCell.Interior.Color to get the color number for the interior color you've applied in Step 1.

Good luck.




回答4:


assuming:

values in A1:A40.

Sub M_snb()
 [a1:A40] = [if(A1:A40="",0,A1:A40)]

 sn = [index(rank(A1:A40,A1:A40),)]
 For j = 1 To UBound(sn)
   If Cells(j, 1) <> 0 Then Cells(j, 1).Interior.Color = RGB(Int(sn(j, 1) * 255 / 40), Abs(sn(j, 1) > UBound(sn) \ 2), 255 - Int((sn(j, 1) - 1) * (255 / 40)))
 Next

 [a1:A40] = [if(A1:A40=0,"",A1:A40)]
End Sub



回答5:


I've managed to find the correct answer, it's actually rather simple. All you have to do is add conditional formatting and then set the .Interior.Color to the same as what the .DisplayFormat.Interior.Color is and then delete the conditional formatting.

This will do exactly what is requested in the main post; and if you want to do it as a fallback then just don't delete the conditional formatting.

' Select Range
Range("A2:A8").Select

' Set Conditional
Selection.FormatConditions.AddColorScale ColorScaleType:=3
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
Selection.FormatConditions(1).ColorScaleCriteria(1).Type = xlConditionValueLowestValue
With Selection.FormatConditions(1).ColorScaleCriteria(1).FormatColor
    .Color = 7039480
    .TintAndShade = 0
End With
Selection.FormatConditions(1).ColorScaleCriteria(2).Type = xlConditionValuePercentile
Selection.FormatConditions(1).ColorScaleCriteria(2).Value = 50
With Selection.FormatConditions(1).ColorScaleCriteria(2).FormatColor
    .Color = 8711167
    .TintAndShade = 0
End With
Selection.FormatConditions(1).ColorScaleCriteria(3).Type = xlConditionValueHighestValue
With Selection.FormatConditions(1).ColorScaleCriteria(3).FormatColor
    .Color = 8109667
    .TintAndShade = 0
End With

' Set Static
For i = 1 To Selection.Cells.Count
    Selection.Cells(i).Interior.Color = Selection.Cells(i).DisplayFormat.Interior.Color
Next

' Delete Conditional
Selection.Cells.FormatConditions.Delete

Hopefully this helps someone in the future.




回答6:


The answers above should work. Still, the color is different that from Excel...

To recreate exact the same thing as Excel color formatting, and a little more straight forward in code:

rgb(cr,cg,cb)

color1: red - rgb(248,105,107)

color2:green - rgb(99,190,123)

color3: blue - rgb(255,235,132)

code:

Sub HeatMapOnNOTSorted()

Dim val_min, val_max, val_mid As Double
Dim cr, cg, cy As Double


Dim mysht As Worksheet
Dim TargetRgn As Range

Set mysht = Sheets("Sheet1")
Set TargetRgn = mysht.Range("c4:d9") '<-Change whatever range HERE

'get the min&max value of the range
val_min = Application.WorksheetFunction.Min(TargetRgn)
val_max = Application.WorksheetFunction.Max(TargetRgn)
val_mid = 0.5 * (val_min + val_max)


For Each rgn In TargetRgn

    ' three color map min-mid-max
    ' min -> mid: green(99,190,123)-> yellow(255,235,132)
        If rgn.Value <= val_mid Then
            cr = 99 + (255 - 99) * (rgn.Value - val_min) / (val_mid - val_min)
            cg = 190 + (235 - 190) * (rgn.Value - val_min) / (val_mid - val_min)
            cb = 123 + (132 - 123) * (rgn.Value - val_min) / (val_mid - val_min)
        Else
    ' mid->max: yellow(255,235,132) -> red(248,105,107)
            cr = 255 + (248 - 255) * (rgn.Value - val_mid) / (val_max - val_mid)
            cg = 235 + (105 - 235) * (rgn.Value - val_mid) / (val_max - val_mid)
            cb = 132 + (107 - 132) * (rgn.Value - val_mid) / (val_max - val_mid)

        End If
    rgn.Interior.Color = RGB(cr, cg, cb)


Next rgn

End Sub



来源:https://stackoverflow.com/questions/28217226/creating-a-color-scale-using-vba-avoiding-conditional-formatting

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!