GUI框架是如何工作的?

18

我在网上寻找答案,我的问题是:GUI框架是如何工作的?例如Qt是如何工作的?有没有关于从头开始编写GUI框架的书籍或网站?框架是否必须从操作系统的GUI框架中调用方法?

-- 谢谢任何花时间尝试回答这个问题的人,并请原谅我拼错了任何单词。


3
这是一个很好的问题,有没有人知道一本涵盖了三种或更多设计方法来构建实际GUI框架的教材,而不仅仅是作为API用户的教材? - Grant Rostig
@GrantRostig,你有没有找到这样的教材? - Sujal Singh
1
@SujalSingh,甚至课程笔记或幻灯片也不可以。 - Grant Rostig
2个回答

25
在过去,我们经常从头开始编写GUI程序。这并不像看起来那么难,但需要几周时间才能得到结果。
首先,您需要一个良好的绘图库。该库的最小功能是绘制裁剪矩形(使用图案),线条,位图和字体。您可以通过将字体创建为位图来作弊,并且裁剪矩形只是一堆水平线。
现在,您至少需要鼠标,键盘和定时器的驱动程序(如果操作系统尚未提供)。通常,您需要检测按键,符号键(例如Shift等),鼠标移动和鼠标单击。基本的计时器函数将允许您检测双击。
然后,您需要创建一个窗口数据结构。此数据结构需要具有坐标,即矩形,链接到父窗口(如果不是顶级窗口),以及窗口函数,即当此窗口应处理某些事件时将调用的函数。
一旦您可以在屏幕上绘制,您就需要一些矩形代数函数。您至少需要一个良好的函数来计算矩形的交集,并快速解析相对于绝对坐标。例如-如果您的子窗口具有父窗口,则应将其x和y递归地添加到父x和y中,直到达到顶级窗口。
此时,您已经拥有: - 原始图形函数, - 窗口结构,- 鼠标驱动程序,键盘驱动程序,和定时器 - 矩形算术。

现在您可以编写 主要事件收集函数。此函数将一直运行。它的目的是检测事件并向正确的窗口发送消息。什么是事件?当您启动程序时,请存储鼠标x和y坐标。然后在一个循环中检查它们是否发生了变化。如果它们发生了变化,请找到该位置的窗口...并向其发送WM_MOUSEMOVE事件。您的收集函数应处理: - 鼠标移动 - 鼠标单击 - 鼠标双击(记住最后一次单击和位置,测量时间并决定是否为双击) - 定时器事件 - 键盘缓冲区更改 ...

现在您应该能够向Windows发送事件。但是您真正需要的是一种机制。它是消息队列和窗口过程的组合。通常的工作方式是这样的:每个窗口都有一个窗口过程,它通常接受四个参数:消息ID(例如鼠标移动、绘画消息),窗口句柄,参数1和参数2。您可以使用类似于send_message函数直接调用此窗口过程,或者可以通过post_message函数向该窗口发送消息。这将将消息放入队列中,窗口将逐一处理消息,最终接收到这个消息。那么为什么要直接调用某些消息并将其他消息放入队列中呢?因为优先级。您知道,键盘点击可能需要等待一段时间才能被处理。但是窗口重绘必须立即完成,以防止屏幕闪烁和错误数据。

因此,您的harvest_events函数使用post_message和send_message向窗口发送消息。而您的窗口消息泵使用类似于以下的典型消息泵获取它们:

while (pmsg = get_message() != NULL) send_message(pmsg->id, pmsg->hwnd, pmsg->p1, pmsg->p2);

get_message 函数简单地从队列中获取消息并调用 send_message 函数。简单易懂,对吧?但事实并非如此。这样做只会接收到驱动程序发送的窗口消息,您还需要一些函数来重新绘制窗口、移动窗口等等。当您创建 move_window 函数、resize_window 函数、show_window 函数和 hide_window 函数时,窗口坐标将发生变化。其他窗口的某些部分将被覆盖(如果顶层窗口被移动或关闭)。您需要计算哪些窗口受到坐标更改的影响,并向这些窗口发送 paint 消息(仅重新绘制未被覆盖的部分——请记住,您拥有剪裁绘图函数,因此这样做是可行的)。

这些函数引入了消息 msg_paint、msg_move、msg_resize、msg_hide 等...

最后,你需要维护窗口的层次结构。你的顶层窗口应该是桌面。它应该有子窗口(应用程序顶层窗口)。这些窗口可能有更进一步的子窗口(按钮、编辑框等)。容纳这些窗口的明显结构是窗口树。当你检测到鼠标点击时,你必须遍历窗口树,并以聪明的方式(找出谁拥有焦点、谁是模态的等)向正确的窗口发送消息。当你绘制时,你也必须遍历所有的子窗口,看看谁被覆盖了,谁没有。最后但并非最不重要的是,你需要将鼠标矩形处理为顶层窗口,以防止在重新绘制窗口或(使用定时器和msg_paint事件)动画时闪烁鼠标。

大致就是这样。


1
将这样的GUI映射到现代面向对象的语言中,通常需要创建窗口类、记住窗口句柄,在静态成员中维护窗口列表,并通过使用现代C++信号和插槽或类似技术将事件从静态窗口函数重连到正确的类。GUI的最初实现通常是过程式的。但由于它是事件驱动的,所以很容易用OO语言进行封装。我认为没有针对此类问题的书籍。可以通过旧的开源库(如文本DFLAT库或图形GEM GUI库)来获取古老的智慧。 - Tomaz Stih
4
我认为这个回答连同评论应该被接受为正确答案。 - Ricardo Costeira
2
有没有人知道一本教材,涵盖了构建和实际GUI框架的三种或更多设计方法,而不仅仅是作为API用户? - Grant Rostig

7
一个像Qt这样的GUI框架通常通过获取现有操作系统的原始对象(窗口、字体、位图等),将其包装在更具平台中立性和更不臃肿的类/结构/句柄中,然后提供您所需的功能来操作它们。是的,这几乎总是涉及使用操作系统自己的函数,但并不一定需要--例如,如果您正在设计一个绘制OpenGL UI的API,大多数底层操作系统的GUI部分甚至都不起作用,您将自己完成几乎所有工作。
无论哪种方式,这都不是弱者能够胜任的。如果您必须询问GUI框架如何工作,那么您还远远没有准备好设计一个。最好坚持使用现有框架并扩展它以执行它尚未执行的花哨功能。

嗨,感谢您的回复,现在开始有些事情变得更加清晰了。我同意您的看法,我可能还不准备好开发一个项目,但希望有一天我能够达到那种理解水平。非常感谢您的回答,对我很有帮助。 - AlexW.H.B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接