pbai 发表于 2026-8-19 07:30:47

PBIDEA:用 uo_chart 画出柱状图与饼图

PBIDEA:用 uo_chart 画出柱状图与饼图


阅读说明
1. 适用版本:PB 9 及以上(本文按 PB 12.5 + PBIDEA 1.x 验证;涉及 painter.pbl 与 websuite.pbl 两个库)
2. 支持数据库:无需数据库(示例全部使用内存 DataStore,不连接任何 DBMS)
3. 操作系统与环境要求:Windows 7+,已安装 PBIDEA 运行库(PbIdea.dll 等);应用库需包含 painter.pbl、websuite.pbl
4. 难度系数:★★☆☆☆
5. 其它阅读说明:前置知识为 DataStore 基本操作(InsertRow/SetItem);建议先看 Day 7《PBIDEA:uo_database 连接池与多库事务复用》了解 PBIDEA 库的引入方式


一、uo_chart 是什么

PBIDEA 的 painter.pbl 里放着一个自绘图表控件 uo_chart。它不依赖任何第三方 ActiveX/OCX,本质是一个 UserObject,通过 ue_paint 事件用 GDI 把图表直接画在窗口上,底层由 websuite.pbl 的 uo_painter(封装 PbIdea.dll)提供画板能力。

它内置 11 种图表类型,通过属性 is_charttype 切换:


is_charttype图表is_charttype图表
line折线图npie南丁格尔玫瑰图
bar柱状图percent百分比环形图
area面积图scatter散点图
pie饼图(可环形)radar雷达图
barline柱线混合guage仪表盘
h_bar横向柱状图


控件创建(constructor)时自动执行 uf_init():创建内部画板 uo_painter p、注册画布事件、并在程序目录建 chart 文件夹,不需要你手动初始化。

二、喂数据:uf_setdata(DataStore)

uo_chart 只有一个主要的数据入口——把 DataStore 传给它:调用 uo_chart_1.uf_setdata(ids) 返回 boolean,数据为空(RowCount < 1 或列数 < 1)时返回 false。

数据约定很关键,务必按这个结构准备 DataStore:


[*]第 1 列:每行的分组名(柱状图横轴上的组名、图例名);
[*]第 2 列起:数值系列。系列名取数据窗口里该列的列头文本,命名规则是"列名 + 下划线 t",例如列名 c1,列头文本控件就叫 c1_t(painter.pbl 自带的 d_chart_demo 就是这么命名的:c1_t 文本为"周一"、"c2_t"为"周二"……);
[*]数据为空(RowCount < 1 或列数 < 1)时返回 false。


柱状图多行多列 = 多组 × 多系列(自动堆叠);饼图一行多列 = 每个数值列一个扇区,扇区名取各列列头文本。

三、示例 1:窗口内画柱状图


前置:① 应用库列表已加入 painter.pbl、websuite.pbl;② 窗口 w_chart_demo 上放一个 UserObject 控件,命名为 uo_chart_1,对象类型选 uo_chart;③ 使用 painter.pbl 自带的数据窗口 d_chart_demo(列:legend + c1~c7,列头周一~周日)。
步骤:1) 打开窗口;2) 在按钮 cb_bar 的 clicked 事件贴入下方代码;3) 运行并点击按钮,即可看到两组 × 七个工作日的堆叠柱状图。



// ===== 示例输入:两周销售数据(第 1 列=分组名,第 2~8 列=周一~周日)=====
datastore ids
ids = create datastore
ids.DataObject = 'd_chart_demo'   // painter.pbl 自带示例数据窗口

// 第一组:本店
ids.InsertRow(0)
ids.SetItem(1, 'legend', '本店')
ids.SetItem(1, 'c1', 12)          // 周一
ids.SetItem(1, 'c2', 18)          // 周二
ids.SetItem(1, 'c3', 15)          // 周三
ids.SetItem(1, 'c4', 22)          // 周四
ids.SetItem(1, 'c5', 19)          // 周五
ids.SetItem(1, 'c6', 26)          // 周六
ids.SetItem(1, 'c7', 14)          // 周日
// 第二组:分店
ids.InsertRow(0)
ids.SetItem(2, 'legend', '分店')
ids.SetItem(2, 'c1', 9)
ids.SetItem(2, 'c2', 14)
ids.SetItem(2, 'c3', 12)
ids.SetItem(2, 'c4', 17)
ids.SetItem(2, 'c5', 16)
ids.SetItem(2, 'c6', 20)
ids.SetItem(2, 'c7', 11)

// ===== 配置图表外观 =====
uo_chart_1.is_charttype = 'bar'   // 柱状图
uo_chart_1.is_title = '一周销售额对比'   // 主标题
uo_chart_1.is_title1 = '(单位:千元)'    // 副标题
uo_chart_1.ib_show_dzz = false      // 关闭右下角水印
uo_chart_1.il_colorstyle = 3      // 色板 3(雅致),可选 1~14

// ===== 喂数据 + 重绘 =====
uo_chart_1.uf_setdata(ids)
uo_chart_1.uf_refresh()             // 立即重绘到窗口

