vst实例 创建VST
Posted Luo大哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vst实例 创建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完成,先欣赏下我们的执行结果吧。
如何使用 MFC 创建 VST 插件?
【中文标题】如何使用 MFC 创建 VST 插件?【英文标题】:How to create VST plugin using MFC? 【发布时间】:2014-11-16 06:50:17 【问题描述】:我已经有一个 MFC gui 独立程序。应该怎么做才能使它成为 VST 2.x 插件? (如果我使用 VSTGUI/win32/qt/etc 会有很多返工 - 或者是否可以/适合使用 VSTGUI?)
我应该为 VST 2.x gui 插件实现哪些 VST 接口(gui 和其他)?
【问题讨论】:
使用 WDL-OL 是个好方法吗?它没有资源编辑器。 【参考方案1】:您担心 VST 的 GUI,而实际上您应该担心其余代码的结构。 VST 2.x 为您提供一个框架的 HWND,您所要做的就是创建一个托管您的 GUI 的子窗口。 MFC,原始 WIN32 - 没关系。
但是,真正的“问题”在于 VST 2.x 界面的其余部分。您应该研究这个界面并了解它是如何工作的。然后,您将能够评估您的代码是否具有正确的结构,以便轻松地作为 VST 插件进行接口。
【讨论】:
这是我的第二个问题:应该实现哪些接口(gui 和其他)?【参考方案2】:您只需要将您的窗口代码从属于您提供的 HWND。最简单的方法是使用 SetParent 从属整个窗口,然后像在普通应用中一样实现 MFC。
但是,没有旋钮,也没有数字或模拟读数。即使使用 MFC,如果您想制作精美的 VST 界面,您也将滚动您自己的 UI 代码。
所以几乎值得自己处理 WM_XXXX 消息并自己进行窗口和绘图。
【讨论】:
以上是关于vst实例 创建VST的主要内容,如果未能解决你的问题,请参考以下文章