简介:TEECHART 8.06完整源代码包面向使用Delphi、C++ Builder等IDE的桌面与跨平台开发者,解决高质量图表集成和自定义难题,尤其适合需要深度改写图表行为或制作中文报表的团队。压缩包内共2362个文件,以333个pas源文件、567个dcu编译单元、229个dpk与数十个bdsproj工程文件为主体,另有大量dfm/xfm界面定义、bmp图标和rc资源,能直接对照源码理解组件架构;整体体积仅11.49MB,便于快速获取和学习。包内附带专门的重新编译配置工具,并兼容Kylix等Linux环境,说明其对跨平台场景有专门考虑,决策立方体相关文件则为多维数据分析提供了可扩展起点。最实用的是PDF导出已做中文修正,避免报告乱码,且完整源码允许开发者自行裁剪功能、增强渲染性能或嵌入新图表类型。目前已有186人浏览学习,对想深入图表库内部机制并高效二次开发的程序员来说,是省去逆向成本的一手资料。
1. 定位:一套自带全部 Pascal 源码的 VCL 图表控件,适合把黑匣子拆开的人
TeeChart 8.06 FULL SOURCE 不是普通的组件安装包,而是把整个 VCL 图表库的 Pascal 单元全部摊开的一份源码资源。对 Delphi / C++Builder 开发者来说,这意味着从坐标轴换算、系列绘制到导出格式、设计期编辑器,每一层都能打断点、改逻辑、重新编译。它的定位更像一个“可改写的图表引擎”:线上出现坐标偏移这类诡异 bug 时,你不必等下一个版本,直接进源码查。它适合两类人:一类是在老系统里嵌入图表、追求稳定可控的业务开发;另一类是研究老牌控件架构、准备做图表二次开发的组件开发者。对新手来说,它还是一份很好的 VCL 源码教材。
2. 技术栈与包结构:一个源码包里装了三层东西
2.1 VCL 组件体系:运行期包与设计期包的分工
TeeChart 8.06 的技术栈一句话能说完:Object Pascal 编写,构建在 VCL 之上,用 GDI 做最终绘制,用 Delphi 的 DPK/BPL 包机制对外发布。那个年代的成熟组件库几乎都是“运行期包 + 设计期包”双线结构。运行期包装的是 TChart、TLineSeries、TAxis 这些真正参与绘图的类,编译进目标程序后不依赖 IDE;设计期包装的是属性编辑器、图表编辑器、系列类型向导,只存在于 Delphi 环境里。
FULL SOURCE 的意义在于两条线的源码都在。你可以把整个库装成 IDE 组件用,也可以只挑 Chart.pas、TeEngine.pas、Series.pas 几个单元直接静态编译进自己的工程。静态链接的好处是彻底绕开 BPL 版本冲突,发布时只带一个 exe;坏处是编译时间变长、包体积变大。我一般会优先静态编译,尤其在改源码做二次开发时,静态方式调试定位要干净得多。
包文件的命名在不同小版本里略有差异,但规律一致:带 Design 字样的是设计期包,不带的是运行期包。动手安装前先分清这两个文件,后面会省很多事。
2.2 目录结构速查:拿到压缩包先认路
常见布局下,源码包按用途分目录。我拆过手头这份 8.06 之后,按“进来先翻哪里”整理成下面这张表:
| 目录/文件(常见布局) | 装的是什么 | 什么时候用到它 |
|---|---|---|
| Source*.pas | 全部核心单元:Chart.pas、TeEngine.pas、Series.pas、TeeCanvas.pas、TeeProcs.pas、TeeTools.pas、TeeFunci.pas、TeeExport.pas 等 | 追源码、改逻辑、下断点 |
| Compiled<版本>\ | 预编译好的 DCU 与 BPL 产物,按 Delphi 版本分子目录 | 只装不改造时,让 IDE 快速编译 |
| Demos\ | 官方示例工程,按功能拆:All Features、数据库、导出、打印、3D | 验证安装、临摹用法 |
| Help\ | CHM 格式帮助文档 | 查属性、查事件 |
| Redist\ 与 Tools\ | 可再发行运行时、注册/卸载脚本 | 部署收尾、清理环境 |
装的时候一定先确认你用的编译器对应哪个子目录。8.06 那个年代发行包喜欢把 DCU 跟源码分开,Compiled 目录按 Delphi 版本分开放,路径指错是后面大量 “File not found” 的源头。这个习惯放到今天的开源控件包里依然适用。
2.3 功能边界:数据、绘制、输出三层
TeeChart 8.06 的功能可以粗分成三层。数据层是 TChartSeries 自己维护的 XValues / YValues,它们的数据类型是 TChartValues——一组按索引排列的浮点数组,配合 TeeFunci.pas 里的函数类做派生数据,比如求和、均值、移动平均、回归线。
绘制层以 TChart.Draw 为总入口,坐标轴换算、系列绘制、标记、图例都在 Chart.pas / TeEngine.pas / TeeCanvas.pas 里实现。输出层是 TeeExport.pas 统领的导出体系,位图、JPEG、GIF、PNG、EMF、EPS、PDF、HTML、Excel 各自是一个格式类,挂在统一的注册表里。另外还有 TeeDBChart.pas 的 TDBChart,把系列数据源接到 ADO / BDE 数据集上。
这四块边界很清楚:改导出格式不碰绘制层,改轴算法不碰数据层。二次开发时先判断需求落在哪一层,再决定动哪个单元,这是 8.06 源码给后来者留下的最好遗产。
3. 核心模块拆解:折线图从数据到像素的完整绘制链路
3.1 数据装载:AddXY 之后数据去了哪
一个折线图刚运行时只做两件事:往系列里塞数据,然后等重绘。塞数据的高层入口是 AddXY,底层落在 XValues / YValues 上。
var S: TLineSeries; begin S := TLineSeries.Create(Self); S.ParentChart := Chart1; S.AddXY(1, 12.5, '周一'); S.AddXY(2, 19.2, '周二'); S.AddXY(3, 8.7, '周三'); end;这里 S.ParentChart 把系列挂到 Chart1 上,是运行时创建系列最关键的一步;只 Create 不挂 ParentChart,数据加了一堆,图上一个点都不会出现。AddXY 的前两个参数是 X、Y 数值,第三个是点的标签,对应绘图时标记(Marks)里显示的文字。
每次 AddXY 之后,图表内部会把轴标记成“需要重算”,但不会立刻重新画,而是等 Windows 的 WM_PAINT 消息驱动重绘。这是老 VCL 控件的常见做法,性能上比每加一个点就重画一遍合理得多。若在循环里一次性灌上千个点,建议先调 Chart1.AutoRepaint := False,灌完再恢复并调用 Repaint,能明显减少闪烁。
3.2 轴换算:数据值怎么变成像素坐标
图表画任何系列之前,轴必须先做完两件事:算出 Min / Max,算出数据值到像素的映射。下面这段是我按 8.06 源码逻辑还原的最小版轴换算:
// TAxis 内部核心:把数据值映射到画布像素 function TAxis.CalcPos(const AValue: Double): Integer; begin if Horizontal then Result := IStartPos + Round((AValue - Min) * (IEndPos - IStartPos) / (Max - Min)) else // 垂直轴像素向下增长,所以要翻转 Result := IEndPos - Round((AValue - Min) * (IEndPos - IStartPos) / (Max - Min)); end;IStartPos 和 IEndPos 是轴内部的两个关键字段,在图表的布局阶段根据 Panel、Title、Legend 占位算好;Min / Max 来自轴自动范围,也可以由用户显式指定。Horizontal 为 True 表示横向轴(Bottom / Top),X 越大像素越靠右;垂直轴则因为屏幕坐标 Y 向下增长,计算时要反过来减,这就是很多坐标偏移 bug 的藏身之处——轴方向没区分,直接把横向公式套到纵向。
你还会在轴单元里看到 NiceStep、AdjustMinMax、RoundFirstLabel 这类方法,它们负责把 Min / Max 圆整成“好看”的刻度:0 到 19.2 会被圆整成 0 到 20,刻度步长取 1-2-5 的倍数。这是轴绘制里最容易被忽略、却最能影响图表观感的部分。
3.3 绘制分派:DrawValues 的多态才是灵魂
整套绘制是三级分发。最外层是 TCustomChart.DrawSeries,它遍历所有可见系列,逐个调用 Draw:
// 外层调度:按顺序绘制可见系列 for t := 0 to SeriesCount - 1 do if SeriesList[t].Visible then SeriesList[t].Draw;TChartSeries.Draw 是每个系列的公共入口,它把准备工作拆成三件套:
procedure TChartSeries.Draw; begin PrepareCanvas(ParentChart.Canvas); // 设置画笔、画刷、字体 DrawValues; // 虚方法,由子类决定画线还是画柱子 DrawMarks; // 数据点标签 end;DrawValues 是虚方法,TLineSeries 覆盖它画折线,TBarSeries 覆盖它画矩形,互不干扰。TLineSeries 的实现大致是:
procedure TLineSeries.DrawValues; var t: Integer; P: TPoint; begin for t := 0 to Count - 1 do begin P.X := CalcXPos(t); // 用水平轴换算像素 P.Y := CalcYPos(t); // 用垂直轴换算像素 if t = 0 then Canvas.MoveTo(P.X, P.Y) else Canvas.LineTo(P.X, P.Y); end; end;CalcXPos / CalcYPos 是系列层封装好的坐标换算入口,内部调用所属轴的 CalcPos。TBarSeries 里同样的 DrawValues 会画成 TopLeft 到底部坐标的矩形;在 3D 模式下又走另一套带深度的坐标。理解了这一级分派,二次开发时加一个新系列就变成“覆盖 DrawValues”一件事。
3.4 标记与图例:画完点之后的两层装饰
系列画完图形本体,紧接着画 Marks——每个数据点旁边的文字标签。DrawMarks 会遍历每个点,根据 Marks.CalloutLength 偏移量算出文字框位置,再画引线和文字。这里有个典型坑:标记文字有宽度,点靠右边界时标签会画出图表区,8.06 里需要手动控制 Marks.Clip 或调整轴范围留白。
图例则是在所有系列画完之后,由 Chart 统一绘制。图例框里每一行对应一个系列,左边是系列颜色的色块,右边是系列标题。TChartSeries.DrawLegend 里能看到它的逻辑:颜色取 Series.Color,文字取 Series.Title,色块大小取 Legend 的默认宽高。这个顺序决定了当你启用了“图例自动显示”时,即使系列还没画完整,图例也能拿到颜色和标题。
4. 跑通流程:从源码包到 IDE 组件面板的一次性落地
4.1 环境准备:先确认编译器和包版本对得上
8.06 是 Ansi 时代的产物,用 Delphi 2007 及以前的版本编译最顺。如果你手头只有新版 Delphi,先检查包里 Compiled 目录提供了哪些子目录,再决定能否直接用 DCU,还是干脆把 Source 目录整个加进工程里重新编译——后者兼容性更好,只是首编会慢。
另外一个容易被忽略的点:解压路径不要带中文和空格。老版本编译器对路径编码敏感,D:\图表控件\TeeChart 8.06这种路径在 BPL 生成和 IDE 搜索路径解析时容易出幺蛾子,放在D:\Libs\TeeChart8这类纯英文路径下一劳永逸。动手前也先确认系统里没装其他版本的 TeeChart,设计期包冲突是 IDE 崩溃的高发原因。
4.2 安装步骤:编译运行期包,注册设计期包
安装就那么三步,但顺序不能乱:
第一步,设置 Library Path。打开 Delphi 的 Tools | Environment Options | Library,把Source目录加进 Library Path,再把 Compiled 目录对应的版本子目录也加进去。这一步行不行直接决定编译期报不报 File not found。
第二步,编译运行期包。在包里找到类似TeeChart Pro 8.06 VCL.dpk的文件(具体名以你手里这份为准),用 Delphi 打开后右键 Compile。编译通过说明源码和当前编译器兼容;如果报错,优先怀疑 4.1 的版本匹配问题。
第三步,安装设计期包。找带 Design 字样的包文件,同样打开,这次右键选 Install。安装成功后会提示组件注册到了哪个页签,常见的是 “TeeChart 8” 之类的页签。看到这个提示,组件层面的安装就算完成了。
装完先别急着写自己的工程,直接编译 Demos 里的 All Features 示例。它能跑通,说明运行期链接、设计期注册、资源文件全部到位,这是最快的全链路验证。
4.3 最小验证工程:一个 Chart 三条数据
官方 demo 跑通之后,我习惯再建一个空工程做最小验证——只放一个 TChart、一个 TButton,确认运行时动态创建系列没问题:
procedure TForm1.Button1Click(Sender: TObject); var S: TLineSeries; begin S := TLineSeries.Create(Self); // Owner 是 Form,由 Form 统一释放 S.ParentChart := Chart1; S.AddXY(1, 12.5, '周一'); S.AddXY(2, 19.2, '周二'); S.AddXY(3, 8.7, '周三'); end;点击按钮后,图上应该出现一条带三个数据点的折线,底部横向轴显示周一 / 周二 / 周三标签,左侧纵向轴自动给出一个“好看”的范围,比如 0 到 20。如果折线出来了、轴范围也自动调整了,说明数据装载、轴换算、系列绘制、Marks 标签四段链路都是通的。这个最小工程后面做二次开发时还能继续当试验台用。
5. 避坑清单:装 8.06 全源码最容易翻车的五个场景
5.1 一编译就报 File not found: TeeConst.dcu
现象:打开任意 demo 工程点编译,报错 “File not found: TeeConst.dcu” 或类似单元缺失。
原因:Delphi 的 Library Path 里没有 Source 目录,或者指向了 Compiled 目录里错误的版本子目录。8.06 的发行包按 Delphi 版本分开存放编译产物,指错一个字母都会让 IDE 找不到单元。
解决:在 Tools | Environment Options | Library 里同时加Source和对应版本子目录两个路径;加完先重启 IDE 再编译。如果还报错,直接看报错单元在哪个子目录,把那个子目录优先加进去。
5.2 运行期包和静态 DCU 打架:重复定义
现象:编译通过,链接时报 “Duplicate unit” 或资源重复定义之类的错误,有时还伴随 BPL 加载失败。
原因:同一套 TeeChart 单元既通过 Library Path 被静态编译进工程,又在 Project Options 里勾选了运行时包引用,两边各来一份,链接器自然要抗议。
解决:二选一并保持全局一致。要做二次开发、想改源码,就全静态:Library Path 指向 DCU,不勾运行时包;只想快速用,就全动态:勾运行时包,别把 Source 目录加进搜索路径。最忌讳的是今天静态明天动态,折腾一整天最后发现是配置不一致。
5.3 中文标签全变方块
现象:图表的标题、轴的标签、数据点的 Marks 里凡是中文,运行起来全是方块或问号。
原因:8.06 是 Ansi 时代的代码,默认字体对中文字符集支持不好,加上老 Delphi 里字符集设置不对,中文按 GB2312 编码却被系统当成西欧字符渲染。
解决:把图表字体显式设为中文字体,比如Chart1.Font.Name := 'SimSun',同时设置Chart1.Font.Charset := GB2312_CHARSET。系列标题、Marks 字体也各自设一遍,因为 8.06 里字体是分层继承的,只改 Chart 字体不一定能覆盖到 Marks 和轴标签。
5.4 程序启动时画图报 Canvas 不允许绘制
现象:在 FormCreate 里主动调 Chart1.Draw 或给系列赋值后立即导出图片,抛 “Canvas does not allow drawing” 异常,或者画出来是空的。
原因:窗口句柄还没创建,Canvas 没有可用的设备上下文。TeeChart 的绘制强依赖窗口 DC,窗口没显示前强行画,等于让画家对着空气作画。
解决:别在 FormCreate 里主动画。正确的做法是把数据装载放在 FormCreate,把导出或截图逻辑放到窗体的 OnActivate、OnResize 或 OnAfterDraw 事件里,等窗口可见后再触发一次 Repaint 或导出。如果你确实需要在窗口创建前拿到图表图片,用Chart1.Bitmap相关方法在内存画布上生成,而不是直接依赖窗口 DC。
5.5 双击 TChart 打开图表编辑器时 IDE 崩溃
现象:安装完成后,往 Form 上拖一个 TChart,双击想开图表编辑器,IDE 直接报错或闪退。
原因:最常见的是设计期包没装上,或者机器上残留了旧版本 TeeChart 的设计期包,两个包同时注册,IDE 加载设计器时冲突。
解决:确认安装的是与你编译器和运行期包同版本的设计期包;然后在组件面板里检查是否残留多个 TeeChart 页签,有就先把旧包卸载干净。IDE 级别的崩溃往往不是代码问题,而是环境脏了,重装一次设计期包通常能解决。
6. 二次开发:给这个老控件加一种自己的系列
6.1 继承覆盖:三十行做一个阶梯图系列
8.06 把绘制分派做得足够开放,扩展一个系列只需要继承现有系列并覆盖 DrawValues。比如要做 K 线场景里常见的阶梯折线,继承 TLineSeries 就够了:
type TStepLineSeries = class(TLineSeries) private FPrevPoint: TPoint; protected procedure DrawValues; override; end; procedure TStepLineSeries.DrawValues; var t: Integer; P: TPoint; begin for t := 0 to Count - 1 do begin P.X := CalcXPos(t); P.Y := CalcYPos(t); if t > 0 then begin Canvas.LineTo(P.X, FPrevPoint.Y); // 先横向走,再纵向走 Canvas.LineTo(P.X, P.Y); end else Canvas.MoveTo(P.X, P.Y); FPrevPoint := P; end; end;这里唯一需要注意的坑是 FPrevPoint 的更新时机:它必须在每次循环末尾记录当前点,而不是在画完最后一条线之后统一处理,否则相邻线段接不上。工程量就这么点——轴换算、数据管理、图例颜色全部从父类继承,算上类型声明也不到 30 行。
6.2 注册与验证:从“能用”到“可交付”
光覆盖 DrawValues 只够在运行时用,要让新系列出现在 IDE 组件面板,得补一个 Register 过程:
procedure Register; begin RegisterComponents('MyChart', [TStepLineSeries]); end;这段代码要放进设计期包工程里重新编译安装,运行期工程只需要引用系列单元。验证套路还是老三步:先在最小试验台上生成 5 个点确认阶梯形状正确,再检查缩放和滚动后曲线的绘制是否完整,最后跑一遍带中文标签的场景确认字体没退化。全部通过,再把系列单元抽出来单独放一个目录,方便别的工程复用。
从那以后,我每次拿到这类老控件源码包,都会强制自己先走一遍“官方 demo 跑通 → 最小工程复现 → 再动手改”的流程,看起来多花半天,实际上省掉的是以天计的调试时间。希望帮到你。
本文还有配套的精品资源,点击获取