Dash AgGrid是Plotly Dash生态中最常用的表格组件,它把ag-grid的强大能力带进了Python的世界。做数据看板的时候,光把数字摆出来往往不够,如果能让行的背景色随数值变化而渐变,用户一眼就能看出哪些行数值偏高、哪些偏低。本文围绕getRowStyle这个属性,讲清楚它的运行机制,并给出几个可以直接运行的渐变配色方案。

getRowStyle的工作机制
在dash-ag-grid中,getRowStyle对应ag-grid原生的rowClassRules之外的另一种行样式方案。它接收一个JavaScript函数字符串,这个函数会在每行渲染时被调用,参数是一个包含params上下文的对象,其中最常用的是params.data——也就是当前行对应的字典数据。函数需要返回一个JavaScript对象,键是CSS样式属性名(驼峰写法),值是颜色、字号等合法CSS值。
下面是最基础的用法,让偶数行和奇数行呈现不同底色:
rowStyle = JsCode("""
function(params) {
if (params.node.rowIndex % 2 === 0) {
return {'background': '#f5f7fa'};
}
return {'background': '#ffffff'};
}
""")
dag.AgGrid(
columnDefs=columnDefs,
rowData=rowData,
getRowStyle=rowStyle,
)注意这里必须使用dash_ag_grid.JsCode来包装函数字符串,否则Dash会把这段文字当成普通属性传过去而不会执行。这个细节是初学者最容易踩的坑:直接传一个Python函数是没有用的,Dash的数据序列化机制不支持把Python闭包发到前端。
基于数值区间实现行级渐变
真正的渐变效果需要把数值映射到色阶上。思路很简单:先确定数值列的最小值和最大值,把每个值归一化到0到1之间,再用这个比例在两个颜色之间做线性插值。为了让读者直观感受,下面演示一个按销售额从浅蓝渐变到深蓝的完整例子。
插值函数写在JsCode内部即可,这样整个计算都在浏览器端完成,行数据更新时无需重新跑Python回调:
from dash import Dash, html
import dash_ag_grid as dag
from dash_ag_grid import JsCode
import pandas as pd
df = pd.DataFrame({
'城市': ['杭州', '苏州', '南京', '合肥', '武汉', '成都'],
'销售额': [320, 150, 480, 90, 260, 410],
})
# 在前端做颜色插值,浅色代表低值,深色代表高值
gradient_style = JsCode("""
function(params) {
var values = [];
params.api.forEachNode(function(node) {
values.push(node.data['销售额']);
});
var minV = Math.min.apply(null, values);
var maxV = Math.max.apply(null, values);
var t = (params.data['销售额'] - minV) / (maxV - minV || 1);
// 从 (232, 244, 255) 线性插值到 (30, 90, 180)
var r = Math.round(232 + (30 - 232) * t);
var g = Math.round(244 + (90 - 244) * t);
var b = Math.round(255 + (180 - 255) * t);
return {
'background': 'rgb(' + r + ',' + g + ',' + b + ')',
'color': t > 0.7 ? '#ffffff' : '#000000'
};
}
""")
app = Dash(__name__)
app.layout = html.Div(dag.AgGrid(
columnDefs=[{'field': c} for c in df.columns],
rowData=df.to_dict('records'),
getRowStyle=gradient_style,
columnSize='sizeToFit',
))
if __name__ == '__main__':
app.run(debug=True)这个方案的优点是自适应性很强:当过滤或排序后数据集变化时,params.api.forEachNode拿到的是当前可见行,渐变的参考范围会随之收窄,视觉效果始终贴合屏幕上的数据。文字颜色在深色背景上自动切换成白色,避免了对比度不足的问题。缺点是每次渲染行都要遍历一遍所有节点,行数上万时会有明显开销,这种规模下建议改用预计算颜色的方案。
服务端预计算与前端渲染的性能对比
另一种做法是在Python端把颜色算好,直接作为一列写进rowData,然后getRowStyle只负责读取这列的值。这样前端函数退化为简单的查表操作:
def lerp_color(t, c1=(232, 244, 255), c2=(30, 90, 180)):
return 'rgb(%d,%d,%d)' % tuple(
round(a + (b - a) * t) for a, b in zip(c1, c2)
)
vmin, vmax = df['销售额'].min(), df['销售额'].max()
df['行颜色'] = df['销售额'].apply(
lambda x: lerp_color((x - vmin) / (vmax - vmin or 1))
)
simple_style = JsCode("""
function(params) {
return {'background': params.data['行颜色']};
}
""")两种方案各有适用场景。前端计算的优势在于过滤后渐变范围自动更新、逻辑集中、不污染数据列;服务端预计算的优势是渲染快、颜色可以缓存、配合导出功能时颜色列可以直接复用。如果你的看板数据量在一千行以内,两种方式体感没有差别;超过五千行或者在低端移动设备上运行,优先选预计算方案。
还有一点值得注意:getRowStyle返回的是内联样式,优先级高于rowClass定义的CSS类,所以如果同时用了条纹行样式,两者会互相覆盖,建议只保留一种来源,避免出现排查半天才发现样式被内联覆盖的情况。掌握了这些细节后,你还可以把渐变逻辑扩展到多列加权评分、同比变化率等更复杂的业务指标上,表格的可读性会有质的提升。
DashAgGridgetRowStyle修改时间:2026-09-07 00:46:28