18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩

18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请问你是怎么调度的咩
重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30478251

Cocos2d 的一大特色就是提供了事件驱动的游戏框架,
引擎会在合适的时候调用事件处理函数,我们只需要在函数中添加对各种游戏事件的处理,
就可以完成一个完整的游戏了。
例如,为了实现游戏的动态变化,Cocos2d 提供了两种定时器事件;
为了响应用户输入,Cocos2d 提供了触摸事件和传感器事件;
此外,Cocos2d 还提供了一系列控制程序生命周期的事件。

Cocos2d 的调度原理管理着所有的事件,Cocos2d 已经为我们隐藏了游戏主循环的实现。

首先来看看游戏实现的原理:
游戏乃至图形界面的本质是不断地绘图,然而绘图并不是随意的,任何游戏都需要遵循一定的规则来呈现出来,这些规则就体现为游戏逻辑。游戏逻辑会控制游戏内容,使其根据用户输入和时间流逝而改变。
因此,游戏可以抽象为不断地重复以下动作:
处理用户输入 ;
处理定时事件 ;
绘图 ;

游戏主循环就是这样的一个循环,它会反复执行以上动作,保持游戏进行下去,直到玩家退出游戏。
在 Cocos2d-x 3.0 中,以上的动作包含在 Director 的某个方法之中,而引擎会根据不同的平台设法使系统不断地调用这个方法,从而完成了游戏主循环。

在cocos2d-x 3.0中,Director 包含一个管理引擎逻辑的方法,它就是 Director::mainLoop()方法,
这个方法负责调用定时器,绘图,发送全局通知,并处理内存回收池。
该方法按帧调用, 每帧调用一次,而帧间间隔取决于两个因素,一个是预设的帧率,默认为 60 帧每秒;
另一个是每帧的计算量大小。
当逻辑 处理与绘图计算量过大时,设备无法完成每秒 60 次绘制,此时帧率就会降低。 

mainLoop()方法会被定时调用,然而在不同的平台下它的调用者不同。
通常 Application 类负责处理平台相关的任务,其中就包含了对 mainLoop()的调用;
不同的平台具体实现也不相同,具体可参考cocos\2d\platform目录;

mainLoop()方法是定义在 Director 中的抽象方法,它的实现位于同一个文件中的 DisplayLinkDirector类中;
virtual void mainLoop() = 0;
具体实现是:
void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (! _invalid)
    {
        drawScene();
     
        // release the objects
        //释放资源对象
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}
上述代码主要包含如下 3 个步骤。
1、判断是否需要释放 Director,如果需要,则删除 Director 占用的资源。通常,游戏结束时才会执行这个步骤。
2、调用 drawScene()方法,绘制当前场景并进行其他必要的处理。
3、弹出自动回收池,使得这一帧被放入自动回收池的对象全部释放。

mainLoop()把内存管理以外的操作都交给了 drawScene()方法,因此关键的步骤都在 drawScene()方法之中;
再来看看drawScene方法:
void Director::drawScene()
{
    // calculate "global" dt
   //计算全局帧间时间差 dt
    calculateDeltaTime();
    
    // skip one flame when _deltaTime equal to zero.
    if(_deltaTime < FLT_EPSILON)
    {
        return;
    }

    if (_openGLView)
    {
        _openGLView->pollInputEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
    if (_nextScene)
    {
        setNextScene();
    }

    kmGLPushMatrix();

    // global identity matrix is needed... come on kazmath!
    kmMat4 identity;
    kmMat4Identity(&identity);

    // draw the scene
   //绘制场景
    if (_runningScene)
    {
        _runningScene->visit(_renderer, identity, false);
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
   //处理通知节点
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, identity, false);
    }

    if (_displayStats)
    {
        showStats();
    }

    _renderer->render();
    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    kmGLPopMatrix();

    _totalFrames++;

    // swap buffers
   //交换缓冲区
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}
可以分析出:
在主循环中,我们主要进行了以下 3 个操作。
1、调用了定时调度器的 update 方法,引发定时器事件。
2、如果场景需要被切换,则调用 setNextStage 方法,在显示场景前切换场景。
3、调用当前场景的 visit 方法,绘制当前场景。