destroy ids

MessageBox('完成', '柱状图已绘制:2 组 × 7 个工作日')


四、示例 2:饼图(一行多列)

饼图的数据结构和柱状图不同:一行数据,第 2 列起每个数值列对应一个扇区,扇区名取各列列头文本。这里仍借用 d_chart_demo 演示(扇区名会显示为"周一~周四",想显示产品名,把数据窗口对应列的标题文本改成产品名即可)。


前置:同示例 1。步骤:在按钮 cb_pie 的 clicked 事件贴入下方代码,运行后点击按钮。



// ===== 示例输入:四个产品的销量占比(一行,第 1 列占位,第 2~5 列为四个扇区值)=====
datastore ids
ids = create datastore
ids.DataObject = 'd_chart_demo'
ids.InsertRow(0)
ids.SetItem(1, 'legend', '产品')    // 占位,饼图用不到
ids.SetItem(1, 'c1', 45)            // 扇区 1(列头"周一")
ids.SetItem(1, 'c2', 25)            // 扇区 2
ids.SetItem(1, 'c3', 18)            // 扇区 3
ids.SetItem(1, 'c4', 12)            // 扇区 4
// c5~c7 留空即 0,不参与

// ===== 配置 =====
uo_chart_1.is_charttype = 'pie'   // 饼图
uo_chart_1.is_title = '产品销量占比'
uo_chart_1.ib_link = false          // false=实心饼;true=环形图
uo_chart_1.ib_show_dzz = false

// ===== 喂数据 + 重绘 =====
uo_chart_1.uf_setdata(ids)
uo_chart_1.uf_refresh()

destroy ids

MessageBox('完成', '饼图已绘制:4 个扇区')


五、示例 3:不依赖界面直接导出 PNG(报表出图)

做报表时经常要把图存成图片文件塞进导出文档里。uf_makechart() 可以脱离屏幕绘制:在内存画布上画完直接 SaveMemoryImage 存成文件,扩展名决定格式(png/jpg/bmp 均可)。调用前必须先 uf_setdata 喂过数据。


前置:同示例 1(窗口上需有 uo_chart_1 控件,因为对象实例化后才能创建内部画板)。步骤:在按钮 cb_export 的 clicked 事件贴入代码;先点"画柱状图"喂数据,再点本按钮。



integer li_rc
// ===== 示例输入:导出尺寸与文件路径 =====
integer li_w
integer li_h
string ls_file
li_w = 900
li_h = 520
ls_file = 'chart/sales_bar.png'   // 相对程序运行目录;chart 目录由 uf_init 自动创建

li_rc = uo_chart_1.uf_makechart(li_w, li_h, ls_file)
if li_rc = 0 and FileExists(ls_file) then
    MessageBox('导出成功', '图片已保存:' + ls_file)
else
    MessageBox('导出失败', '返回码:' + string(li_rc))
end if


六、可选:点击图表取数据

控件内置两个点击事件,可用来做"点击柱/扇区弹出明细"的交互:


事件参数含义
ue_on_barai_row, ai_column点击柱子:第几组、第几列
ue_on_pieai_index点击扇区:第几个扇区



前置:窗口上已有 uo_chart_1。步骤:在 uo_chart_1 的事件列表里找到 ue_on_bar,双击进入并写入下方代码。



// uo_chart_1 的 ue_on_bar 事件:点击柱子时触发
MessageBox('点击了柱子', '第 ' + string(ai_row) + ' 组,第 ' + string(ai_column) + ' 列')


七、常见坑


[*]系列名取不到:数据窗口的列头文本控件必须命名为"列名_t"(如 c1 配 c1_t),否则取列头失败会回退成列名显示;
[*]饼图画不出多个扇区:饼图要用"一行多列",扇区数 = 数值列数;用了多行数据会被最后一行覆盖;
[*]图是空的:uf_setdata 返回 false 说明 DataStore 无数据或列数不足,先检查 RowCount 和列数;
[*]改完数据不刷新:喂数据后必须调 uf_refresh() 才会重绘;
[*]scatter / percent / guage 特殊:散点图用数组版 uf_setdata(ad_x[], ad_y[]);percent/guage 可用 uf_setdata(ad_v[], as_legend[]),其余类型都走 DataStore 版;
[*]水印与配色:ib_show_dzz 关闭水印;il_colorstyle 1~14 切换色板;柱状图 ib_stack=false 可取消堆叠、ib_3dbar=true 可开 3D。


小结

uo_chart 的核心用法只有三步:准备 DataStore(注意列头命名)→ uf_setdata 喂数据 → uf_refresh 重绘;需要图片文件时再用 uf_makechart 导出。理解了"第 1 列分组名、第 2 列起数值系列"这个约定,柱状图、饼图、折线图、雷达图等 11 种图基本都能照葫芦画瓢。下一篇继续讲 PBIDEA 的办公组件:用 uo_xlsx 读写 Excel。
页: [1]
查看完整版本: PBIDEA:用 uo_chart 画出柱状图与饼图

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com