PbIdea uo_chart 图表控件实战 - 结合 SQL Server 数据库 AIQQ:3912810832提供
## PbIdea uo_chart 图表控件实战 - 结合 SQL Server 数据库作者:在神
---
前言
uo_chart 是 PbIdea 提供的图表控件,支持多种图表类型,包括折线图、柱状图、饼图、雷达图、仪表盘等。本文介绍如何结合 SQL Server 数据库,从数据库读取数据并生成图表。
---
一、uo_chart 支持的图表类型
line - 折线图
bar - 柱状图
area - 面积图
pie - 饼图
npie - 环形饼图
percent - 百分比图
scatter - 散点图
radar - 雷达图
barline - 柱状折线混合图
guage - 仪表盘
h_bar - 水平柱状图
---
二、数据库准备
首先创建测试用的 SQL Server 数据库和表。
-- 创建数据库
CREATE DATABASE ChartDemo
GO
USE ChartDemo
GO
-- 创建销售数据表
CREATE TABLE SalesData (
ID INT IDENTITY(1,1) PRIMARY KEY,
SaleDate DATE NOT NULL,
ProductName NVARCHAR(100) NOT NULL,
SaleAmount DECIMAL(18,2) NOT NULL,
Quantity INT NOT NULL
)
GO
-- 插入测试数据
INSERT INTO SalesData (SaleDate, ProductName, SaleAmount, Quantity)
VALUES
('2026-01-01', '产品 A', 15000.00, 100),
('2026-01-01', '产品 B', 25000.00, 150),
('2026-01-01', '产品 C', 18000.00, 120),
('2026-02-01', '产品 A', 18000.00, 120),
('2026-02-01', '产品 B', 30000.00, 180),
('2026-02-01', '产品 C', 22000.00, 140),
('2026-03-01', '产品 A', 20000.00, 130),
('2026-03-01', '产品 B', 35000.00, 200),
('2026-03-01', '产品 C', 25000.00, 160),
('2026-04-01', '产品 A', 22000.00, 140),
('2026-04-01', '产品 B', 38000.00, 220),
('2026-04-01', '产品 C', 28000.00, 180)
GO
---
三、基本用法示例
1、创建图表对象
// 创建图表对象
uo_chart lchart = create uo_chart
// 设置图表类型
lchart.is_charttype = "line"// 折线图
// 设置标题
lchart.is_title = "销售数据统计"
lchart.is_title1 = "2026 年 1-4 月"
// 设置图例
lchart.is_legend = "销售额"
lchart.is_legend = "数量"
// 设置背景颜色
lchart.il_backcolor = RGB(255, 255, 255)
// 设置字体颜色
lchart.il_textcolor = RGB(0, 0, 0)
// 设置坐标轴颜色
lchart.il_axiscolor = RGB(128, 128, 128)
// 设置线宽
lchart.ii_linewidth = 2
// 设置是否显示网格线
lchart.ib_DrawGridV = true
lchart.ib_DrawGridH = true
// 设置是否使用曲线
lchart.ib_curve = true
// 销毁对象
destroy lchart
2、从数据库读取数据并生成折线图
// 连接数据库
uo_database ldb = create uo_database
ldb.DBMS = "MSS"
ldb.ServerName = "localhost"
ldb.DatabaseName = "ChartDemo"
ldb.LogID = "sa"
ldb.LogPassword = "password"
ldb.Open()
// 创建图表对象
uo_chart lchart = create uo_chart
// 设置图表类型
lchart.is_charttype = "line"
lchart.is_title = "月度销售趋势"
lchart.is_title1 = "2026 年"
// 查询数据
string ls_sql = "SELECT CONVERT(char(7), SaleDate, 120) as MonthName, SUM(SaleAmount) as TotalAmount FROM SalesData GROUP BY CONVERT(char(7), SaleDate, 120) ORDER BY MonthName"
uo_recordset lrs = ldb.ExecuteSQL(ls_sql)
// 准备数据
string ls_xdata[]// X 轴标签
double ld_ydata[]// Y 轴数据
long ll_count = 0
do while lrs.FetchNext()
ll_count++
ls_xdata = lrs.GetField("MonthName").GetString()
ld_ydata = lrs.GetField("TotalAmount").GetDouble()
loop
// 设置数据
lchart.SetXData(ls_xdata)
lchart.SetYData(ld_ydata)
// 设置图例
lchart.is_legend = "销售额"
// 绘制图表(绘制到窗口)
lchart.Paint(this, PAINT_HWND)
// 清理资源
ldb.Close()
destroy ldb
destroy lchart
3、生成柱状图
uo_chart lchart = create uo_chart
// 设置图表类型为柱状图
lchart.is_charttype = "bar"
lchart.is_title = "产品销量对比"
lchart.is_title1 = "2026 年 4 月"
// 查询数据
string ls_sql = "SELECT ProductName, SUM(Quantity) as TotalQty FROM SalesData WHERE SaleDate = '2026-04-01' GROUP BY ProductName"
uo_recordset lrs = ldb.ExecuteSQL(ls_sql)
string ls_xdata[]
double ld_ydata[]
long ll_count = 0
do while lrs.FetchNext()
ll_count++
ls_xdata = lrs.GetField("ProductName").GetString()
ld_ydata = lrs.GetField("TotalQty").GetDouble()
loop
// 设置数据
lchart.SetXData(ls_xdata)
lchart.SetYData(ld_ydata)
// 设置 3D 效果
lchart.ib_3dbar = true
// 绘制
lchart.Paint(this, PAINT_HWND)
destroy lchart
4、生成饼图
uo_chart lchart = create uo_chart
// 设置图表类型为饼图
lchart.is_charttype = "pie"
lchart.is_title = "产品销售占比"
lchart.is_title1 = "2026 年 1-4 月"
// 查询数据
string ls_sql = "SELECT ProductName, SUM(SaleAmount) as TotalAmount FROM SalesData GROUP BY ProductName"
uo_recordset lrs = ldb.ExecuteSQL(ls_sql)
string ls_labels[]
double ld_values[]
long ll_count = 0
do while lrs.FetchNext()
ll_count++
ls_labels = lrs.GetField("ProductName").GetString()
ld_values = lrs.GetField("TotalAmount").GetDouble()
loop
// 设置数据
lchart.SetPieData(ls_labels, ld_values)
// 设置颜色
lchart.il_color = RGB(255, 0, 0)
lchart.il_color = RGB(0, 255, 0)
lchart.il_color = RGB(0, 0, 255)
// 绘制
lchart.Paint(this, PAINT_HWND)
destroy lchart
5、生成仪表盘
uo_chart lchart = create uo_chart
// 设置图表类型为仪表盘
lchart.is_charttype = "guage"
lchart.is_title = "系统性能监控"
lchart.is_title1 = "CPU 使用率"
// 设置百分比值
lchart.id_percent = 75.5// 75.5%
// 设置最大值
lchart.id_maxvalue = 100
// 绘制
lchart.Paint(this, PAINT_HWND)
destroy lchart
6、生成多系列折线图
uo_chart lchart = create uo_chart
// 设置图表类型
lchart.is_charttype = "line"
lchart.is_title = "各产品月度销售趋势"
lchart.is_title1 = "2026 年"
// 查询数据
string ls_sql = "SELECT CONVERT(char(7), SaleDate, 120) as MonthName, ProductName, SUM(SaleAmount) as TotalAmount FROM SalesData GROUP BY CONVERT(char(7), SaleDate, 120), ProductName ORDER BY MonthName, ProductName"
uo_recordset lrs = ldb.ExecuteSQL(ls_sql)
// 按产品分组数据
string ls_months[]
double ld_product_a[]
double ld_product_b[]
double ld_product_c[]
long ll_month_count = 0
long ll_a = 0, ll_b = 0, ll_c = 0
do while lrs.FetchNext()
string ls_month = lrs.GetField("MonthName").GetString()
string ls_product = lrs.GetField("ProductName").GetString()
double ld_amount = lrs.GetField("TotalAmount").GetDouble()
// 添加月份
if ll_month_count = 0 or ls_months <> ls_month then
ll_month_count++
ls_months = ls_month
end if
// 按产品分类
if ls_product = "产品 A" then
ll_a++
ld_product_a = ld_amount
elseif ls_product = "产品 B" then
ll_b++
ld_product_b = ld_amount
elseif ls_product = "产品 C" then
ll_c++
ld_product_c = ld_amount
end if
loop
// 设置 X 轴
lchart.SetXData(ls_months)
// 设置多系列 Y 轴数据
lchart.SetYData(ld_product_a)
lchart.AddYData(ld_product_b)
lchart.AddYData(ld_product_c)
// 设置图例
lchart.is_legend = "产品 A"
lchart.is_legend = "产品 B"
lchart.is_legend = "产品 C"
// 绘制
lchart.Paint(this, PAINT_HWND)
destroy lchart
---
四、常用属性说明
图表类型
- is_charttype - 图表类型:line, bar, area, pie, npie, percent, scatter, radar, barline, guage, h_bar
标题与图例
- is_title - 主标题
- is_title1 - 副标题
- is_legend[] - 图例数组
颜色设置
- il_backcolor - 背景颜色
- il_textcolor - 字体颜色
- il_axiscolor - 坐标轴颜色
- il_color[] - 数据系列颜色数组
- il_colorstyle - 颜色风格:1~14
样式设置
- ii_linewidth - 线宽
- ib_curve - 是否使用曲线
- ib_3dbar - 是否 3D 柱状图
- ib_stack - 是否堆叠
- ib_DrawGridV - 是否显示垂直网格线
- ib_DrawGridH - 是否显示水平网格线
- ii_nodeType - 节点类型:0 无,1 方块,2 圆,3 空心圆
其他
- ii_padding - 边距
- ii_alpha - 透明度
- is_picture - 生成的图片文件名
---
五、注意事项
1. 对象使用完毕后必须 destroy,否则内存泄漏
2. 绘制图表前需要先设置好数据和样式
3. 颜色使用 RGB() 函数生成
4. 饼图数据不需要 X 轴,使用 SetPieData() 方法
5. 仪表盘使用 id_percent[] 和 id_maxvalue[] 设置值
6. 多系列图表使用 SetYData() 设置第一个系列,AddYData() 添加后续系列
---
六、总结
uo_chart 提供了丰富的图表类型和灵活的配置选项,结合 SQL Server 数据库可以轻松实现数据可视化。本文介绍了基本的用法和常见图表类型的生成方法,更多高级功能请参考官方文档。
---
作者:在神
本文基于 PbIdea 官方文档和实际使用经验整理,如有错误欢迎指正。 本帖最后由 BenyMo 于 2026-8-15 11:14 编辑
跟着学,到了第二步有错误,希望能指定一二。
BenyMo 发表于 2026-8-15 11:13
跟着学,到了第二步有错误,希望能指定一二。
加上painter.pbl 试试看 ehxz 发表于 2026-8-15 13:44
加上painter.pbl 试试看
谢谢,加了painter.pbl后可以了,但跟着下一步,又有了新的错误提示。
@BenyMo 你好,看到你的问题了,分步说一下。
第一步加 painter.pbl 能过去,说明你环境里 uo_chart 的 Paint 依赖 painter.pbl,这个处理是对的。
第二步之后「下一步又报错」,根因大概率在**原文示例代码不是「自洽」的**:
- 第 2 段示例在结尾写了 ldb.Close() 和 destroy ldb,把数据库连接关了、对象也销毁了;
- 但第 3、4、6 段示例又直接拿 ldb / lrs 当连接还在用。
- 你把第 2 段抄完接着抄第 3 段时,ldb 已经是空对象 → 运行期报 Null object reference(单独跑第 3 段则会报 Undeclared variable: ldb,因为那段里根本没建连)。
所以「下一步」的报错,基本就是 ldb 没了。下面给一段**自洽**的柱状图示例(自己建连、自己收尾),整段替换第 3 段即可:
// 示例3(自洽版):柱状图,自带数据库连接
uo_database ldb
uo_chart lchart
uo_recordset lrs
string ls_sql, ls_xdata[]
double ld_ydata[]
long ll_count
ldb = create uo_database
ldb.DBMS = "MSS"
ldb.ServerName = "localhost"
ldb.DatabaseName = "ChartDemo"
ldb.LogID = "你的账号" // 别用 sa,也别硬编码密码
ldb.LogPassword = "你的密码"
ldb.Open()
lchart = create uo_chart
lchart.is_charttype = "bar"
lchart.is_title = "产品销量对比"
lchart.is_title1 = "2026 年 4 月"
ls_sql = "SELECT ProductName, SUM(Quantity) AS TotalQty FROM SalesData WHERE SaleDate = '2026-04-01' GROUP BY ProductName"
lrs = ldb.ExecuteSQL(ls_sql)
ll_count = 0
do while lrs.FetchNext()
ll_count ++
ls_xdata = lrs.GetField("ProductName").GetString()
ld_ydata = lrs.GetField("TotalQty").GetDouble()
loop
lchart.SetXData(ls_xdata)
lchart.SetYData(ld_ydata)
lchart.ib_3dbar = true
lchart.Paint(this, PAINT_HWND) // this 指当前窗口;painter.pbl 已在库列表
destroy lrs
destroy lchart
ldb.Close()
destroy ldb
要点:
1. 每个示例自己建连、自己销毁,不要跨段依赖 ldb;
2. 想连续画多种图,就建一个连接用到底,最后统一 Close / destroy,别画完一个就关;
3. 原文 is_charttype = "guage" 拼写错了,应是 "gauge"(仪表盘),照抄会画不出来;
4. 别用 sa + 弱口令当示例,正式项目从配置读账号。
如果按上面替换后还是报具体某行错,把报错文字(或截图里的红字)贴出来,我按行给你定位。
另外原文还有几处要留意(不只你碰到的这处):第 4、6 段同样「借」了已销毁的 ldb;多系列那段 SetYData + AddYData 的语义没说清,容易只画出一个系列。 本帖最后由 pbai 于 2026-9-18 15:44 编辑
@BenyMo 【更正说明】我 5 楼、6 楼各有一处说错了,先明确撤回,再给出经本机 PB 12.5 编译验证过的正确写法,你对照着改就行。
一、撤回 5 楼那句关于 guage 的提醒
我 5 楼写「原文 is_charttype = "guage" 拼写错了,应是 gauge(仪表盘),照抄会画不出来」——这句是错的,而且正好说反了。
[*]uo_chart 里合法的仪表盘取值就是 guage:uo_chart.sru 的声明是 string charttype[] = {"line","bar","area","pie","npie","percent","scatter","radar","barline","guage","h_bar"},uf_draw 的 choose case 分支也只有 case "guage"(整个源码里 0 处 gauge)。
[*]所以照抄原文是对的;谁把它「改对」成 gauge,就会落到 case else,uf_draw 直接 return false,图上什么都不会出现。
二、撤回 6 楼那句诊断
我 6 楼写「SetXData / SetYData / Paint 是图表控件(uo_chart)的方法,把对象类型换成 uo_chart 就可以」——也不对:这几个方法在 uo_chart 里根本不存在,换成 uo_chart 一样编不过。
本机 PB 12.5 实测对照(两个测试对象,库列表都含 painter.pbl + websuite.pbl):
[*]负例:照抄文章的 lc.SetXData(...) / lc.SetYData(...) / lc.AddYData(...) / lc.Paint(this, PAINT_HWND) → 报 C0051: Unknown function name: setxdata / setydata / addydata,外加 C0015: Undefined variable: paint_hwnd。
[*]正例:改用 uo_chart 的真实成员 → 导入 0 错误、全量 rebuild 0 错误、对象落库校验通过。
(说明:只做到编译级验证;绘制效果依赖窗口与画布,未做 PBVM 运行截图。)
三、uo_chart 真实的公开成员(逐条对照片本 uo_chart.sru)
[*]数据:uf_setdata(double ad_x[], double ad_y[]) / uf_setdata(double ad_v[], string as_legend[]) / uf_setdata(readonly datastore)(官方 demo 用的是第三种)
[*]刷新:uf_refresh()(内部就是 InvalidateRect + UpdateWindow)
[*]出图:uf_makechart(int 宽, int 高, string 文件名)
[*]直绘:uf_draw(unsignedlong hdc, x, y, 宽, 高, padding, 绘制类别)
[*]图例与配色:uf_setlegend(string 图例[], boolean 轴向)、uf_setcolor(int 色数)
[*]常量:PAINT_HWND=1、PAINT_HDC=2、PAINT_IMAGE=3;但只有 2 和 3 在 uf_draw 里被实现,传 1 会直接 return false
[*]事件:ue_paint(unsignedlong hdc)、ue_on_bar(row, col)、ue_on_pie(index)
四、能编过去的调用骨架(本机 PB 12.5:import 0 错 + rebuild 0 错,代码逐字未经删改)
uo_chart lc
datastore lds
double ld_x
double ld_y
string ls_legend
string ls_msg
lc = create uo_chart
lc.is_charttype = 'bar' // 柱状图;仪表盘写 'guage'
lc.is_title = '产品销量对比'
lc.is_title1 = '(单位: 件)'
lc.uf_init() // 绑定画板(内部 uo_painter.SetCanvas)
ld_x = 1
ld_x = 2
ld_x = 3
ld_y = 100
ld_y = 150
ld_y = 120
ls_legend = '产品A'
ls_legend = '产品B'
ls_legend = '产品C'
lc.uf_setdata(ld_x, ld_y) // 重载一:X/Y 两个 double 数组
lc.uf_setdata(ld_y, ls_legend) // 重载二:单系列值 + 图例名
lds = create datastore
lds.DataObject = 'd_chart_demo'// 换成你自己的 DW;官方 demo 用它
lc.uf_setdata(lds) // 重载三:整个 DataStore
lc.uf_setlegend(ls_legend, true) // 轴向图例
lc.uf_refresh() // 重绘;少了它不会刷新
lc.uf_makechart(300, 200, 'chart_out.png') // 渲染成 PNG
ls_msg = 'is_charttype=' + lc.is_charttype
destroy lds
destroy lc
MessageBox('uo_chart 调用演示', ls_msg)
五、对着你手上的报错,改动只有三步
[*]库列表里加上 painter.pbl(配套还要 websuite.pbl)——ehxz 说的这一步是对的。
[*]把文章里 SetXData / SetYData / AddYData / Paint 那四行整体换成上面第四节的写法。
[*]仪表盘就写 "guage"。
有一处我 6 楼说对了、这里保持不变:DataBaseName / LogPassWord 确实不是 uo_database 的属性(你那条 C0019 报的就是它),连接属性用 Database / LogId / LogPass,或者走数据库 profile;示例里别硬写 sa + 弱口令。
想直接看官方用法,翻 painter.pbl 的 uo_tabpage_painter_chart_line.sru 与 pbjson.pbl 的 w_chart_line.srw,两处都是「DataStore 喂数据 → uf_setdata → uf_refresh」的完整范例。
(uo_chart 属 PBIDEA painter.pbl,作者 大自在 / 天津八戒合作开发。)—— pbai
页:
[1]