在游戏主循环 drawScene 方法中,我们可以看到每一帧引擎都会调用 _scheduler的 update 方法。
【Scheduler *_scheduler;】_scheduler 是 Scheduler 类型的对象,是一个定时调度器。
所谓定时调度器,就是一个管理所有节点定时器的对象, 
它负责记录定时器,并在合适的时间触发定时事件。

再来分析一下定时器的情况:
Cocos2d-x 提供了两种定时器,分别是:
update 定时器,每一帧都被触发,使用 scheduleUpdate 方法来启用;
schedule 定时器,可以设置触发的间隔,使用 schedule 方法来启用。
看下Node中的实现:
void Node::scheduleUpdateWithPriority(int priority)
{
    _scheduler->scheduleUpdate(this, priority, !_running);
}

void Node::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)
{
    CCASSERT( selector, "Argument must be non-nil");
    CCASSERT( interval >=0, "Argument must be positive");

    _scheduler->schedule(selector, this, interval , repeat, delay, !_running);
}
其中 _scheduler是 Scheduler 对象。
可以看到,这两个方法的内部除去检查参数是否合法,只是调用了 Scheduler提供的方法。
换句话说,Node 提供的定时器只是对 Scheduler 的包装而已。
不仅这两个方法如此,其他定时器相关的方法也都是这样。

Scheduler的分析
经过上面的分析,我们已经知道 Node 提供的定时器不是由它本身而是由 Scheduler管理的。
因此,我们把注意力转移到定时调度器上。
显而易见,定时调度器应该对每一个节点维护一个定时器列表,在恰当的时候就会触发其定时事件。

Scheduler的主要成员请查看:cocos\2d\CCScheduler.h

为了注册一个定时器,开发者只要调用调度器提供的方法即可。
同时调度器还提供了一系列对定时器的控制接口,例如暂停和恢复定时器。
在调度器内部维护了多个容器,用于记录每个节点注册的定时器;
同时,调度器会接受其他组件(通常 与平台相关)的定时调用,随着系统时间的改变驱动调度器。 

调度器可以随时增删或修改被注册的定时器。
具体来看,调度器将 update 定时器与普通定时器分别处理:
当某个节点注册 update 定时器时,调度器就会把节点添加到 Updates 容器中,
即struct _hashUpdateEntry *_hashForUpdates里面;
为了提高调度器效率,Cocos2d-x 使用了散列表与链表结合的方式来保存定时器信息;
当某个节点注册普通定时器时,调度器会把回调函数和其他信息保存到 Selectors 散列表中,
即struct _hashSelectorEntry *_hashForTimers里面。

在游戏主循环中,我们已经见到了 update 方法。
可以看到,游戏主循环会不停地调用 update 方法。
该方法包含一个实型参数,表示两次调用的时间间隔。
在该方法中,引擎会利用两次调用的间隔来计算何时触发定时器。
我们再来分析下 update 方法的工作流程
// main loop
void Scheduler::update(float dt)
{
    _updateHashLocked = true;

    //a.预处理
    if (_timeScale != 1.0f)
    {
        dt *= _timeScale;
    }

    //
    // Selector callbacks
    //

    // Iterate over all the Updates' selectors
    //b.枚举所有的 update 定时器
    tListEntry *entry, *tmp;

    // updates with priority < 0
    //优先级小于 0 的定时器
    DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
    {
        if ((! entry->paused) && (! entry->markedForDeletion))
        {
            entry->callback(dt);
        }
    }

    // updates with priority == 0
    //优先级等于 0 的定时器
    DL_FOREACH_SAFE(_updates0List, entry, tmp)
    {
        if ((! entry->paused) && (! entry->markedForDeletion))
        {
            entry->callback(dt);
        }
    }

    // updates with priority > 0
    //优先级大于 0 的定时器
    DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
    {
        if ((! entry->paused) && (! entry->markedForDeletion))
        {
            entry->callback(dt);
        }
    }

    // Iterate over all the custom selectors
    //c.枚举所有的普通定时器
    for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )
    {
        _currentTarget = elt;
        _currentTargetSalvaged = false;

        if (! _currentTarget->paused)
        {
            // The 'timers' array may change while inside this loop
            //枚举此节点中的所有定时器
           //timers 数组可能在循环中改变,因此在此处需要小心处理
            for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
            {
                elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);
                elt->currentTimerSalvaged = false;

                elt->currentTimer->update(dt);

                if (elt->currentTimerSalvaged)
                {
                    // The currentTimer told the remove itself. To prevent the timer from
                    // accidentally deallocating itself before finishing its step, we retained
                    // it. Now that step is done, it's safe to release it.
                    elt->currentTimer->release();
                }

                elt->currentTimer = nullptr;
            }
        }

        // elt, at this moment, is still valid
        // so it is safe to ask this here (issue #490)
        elt = (tHashTimerEntry *)elt->hh.next;

        // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
        if (_currentTargetSalvaged && _currentTarget->timers->num == 0)
        {
            removeHashElement(_currentTarget);
        }
    }

    // delete all updates that are marked for deletion
    // updates with priority < 0
    //d.清理所有被标记了删除记号的 update 方法
   //优先级小于 0 的定时器
    DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
    {
        if (entry->markedForDeletion)
        {
            this->removeUpdateFromHash(entry);
        }
    }

    // updates with priority == 0
    //优先级等于 0 的定时器
    DL_FOREACH_SAFE(_updates0List, entry, tmp)
    {
        if (entry->markedForDeletion)
        {
            this->removeUpdateFromHash(entry);
        }
    }

    // updates with priority > 0
    //优先级大于 0 的定时器
    DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
    {
        if (entry->markedForDeletion)
        {
            this->removeUpdateFromHash(entry);
        }
    }

    _updateHashLocked = false;
    _currentTarget = nullptr;

#if CC_ENABLE_SCRIPT_BINDING
    //
    // Script callbacks
    //

    // Iterate over all the script callbacks
    //e.处理脚本引擎相关的事件
    if (!_scriptHandlerEntries.empty())
    {
        for (auto i = _scriptHandlerEntries.size() - 1; i >= 0; i--)
        {
            SchedulerScriptHandlerEntry* eachEntry = _scriptHandlerEntries.at(i);
            if (eachEntry->isMarkedForDeletion())
            {
                _scriptHandlerEntries.erase(i);
            }
            else if (!eachEntry->isPaused())
            {
                eachEntry->getTimer()->update(dt);
            }
        }
    }
#endif
    //
    // Functions allocated from another thread
    //

    // Testing size is faster than locking / unlocking.
    // And almost never there will be functions scheduled to be called.
    if( !_functionsToPerform.empty() ) {
        _performMutex.lock();
        // fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
        auto temp = _functionsToPerform;
        _functionsToPerform.clear();
        _performMutex.unlock();
        for( const auto &function : temp ) {
            function();
        }
        
    }
}
借助注释,能够看出 update 方法的流程大致如下所示。
1、参数 dt 乘以一个缩放系数,以改变游戏全局的速度,其中缩放系数可以由 Scheduler的TimeScale属性设置。
2、分别枚举优先级小于 0、等于 0、大于 0 的 update 定时器。
如果定时器没有暂停,也没有被标记为即将删除,则触发定时器。
3、枚举所有注册过普通定时器的节点,再枚举该节点的定时器,调用定时器的更新方法,从而决定是否触发该定时器。
4、再次枚举优先级小于 0、等于 0、大于 0 的 update 定时器,移除前几个步骤中被标记了删除记号的定时器。
我们暂不关心脚本引擎相关的处理。 

