Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/google-chrome/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
User interface 模态对话框是如何实现的?_User Interface - Fatal编程技术网

User interface 模态对话框是如何实现的?

User interface 模态对话框是如何实现的?,user-interface,User Interface,很长一段时间以来,我一直在想模态对话框是如何实现的 让我以Qt为例。(几乎所有GUI工具包都有这种机制) 在主事件循环中,调用一个插槽,并在该插槽中打开一个模式对话框。在对话框关闭之前,插槽不会将控制权返回到主事件循环。所以我认为主事件循环被阻塞,没有响应。显然这不是真的,因为当你打开一个模态对话框时,背景主窗口仍然在工作,就像重新绘制它的UI或者继续显示一条曲线或一些图形。它只是变得不接受任何用户输入 我做了一个实验。我没有在插槽中打开模式对话框,但在那里启动了一个新线程,并等待线程在该插槽中

很长一段时间以来,我一直在想模态对话框是如何实现的

让我以Qt为例。(几乎所有GUI工具包都有这种机制)

在主事件循环中,调用一个插槽,并在该插槽中打开一个模式对话框。在对话框关闭之前,插槽不会将控制权返回到主事件循环。所以我认为主事件循环被阻塞,没有响应。显然这不是真的,因为当你打开一个模态对话框时,背景主窗口仍然在工作,就像重新绘制它的UI或者继续显示一条曲线或一些图形。它只是变得不接受任何用户输入

我做了一个实验。我没有在插槽中打开模式对话框,但在那里启动了一个新线程,并等待线程在该插槽中完成。这绝对阻止了主事件循环


模态对话框到底是如何实现的?它如何保持主事件循环未被阻止,但同时阻止调用插槽?

通常,这种类型的模式对话框是通过运行自己的消息循环而不是应用程序的消息循环来实现的。即使在模式操作期间,定向到主窗口的消息(如计时器或绘制消息)仍将被传递


在某些情况下,您可能必须小心,不要递归地重复做相同的事情。例如,如果您在计时器消息上触发一个模式对话框,并结合一些持久性标志,则您需要确保不会在计时器消息触发时重复显示同一对话框。

如果您正在查找示例,请参阅另一个示例:

在Tk中,只有一个事件循环。模态行为(不必是对话框,也可以是工具提示、文本框等)只需使主窗口忽略鼠标和键盘事件即可实现。由于事件循环仍在运行,因此所有其他事件(如重绘等)仍可以提供服务

Tk通过
[grab]
函数实现这一点。对UI对象调用
grab
,使其成为唯一能够响应键盘和鼠标事件的对象。基本上阻止所有其他对象。这不会影响事件循环。它只是暂时禁用事件处理程序,直到释放抓取

应该注意的是,运行X的类Unix操作系统也在窗口系统中内置了
grab
。因此,它不一定仅仅由UI工具包库实现,有时也是操作系统的内置功能。同样,这是通过简单的事件阻塞/禁用来实现的,而不是实例化单独的事件循环。我相信OSX之前的旧MacOS也是如此。但不确定OSX或Windows。尽管模态通常由操作系统本身实现,但像Qt和Tk这样的工具包通常实现它们自己的机制来跨不同平台标准化行为


因此,结论是,不需要阻塞主事件循环来实现模态。您只需要阻止事件和/或事件处理程序。

只需要一个事件循环,当出现模式对话框时,它不会阻止。不过,我想,不同的工具包处理这个问题的方式可能不同。您需要查阅文档才能确定。然而,从概念上讲,它都以相同的方式工作

每个事件都有一个发生事件的源。出现模式对话框时,事件循环会忽略或重定向对话框外部发生的所有事件。这真的没有什么魔力。一般来说,它就像事件循环代码中的if语句,表示“if(modal_is_show()和!event_is_in_modal_window()){ignore_和_wait_for_next_event()}”。当然,逻辑有点复杂,但这就是它的要点。

的答案是正确的

然而,通过阅读他和我之间的后续讨论,我觉得有进一步澄清的空间,通过散文和一些伪代码

我将用事实清单来处理散文部分:

  • 在模式活动完成之前,主消息循环不会运行
  • 但是,在模式活动运行时仍会传递事件
  • 这是因为在模态活动中有一个嵌套的事件循环
到目前为止,我只是重复了格雷格的回答,请容忍我,因为这是为了连续性。下面是我希望提供更多有用信息的地方

  • 嵌套事件循环是GUI工具包的一部分,因此,它知道与存在的每个窗口相关的回调函数
  • 当嵌套事件循环引发事件(例如指向主窗口的重绘事件)时,它将调用与该事件关联的回调函数。注意,在面向对象系统中,这里的“回调”可能是指表示窗口的类的方法
  • 回调函数执行所需的操作(例如重新绘制),并直接返回嵌套的消息循环(模式活动中的循环)
最后,但并非最不重要的一点是,这里有一段伪代码,希望通过一个虚构的“GuiToolkit”进一步说明:


是的,消息仍将被传递,但主事件循环如何有机会“处理”它们。我的意思是,当插槽未返回时(插槽正在等待模式对话框),主事件循环是否被阻止?您可能会发现,主消息循环在模式对话框可见期间不处理消息。在这种情况下,消息由模式对话框代码内的其他消息循环处理。您确定模式对话框内的消息循环将处理指向主窗口的所有消息吗?例如,当您拖动和移动模式对话框时,背景窗口将重新绘制/重画某个区域,是否由模式对话框处理?我无法理解。重绘消息由模式对话框中的消息循环接收,并且
void GuiToolkit::RunModal( ModalWindow *m )
{
    // main event loop
    while( !GuiToolkit::IsFinished() && m->IsOpen() )
    {
          GuiToolkit::ProcessEvent(); // this will call
                                      // MainWindow::OnRepaint
                                      // as needed, via the virtual
                                      // method of the base class
                                      // NonModalWindow::OnRepaint
    }
}

class AboutDialog: public ModalWindow
{
}

class MainWindow: public NonModalWindow
{
    virtual void OnRepaint()
    {
        ...
    }
    virtual void OnAboutBox()
    {
         AboutDialog about;
         GuiToolkit::RunModal(&about); // blocks here!!
    }
}

main()
{
    MainWindow window;
    GuiToolkit::Register( &window ) // GuiToolkit knows how to 
                                    // invoke methods of window

    // main event loop
    while( !GuiToolkit::IsFinished() )
    {
          GuiToolkit::ProcessEvent(); // this will call
                                      // MainWindow::OnAboutBox
                                      // at some point
    }
}