vst实例(2) 创建VST
前面我们知道,创建一个虚拟树,应该首先告知VST节点数据的大小(即nodedatasize),其实在创建树结构时,这一点并不是必须的,而是如果你需要让VST的每一个节点能指向一定的数据,从而在执行树的操作时,能用到这些数据,那么你最适合的方法是定义一个结构类型(record),然后让node.data指向这个record。
实际上,我们也可以不使用node的data属性,而使用node.index或level属性来指向数据(例如指向数组的数据),VST提供的原始例子程序minima就是这样的。但是,如果不适用指针指向数据的话,完全体现不出VST的优势,而且排序、图标、定制绘制单元格等等都将难以实现。
严格来说,创建VST只需要做三步:
(1)告知VST根节点的数量(rootnodecount)。
(2)告知VST子孙节点的数量(chilenodecount)。
(3)告知VST的单元格显示文本(CELLTEXT)。
如果需要让VST的node指针指向某些数据的话,可以在第三条前告诉NODE的数据指针。
一、创建
1、设置根节点数
我们的程序目标是创建一个二级树,所以创建VST就是告知根节点的数量以及每一个节点的子节点的数量,因此我们创建VST节点的第一步就是在适当的位置告诉程序rootnodecount的数量。
创建树的根节点的过程,我们通过写一个过程(procedure)来实现:
procedure TForm2.createvst;
begin
QRY1.Open('select * from codes where site_code in '+
'(select distinct fir_code from codes where single_runway_ad>-1 )'+
' and single_runway_ad=-1');
//firedac默认初始只读取50行数据,此函数强制让QRY1完全读取table数据
//如果不执行此行,则recordcount将最大只有50;
QRY1.FetchAll;
//设置根节点数为有机场的情报区数,因为根节点指向的是情报区。
vst.RootNodeCount:=qry1.RecordCount;
QRY1.Close;
end;
2、初始化节点
在设置根节点数后,程序接下来的操作是触发事件oninitnode,因此我们为oninitnode写代码如下:
procedure TForm2.vstInitNode(Sender: TBaseVirtualTree; ParentNode,
Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin
//GetNodeLevel函数用于返回当前节点所在的层级,从0开始,根节点的level是0
case Sender.GetNodeLevel(node) of
0:
begin
//设置根节点的节点高度为36;
Sender.NodeHeight[node]:=36;
//根节点的节点选框类型为ctTriStateCheckBox
sender.CheckType[node]:=ctTriStateCheckBox;
Sender.CheckState[node]:=TCheckState(1);
if QRY1.Active then//当QRY1处于关闭状态时,下面的读取数据过程可能会产生错误
// GetNodeData将取出节点指向的数据
//下面的代码将填充数据
with pcodes(Sender.GetNodeData(node))^ do
begin
icao:=QRY1.FieldByName('site_code').AsString;
names:=QRY1.FieldByName('name_loc').AsString;
CODETYPE:='FIR';
//在这里告诉VST,这个节点需要有子节点
//这行语句将触发事件oninitchildren
Sender.HasChildren[node]:=True;
//每增加一个节点会触发一次oninitnode,
//因此如果不降qry1指向的数据往下移的话,所有根节点的数据将是完全一样的。
QRY1.Next;
end;
end;
1:
begin
//设置子节点的nodeheight=32,比根节点略低。
sender.NodeHeight[Node]:=32;
//子节点的节点选框类型为ctCheckBox
sender.CheckType[node]:=ctCheckBox;
//在添加每一个子节点的时候也会触发一次oninitnode事件,下面的代码是为子节点填充数据
if not QRY2.Active then Exit; //这行语句也是为防错处理
with pcodes(Sender.GetNodeData(node))^ do
begin
icao:=QRY2.FieldByName('site_code').AsString;
names:=QRY2.FieldByName('name_loc').AsString;
IATA:=QRY2.FieldByName('py_code').AsString;
if QRY2.FieldByName('single_runway_ad').AsString.Trim='0' then
rwy_style:='多跑道'
else
rwy_style:='单跑道'; if QRY2.FieldByName('main_alt_ad').AsString.Trim='0' then
apt_type:='备用'
else
apt_type:='主用';
CODETYPE:='AIRPORT';
QRY2.Next;
end;
end;
end;
end;
3、设置子节点数
在设置rootnodecount时,会调用根节点数量次数的oninitnode事件,而在设置根节点数据时,我们有一行语句:Sender.HasChildren[node]:=True,每次遇到这个语句,程序会触发oninitchildren事件,我们在oninitchildren事件中打开QRY2,并设置子节点数。代码如下:
procedure TForm2.vstInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
var ChildCount: Cardinal);
begin
with pcodes(Sender.GetNodeData(node))^ do
begin
QRY2.open('select * from codes where fir_code=:FIR and single_runway_ad<>-1',[icao]);
QRY2.FetchAll;
end;
ChildCount:=QRY2.RecordCount;
end;
事实上,我们在oninitchildren事件中给VST赋予子节点的数量时,每个子节点的创建时也会调用一次oninitnode事件。
在执行oninitnode事件时,我们除了赋值外,通常设置节点状态、选择框类型等等也在这里实现。
综合上面的语句,我们可以得出VST的基础创建逻辑是:
告诉VST节点数(无论是根节点还是子节点)→每一个节点调用一次oninitnode事件。
4、另一种创建树结构的方法
实际上,我们可以重写上面的代码如下:
VAR ND:PVIRTUALNODE;
……
QRY1.First;
while not QRY1.Eof do
begin
nd:=vst.AddChild(nil);
with pcodes(vst.GetNodeData(nd))^ do
begin
//根节点的获取数据语句
qry2.Open('相关的SQL语句');
while not QRY2.Eof do
begin
with pcodes(vst.GetNodeData(vst.AddChild(nd)))^ do
begin
//获取机场数据的语句
end;
QRY2.Next;
end;
end;
QRY1.Next;
end;
上面的语句中我们使用了添加节点的函数:
function AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode;
功能是为父节点创建最后一个子节点,如果parent为nil,则创建根节点。Userdata通常可以省略。
需要注意的是,如果使用上面的语句,则需要取消oninitchildren事件的代码,并且如果在上面的语句中进行了赋值,还应该取消oninitnode事件中的相应设置。
那么,是不是觉得上面的语句更简单,逻辑更清晰呢?从逻辑上来讲确实如此,但是,从效率上来讲,使用addchild语句来创建和添加节点,则程序的执行效率会低很多,因为addchild语句的实际执行效果是:如果parent=nil,则执行的是rootnodecount:=rootnodecount+1;如果parent非空,则执行的是childcount:= ChildCount+1.
当然如果只是如此,效率并不会低多少,实际上,按照我们的原始的设定,则是一次性的获得数据后,在一次性的绘制VST,并显示VST,而addchild语句则是每次执行addchild后,直接执行全过程,最终的执行效率会低许多,机器较慢的情况下,甚至可能在数据量大的情况下出现抖动。
5、设置celltext
好了,言归正传,让我们继续创建VST。
我们创建VST实际上只剩下最后一项工作了——告诉每行每列应该显示什么数据,则需要在ongettext事件中写代码,此程序的代码如下:
procedure TForm2.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
with pcodes(Sender.GetNodeData(node))^ do
begin
case Column of
0:CellText:=icao;
1:CellText:=iata;
2:CellText:=CODETYPE;
3:CellText:=names;
4:CellText:=rwy_style;
5:CellText:=apt_type;
end;
end;
end;
至此,创建VST完成,先欣赏下我们的执行结果吧。

vst实例(2) 创建VST的更多相关文章
- JBPM4入门——6.流程实例的创建和执行
本博文只是简要对JBPM4进行介绍,如需更详细内容请自行google 链接: JBPM入门系列文章: JBPM4入门——1.jbpm简要介绍 JBPM4入门——2.在eclipse中安装绘制jbpm流 ...
- SpringMVC系列(十五)Spring MVC与Spring整合时实例被创建两次的解决方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的关系
一.Spring MVC与Spring整合时实例被创建两次的解决方案 1.问题产生的原因 Spring MVC的配置文件和Spring的配置文件里面都使用了扫描注解<context:compon ...
- 品Spring:真没想到,三十步才能完成一个bean实例的创建
在容器启动快完成时,会把所有的单例bean进行实例化,也可以叫做预先实例化. 这样做的好处之一是,可以及早地发现问题,及早的抛出异常,及早地解决掉. 本文就来看下整个的实例化过程.其实还是比较繁琐的. ...
- Spring源码浅析之bean实例的创建过程(一)
在之前的文章内容中,简单介绍了bean定义的加载过程,下面这篇的主要内容就是bean实例的创建过程. bean实例的创建方式 ApplicationContext context = new Clas ...
- Spring源码浅析之bean实例的创建过程(二)
在上一篇内容中,介绍了doGetBean方法的源码内容,知道了bean在创建的过程中,有三个范围,单例.多例.Scope,里面都使用到了createBean.下面本篇文章的主要内容,就是围绕creat ...
- Spring(3.2.3) - Beans(3): Bean 实例的创建方式
创建一个 Bean 实例对象的方法通常有如下方式: 调用构造器创建 Bean 实例 调用静态工厂方法创建 Bean 实例 调用实例工厂方法创建 Bean 实例 使用构造器创建 Bean 实例 XML ...
- Github实例教程-创建库、创建主页
以README文件为实例,具体介绍github的使用过程 请先下载git,然后配置下面内容: ( 我的系统是debian,其它版本号的UNIX/Linux有区别),windows的临时不清楚. (一) ...
- 在OpenStack虚拟机实例中创建swap分区的一种方法
测试组里一个同学负责MapR的搭建,MapR文档中建议每个节点上至少有24GB的swap分区,不知道MapR为啥会有这种反人类的建议……swap无非就是一块顺序读写的磁盘空间,莫非省着内存不用,用sw ...
- Vue01 Vue介绍、Vue使用、Vue实例的创建、数据绑定、Vue实例的生命周期、差值与表达式、指令与事件、语法糖
1 Vue介绍 1.1 官方介绍 vue是一个简单小巧的渐进式的技术栈,它提供了Web开发中常用的高级功能:视图和数据的解耦.组件的服用.路由.状态管理.虚拟DOM 说明:简单小巧 -> 压缩后 ...
- Java类的加载及实例的创建
java中class.forName()和classLoader都可用来对类进行加载.class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的sta ...
随机推荐
- 研发效能负责人/研发效能1号位 |DevOps负责人
想要做好业务,老板们除了要梳理好公司级别的业务目标,公司的组织架构,还要搭个有产出的班子,也就是找负责人.建团队,让组织架构充实起来.搭班子最重要的就是把负责人找到,就是团队1号位的人.本文主要讲团队 ...
- Day04笔记
01.explicit的作用(了解) class Maker { public: //explicit只能放在构造函数前面,构造函数只有一个参数或其他参数有默认值时 explicit Maker(in ...
- P7961 数列 题解
对模拟的过程不敏感,对范围的数字不敏感 手玩是发现规律的好方式 计数 dp 以及一众计数题是明显短板,需要加紧突破. 样例解释已经较为明显地提示了这道题的大致做法.对于计数题,有动归与组合数学两种方法 ...
- 自己动手从零写桌面操作系统GrapeOS系列教程——20.汇编语言读硬盘实战
学习操作系统原理最好的方法是自己写一个简单的操作系统. 本讲我们设计一个简单的读硬盘实验.通过一定的方法使硬盘第二个扇区的前3个字节依次为1.2.3,最后3个字节依次为3.2.1,中间的506个字节全 ...
- java网络编程--4 UDP
java网络编程--4 UDP 1.7.UDP 发短信:不用连接,但是需要知道对方的地址 主要包含两个类:DatagramPacket 和 DatagramSocket 发送消息 发送端: packa ...
- 在一张 24 GB 的消费级显卡上用 RLHF 微调 20B LLMs
我们很高兴正式发布 trl 与 peft 的集成,使任何人都可以更轻松地使用强化学习进行大型语言模型 (LLM) 微调!在这篇文章中,我们解释了为什么这是现有微调方法的有竞争力的替代方案. 请注意, ...
- 原型继承和 Class 继承
涉及面试题: 原型如何实现继承? Class 如何实现继承? Class 本质是什么? ⾸先先来讲下 class ,其实在 JS 中并不存在类, class 只是语法糖,本质还是函数. class P ...
- QML和QT
推荐一些学习qml教程 Qt官方的QML教程: https://doc.qt.io/qt-5/qtqml-index.html,这是一个由Qt官方提供的完整的QML教程,包含了所有基本知识和高级语法. ...
- 记一次 .NET 某传感器采集系统 线程爆高分析
一:背景 1. 讲故事 前段时间有位朋友微信找到我,说他的程序使用 hsl 库之后,采集 plc 时内存溢出,让我帮忙看一下怎么回事,哈哈,貌似是分析之旅中的第二次和 hsl 打交道,既然找到我,那就 ...
- 创建用户认证授权的 kubeconfig 文件
创建用户认证授权的 kubeconfig 文件 当我们安装好集群后,如果想要把 kubectl 命令交给用户使用,就不得不对用户的身份进行认证和对其权限做出限制. 下面以创建一个 cby 用户并将其绑 ...