对于 update 定时器来说,每一节点只可能注册一个定时器,
因此调度器中存储定时器数据的结构体tListEntry *entry主要保存了注册者与优先级。
对于普通定时器来说,每一个节点可以注册多个定时器,
引擎使用回调函数(选择器)来区分同一节点 导出Excel2 - 项目分解篇 - xiaobin_HLJ80的专栏 - 博客频道 - CSDN.NET
有奖征资源,博文分享有内涵          5月推荐博文汇总          大数据读书汇--获奖名单公布          2014 CSDN博文大赛

导出Excel2 - 项目分解篇

分类: Delphi/C# 110人阅读 评论(0) 收藏 举报


        接上一篇《导出Excel1 - 项目分解篇》,介绍3个子过程。


      1. treeADO

          说明:把数据表字段转化为Tree结构         

 

         声明:procedure treeADO(adoDataSetX:TADODataSet;tvTemplate1:TTreeView);


         实现:

procedure treeADO(adoDataSetX:TADODataSet;tvTemplate1:TTreeView);
var
  i,j,k,n,pre_p,a_p:integer;
  s,pre_s,tepstr: string;
//  aft_s  cur_s prn_s,
  //
  exportMemo:TStringList;
begin
  //create mylist
  exportMemo:=TStringList.Create;
  //

  pre_s:='';
  for i:=0 to adoDataSetX.FieldDefList.Count-1 do
  begin
    s:= adoDataSetX.FieldDefList[i].Name;
    if pos('_',s)=0 then
    begin
      exportMemo.Add(s);
    end
    else
    begin
      pre_p:=0;
      a_p:=0;
      n:=0;
      for j:=1 to length(s) do
      if s[j]='_' then
      begin
         a_p:=j;
         tepstr:='';
         for k:=1 to n do
         begin
           tepstr:=tepstr+#9;
         end;
         if (copy(s,pre_p+1,a_p-pre_p-1)<>  copy(pre_s,pre_p+1,a_p-pre_p-1)) then
         begin
           exportMemo.Add(tepstr+copy(s,pre_p+1,a_p-pre_p-1));
         end;
         pre_p:= a_p;
         n:=n+1;
      end;
      exportMemo.Add(tepstr+#9+copy(s,a_p+1,length(s)-pre_p));
    end;
    pre_s:= s;
  end;
  exportMemo.SaveToFile(findSysTempDir+'temp.txt');
  TVtemplate1.LoadFromFile(findSysTempDir+'temp.txt');
  DeleteFile(findSysTempDir+'temp.txt');
  //free
  FreeAndNil(exportMemo);
end;

        2. WriteheaderADO

            说明:把Tree结构的数据,输出到Excel中。           


            声明:procedure WriteheaderADO(adoDataSetX:TADODataSet;titlehead:string;
                                        ColWidthInt:Integer;tvTemplate3:TTreeView);


            实现:

procedure WriteheaderADO(adoDataSetX:TADODataSet;titlehead:string;
                                        ColWidthInt:Integer;tvTemplate3:TTreeView);
var
  colnum,i,titlnum,Depnum:integer;
  colname,temcaption:string;
  colmix,colmax:string;
  treeno,treecount,colnums,treenotemp:integer;
  childcount:integer;
  Trode:Ttreenode;
  rownum:integer;
begin
 colnum:=adoDataSetX.FieldDefList.Count;
 rownum:=adoDataSetX.RecordCount;
 for i:=1 to colnum do
 begin
  //colwidth
  worksheet.Columns[i].ColumnWidth:=ColWidthInt;
  //
  worksheet.Columns[i].Font.size:=9;
  worksheet.Columns[i].Font.Name := '宋体';
 end;
 if colnum>26 then
   colname:=char(colnum div 26 +64)+ char(colnum mod 26 +64)
 else
   colname:=char(colnum +64);
 if  rownum=0 then rownum:=Maxnum;
 worksheet.Range['A1:'+colname+inttostr(2+rownum+Maxnum-1)].Font.Name := '宋体';
 worksheet.Range['A1:'+colname+inttostr(2+rownum+Maxnum-1)].Font.Size := 9;
 worksheet.range['A1:'+colname+inttostr(2+rownum+Maxnum-1)].HorizontalAlignment := $FFFFEFF4;
 worksheet.range['A1:'+colname+inttostr(2+rownum+Maxnum-1)].VerticalAlignment := $FFFFEFF4;

 worksheet.range['A2:'+colname+inttostr(2+rownum+Maxnum-1)].Borders[1].Weight := 2;
 worksheet.range['A2:'+colname+inttostr(2+rownum+Maxnum-1)].Borders[2].Weight := 2;
 worksheet.range['A2:'+colname+inttostr(2+rownum+Maxnum-1)].Borders[3].Weight := 2;
 worksheet.range['A2:'+colname+inttostr(2+rownum+Maxnum-1)].Borders[4].Weight := 2;

 worksheet.Range['A1',colname+'1'].Merge('false');
 worksheet.Range['A1:'+colname+'1'].Font.Name := '宋体';
 worksheet.Range['A1:'+colname+'1'].Font.Size := 14;
 worksheet.Range['A1:'+colname+'1'].Font.Bold := True;
 worksheet.range['A1:'+colname+'1'].HorizontalAlignment := $FFFFEFF4;
 worksheet.Rows[1].VerticalAlignment := $FFFFEFF4;
 worksheet.range['A1:'+colname+'1'].Value:=titlehead;

 treecount:=tvTemplate3.Items.count;
 treeno:=0 ;
 colnums:=0 ;
 while treeno <= treecount-1 do
  begin
   //取当前头有几层
    titlnum:=1;
    temcaption:=adoDataSetX.FieldDefList[colnums].Name;
     while Pos('_',temcaption) > 0 do
     begin
       Delete(temcaption, 1, Pos('_', temcaption));
       titlnum:=titlnum+1;
     end;
   ///////////////////////
     if  (tvTemplate3.Items[treeno].Parent=nil) and (tvTemplate3.Items[treeno].Count=0)   then
     //无child的第一层节点
     begin
{2004      if (colnums+1) >26  then
        colname:=char((colnums+1) div 26 +64)+ char((colnums+1) mod 26 +64) //colnum 改为 colnums
      else
       colname:=char((colnums+1) +64);
}
      if colnums div 26 = 0 then
        colname := char(colnums MOD 26 + 1 +64)
      else
        colname := char(colnums div 26 +64) + char(colnums MOD 26 + 1 +64) ;

      worksheet.Range[colname+'2',colname+inttostr(Maxnum+1)].Merge('false');
      worksheet.Range[colname+'2',colname+inttostr(Maxnum+1)].Value:= tvTemplate3.Items[treeno].text;
      treeno:=treeno+1;
      colnums:=colnums+1;
     end
    else   //下级结点
     begin
        if (tvTemplate3.Items[treeno].Parent=nil) and (tvTemplate3.Items[treeno].Count<>0) then
        //有child的第一层节点
         begin
             numberNode(tvTemplate3.Items[treeno],tvTemplate3);
             childcount :=childNum;
             Depnum:=Depthnum;//本结点的深度
            // Depnum:=titlnum;
            // childcount := TreeView1.Items[treeno].Count;
//xia加            if treeno >26 then
      if colnums div 26 = 0 then
        colmix := char(colnums MOD 26 + 1 +64)
      else
        colmix := char(colnums div 26 +64) + char(colnums MOD 26 + 1 +64) ;

{ 2004           if (colnums+1) >26 then            //  xia加
               colmix:=char((colnums+1) div 26 +64)+ char((colnums+1) mod 26 +64)
            else
               colmix:=char((colnums+1) +64);
 }

      if (colnums + childcount -1) div 26 = 0 then
        colmax := char((colnums + childcount -1) MOD 26 + 1 +64)
      else
        colmax := char((colnums + childcount -1) div 26 +64)
                                + char((colnums + childcount -1) MOD 26 + 1 +64) ;

{ 2004
            if (colnums+1+childcount-1)>26 then
               colmax:=char((colnums+1+childcount-1) div 26 +64)+ char((colnums+1+childcount-1) mod 26 +64)
            else
               colmax:=char((colnums+1+childcount-1) +64);
}
            worksheet.Range[colmix+'2',colmax+'2'].Merge('false');
            worksheet.Range[colmix+'2',colmax+'2'].Value:= tvTemplate3.Items[treeno].text;
            colnums:=colnums-1;
          //  if colnums =-1 then  colnums:=0;
         end
        else if (tvTemplate3.Items[treeno].Parent<>nil) and (tvTemplate3.Items[treeno].Count<>0) then
          //有child的层节点
          begin
             numberNode(tvTemplate3.Items[treeno],tvTemplate3);
             childcount :=childNum;
             Depnum:=Depthnum;//本结点的深度
           // childcount :=TreeView1.Items[treeno].Count;
           // Depnum:=titlnum;
//xia加            if treeno >26 then
      if colnums div 26 = 0 then
        colmix := char(colnums MOD 26 + 1 +64)
      else
        colmix := char(colnums div 26 +64) + char(colnums MOD 26 + 1 +64) ;

{2004            if (colnums+1) >26 then            //  xia加
            colmix:=char((colnums+1) div 26 +64)+ char((colnums+1) mod 26 +64)
            else
               colmix:=char((colnums+1) +64);
}
      if (colnums + childcount -1) div 26 = 0 then
        colmax := char((colnums + childcount -1) MOD 26 + 1 +64)
      else
        colmax := char((colnums + childcount -1) div 26 +64)
                                + char((colnums + childcount -1) MOD 26 + 1 +64) ;

{2004            if (colnums+1+childcount-1)>26 then
               colmax:=char((colnums+1+childcount-1) div 26 +64)+ char((colnums+1+childcount-1) mod 26 +64)
            else
               colmax:=char((colnums+1+childcount-1) +64);
}
            treenotemp:=0;
            Trode:=tvTemplate3.Items[treeno].Parent;
            while Trode <> nil do
             begin
               treenotemp:=treenotemp+1;
               Trode:=Trode.Parent ;
             end;

            worksheet.Range[colmix+inttostr(2+treenotemp),colmax+inttostr(2+treenotemp)].Merge('false');
            worksheet.Range[colmix+inttostr(2+treenotemp),colmax+inttostr(2+treenotemp)].Value:= tvTemplate3.Items[treeno].text;
            colnums:=colnums-1;
           // if colnums =-1 then  colnums:=0;
          end
        else  //最低层结点
         begin
      if colnums div 26 = 0 then
        colname := char(colnums MOD 26 + 1 +64)
      else
        colname := char(colnums div 26 +64) + char(colnums MOD 26 + 1 +64) ;
{2004            if colnums+1>26 then
             colname:=char((colnums+1) div 26 +64)+ char((colnums+1) mod 26 +64)
            else
             colname:=char((colnums+1) +64);
}
            if Maxnum-titlnum=0 then
               worksheet.cells.item[Maxnum+1,colnums+1]:=tvTemplate3.Items[treeno].text
            else
            begin
              treenotemp:=0;
              Trode:=tvTemplate3.Items[treeno].Parent;
              while Trode <> nil do
              begin
                treenotemp:=treenotemp+1;
                Trode:=Trode.Parent ;
              end;
              worksheet.Range[colname+inttostr(2+titlnum-1),colname+inttostr(2+Maxnum-1)].Merge('false');
              worksheet.Range[colname+inttostr(2+titlnum-1),colname+inttostr(2+Maxnum-1)].Value:= tvTemplate3.Items[treeno].text;
            end;
        end;
        treeno:=treeno+1;
        colnums:=colnums+1;
     end; //else
  end;  //while

  //procedure WriteExcelData(adoDataSetX:TADODataSet);
end;


        2.1 numberNode

              说明:计算节点数

              声明:procedure numberNode(TNode:TTreenode;tvTemplate2:TTreeView);


              实现:

procedure numberNode(TNode:TTreenode;tvTemplate2:TTreeView);
type
   Nodesum=record
   Node:TTreenode; //结点
   Layer:Integer;  //所在层
 end;
var
  a:array of Nodesum;
  Node:TTreenode;
  Depth,i,j,num:Integer;
begin
  Depth:=0;
  i:=0;
  j:=0;
  num:=0;
  Setlength(a,tvTemplate2.Items.Count);
  Node:=TNode;  //使顶结点为您所选择的结点
  a[i].Node:=Node;
  a[i].Layer:=1;
  while a[i].Node<> nil do
    begin
        Node:=a[i].Node.Getfirstchild;//取其第一个子结点
        while Node <> nil do
           begin
             j:=j+1;
             a[j].Node:=Node;
             a[j].Layer:=a[i].Layer+1; //此结点所在的层数
             Node:=Node.GetNextSibling;
           end;//如此循环,以取出此层所有结点
    i:=i+1;
  end;
  i:=0;
 while (a[i].node<>nil)   do
     begin
      if a[i].Layer>Depth then
         Depth:=a[i].Layer;//求最大的层数,即子树深度
      IF (a[i].Node.count=0) THEN
         num:=num+1;
      i:=i+1;
     end;
 If (a[0].Node<>nil)   then
 begin
    childNum :=num;
    DepthNum:=Depth;
 end;
end;




        3. WriteExcelData

            说明:输出数据表数据到Excel中。


            声明:procedure WriteExcelData(adoDataSetX:TADODataSet);


           实现:

procedure WriteExcelData(adoDataSetX:TADODataSet);
var
  i1:Integer;
  Tabrow:integer;
  Valuestr:string;  
begin
  adoDataSetX.First ;
  try
     For i1:=0 To  adoDataSetX.Recordcount -1 do
    begin
     for Tabrow:=0 to adoDataSetX.FieldDefList.Count-1  do
      begin
        if not adoDataSetX.Fields[Tabrow].IsNull  then
         begin
           Valuestr:= adoDataSetX.Fields[Tabrow].AsString;
           worksheet.Cells.item[2+Maxnum+i1,Tabrow+1]:=''''+ Valuestr  ;
         end;
      end;
     adoDataSetX.Next;
   end;
  except
  end;

end;


文章评论

18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
十大编程算法助程序员走上高手之路
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
什么才是优秀的用户界面设计
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员周末都喜欢做什么?
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
“懒”出效率是程序员的美德
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
如何成为一名黑客
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
做程序猿的老婆应该注意的一些事情
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
每天工作4小时的程序员
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
老美怎么看待阿里赴美上市
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
中美印日四国程序员比较
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
那些争议最大的编程观点
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
团队中“技术大拿”并非越多越好
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
Web开发者需具备的8个好习惯
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员应该关注的一些事儿
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
Java 与 .NET 的平台发展之争
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员的一天:一寸光阴一寸金
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
60个开发者不容错过的免费资源库
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
漫画:程序员的工作
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
我的丈夫是个程序员
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
当下全球最炙手可热的八位少年创业者
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员的样子
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员眼里IE浏览器是什么样的
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
代码女神横空出世
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员最害怕的5件事 你中招了吗?
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
写给自己也写给你 自己到底该何去何从
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
亲爱的项目经理,我恨你
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序猿的崛起——Growth Hacker
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
不懂技术不要对懂技术的人说这很容易实现
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
编程语言是女人
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员必看的十大电影
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
一个程序员的时间管理
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
科技史上最臭名昭著的13大罪犯
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
鲜为人知的编程真相
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
我跳槽是因为他们的显示器更大
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
Java程序员必看电影
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
老程序员的下场
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
Web开发人员为什么越来越懒了?
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
旅行,写作,编程
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
10个帮程序员减压放松的网站
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
总结2014中国互联网十大段子
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员和编码员之间的区别
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
看13位CEO、创始人和高管如何提高工作效率
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
要嫁就嫁程序猿—钱多话少死的早
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
5款最佳正则表达式编辑调试器
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
程序员都该阅读的书
18、Cocos2dx 3.0游戏开发找小三之cocos2d-x,请教你是如何调度的咩
为什么程序员都是夜猫子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有