简体   繁体   English

如何使用 Thread.Sleep 为二叉树设置动画?

[英]How to use Thread.Sleep to animate a Binary Tree?

I've been working on a program that visually outputs the contents of a binary tree (represented in turn by classes that I wrote myself).我一直在研究一个程序,它可以直观地输出二叉树的内容(依次由我自己编写的类表示)。 The last feature I want to include in this program is an animation of the postorder, inorder, and preorder construction of the tree.我要包含在该程序中的最后一个特征是树的后序、中序和前序构造的 animation。

This has proven much more challenging than I thought.事实证明,这比我想象的更具挑战性。 Here's the original Draw Method:这是原始的绘制方法:

private void DrawNode(int x, int y, BinaryTreeNode<T> node, int nodeLevel, int maxDepth, int connectX = -1, int connectY = -1, )
    {
        //calculate distance between the node's children
        int distance = CalculateDistance(nodeLevel, maxDepth);

        //draw the node at the specified coordinate
        node.Draw(x, y, this.device);

        if (node.Left != null)
        {
            DrawNode(x - distance / 2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node);
        }
        if (node.Right != null)
        {
            DrawNode(x + distance / 2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node);
        }

        //connect the node to its parent
        if ((connectX != -1) && (connectY != -1))
        {
            node.Connect(connectX, connectY, device);
        }

        this.display.Image = surface;
    }

My original idea was to simply put Thread.Sleep(1000) inside each of the first two if clauses - all I really needed to do was pause the execution of the program for 1 second before each drawing of a node.我最初的想法是简单地将 Thread.Sleep(1000) 放在前两个 if 子句中的每一个中——我真正需要做的就是在每次绘制节点之前暂停程序的执行 1 秒。

I realized that the Sleep method was blocking the execution of the drawing code, so I gave up on that method.. I then tried to use Timers, but found it impossibly difficult when dealing with the tree.我意识到 Sleep 方法阻塞了绘图代码的执行,所以我放弃了那个方法。然后我尝试使用 Timers,但在处理树时发现它非常困难。

My goal is to simply find a way to pause program execution without disrupting the GUI's responsiveness and without overly complicating the code..我的目标是简单地找到一种方法来暂停程序执行,而不会破坏 GUI 的响应能力,也不会使代码过于复杂。

Any help would be appreciated:).任何帮助,将不胜感激:)。

Edit: Some potentially relevant information: The program runs on Winforms, all graphics are handled via GDI+.编辑:一些可能相关的信息:该程序在 Winforms 上运行,所有图形均通过 GDI+ 处理。 If you need any other information, just ask:)如果您需要任何其他信息,请询问 :)

Edit: For SLaks,编辑:对于 SLaks,

//draw the node's children
        if (drawChildren)
        {
            if (node.Left != null)
            {
                if (this.timer2.Enabled)
                {
                    this.timer2.Stop();
                }
                if (!this.timer1.Enabled)
                {
                    this.timer1.Start();
                }
                this.count1++;
                this.timer1.Tick += (object source, EventArgs e) =>
                {
                    this.count1--;
                    DrawNode(x - distance / 2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node);
                    if (this.count1 == 0)
                    {
                        this.timer1.Stop();
                    }
                };
            }
            else
            {
                this.timer1.Stop();
                this.timer2.Start();
            }
            if (node.Right != null)
            {
                this.count2++;
                this.timer2.Tick += (object source, EventArgs e) =>
                {
                    this.count2--;
                    DrawNode(x + distance / 2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node);
                    if (this.count2 == 0)
                    {
                        this.timer2.Stop();
                    }
                };
            }
        }

Use a timer and set an appropriate interval for updates.使用计时器并设置适当的更新间隔。 In the Tick event perform the next step of drawing and display it.Tick事件中执行下一步的绘制并显示它。

First, write yourself a DrawNodesForLevel(int level) function. Then start at the top level, start the timer, every time it ticks, call DrawNodesForLevel() with the appropriate level, and increment the level.首先,给自己写一个DrawNodesForLevel(int level) function。然后从顶层开始,启动计时器,每次计时时,调用具有适当级别的DrawNodesForLevel() ,并递增级别。 When you get to the end, stop the timer.当你走到尽头时,停止计时器。

EDIT: Updated with the understanding that you want to pause between each node, not each level.编辑:更新了你想在每个节点之间暂停的理解,而不是每个级别。

Move the variables in your function to their own DrawNodeState class, and pass the instance of that class whenever you call DrawNode().将 function 中的变量移动到它们自己的DrawNodeState class,并在调用 DrawNode() 时传递该 class 的实例。 Then, instead of having DrawNode() call itself, have DrawNode() start a timer (also part of the DrawNodeState class).然后,不是让 DrawNode() 调用自身,而是让 DrawNode() 启动一个计时器(也是DrawNodeState类的一部分)。 When that timer ticks, the tick function calls DrawNode() and passes it the state structure.当该计时器计时时,计时 function 调用 DrawNode() 并将其传递给 state 结构。

The state structure is also going to have to track whether it last drew the left or right node, so it can draw the appropriate node next. state 结构还必须跟踪它最后绘制的是左节点还是右节点,以便接下来绘制合适的节点。

Split your code into 2 parts - one traversing the tree, another rendering (which you already have).将您的代码分成两部分 - 一个遍历树,另一个渲染(您已经拥有)。

Rewrite your "traverse tree" code to be IEnumerable<node> so you can pick nodes one by one.将您的“遍历树”代码重写为IEnumerable<node>以便您可以一个接一个地选择节点。 There are non-recursive verions of tree traversal for any order, so you can use "yield return" to make iterator.任何顺序都有非递归版本的树遍历,所以你可以使用“yield return”来制作迭代器。 You should be able to create simple tests to verify the code (no UI necessary for this).您应该能够创建简单的测试来验证代码(不需要 UI)。

Than in the timer callback simply take next item from the iterator till all done.比在定时器回调中简单地从迭代器中取出下一个项目直到完成。

One possible solution would be to spawn a separate thread, then use that thread to invoke the DrawNode function at a periodic interval.一种可能的解决方案是生成一个单独的线程,然后使用该线程定期调用 DrawNode function。 This way the UI thread will not be blocked.这样UI线程就不会被阻塞。

Since the thread you spawned will not be the UI thread, you will need to explicitly invoke the DrawNode function on the UI thread.由于您生成的线程不是 UI 线程,因此您需要在 UI 线程上显式调用 DrawNode function。 Here's one potential way of doing that:这是一种可能的方法:

How to update the GUI from another thread in C#? 如何从 C# 中的另一个线程更新 GUI?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM