【问题标题】:Is there a C library for GUIs that does not require its own event loop to be used?是否有不需要使用自己的事件循环的 GUI 的 C 库?
【发布时间】:2013-11-06 15:08:47
【问题描述】:

我正在寻找一个可以从纯 C 语言使用的 GUI 工具包,它至少可以在 Linux 上运行,并且不会强迫我使用它自己的事件循环——我想将 libev 用于主循环并让它通知X 事件发生时的工具包库。

我还没有找到类似的东西——我真的需要修补工具包库才能得到我想要的吗?

【问题讨论】:

  • 我在自己的 epoll 事件循环中使用了 ncurses —— 我只调用 ncurses 处理程序,只有一次,一旦 stdin 可供读取。
  • @KerrekSB 是的,我考虑过类似的事情......但是,我可能还必须至少修补工具包的超时逻辑。
  • 即使一个 gui 工具包连接到它自己的事件循环,你也不应该设置任何 connecting 属性,例如。我使用的是一个事件循环,但只能通过我在 draw 控件时设置的属性。因此,我可以完全避免使用事件循环,并选择以编程方式设置任何属性,因为我需要反映我的应用程序的状态。
  • 出于好奇,你为什么会有这样的需求?
  • “在额外的线程和上下文切换上浪费资源” — 您要避免浪费的主要资源是 您的时间。采取相应的行动。

标签: c user-interface x11 toolkit event-loop


【解决方案1】:

不幸的是,这种需求可能严重限制了您可以选择的 GUI 工具包,因为它们在这方面都非常糟糕(以及许多其他工具包)。我不知道这作为一个答案是否公平,但我想向您提出一个不同的解决方案:让 GUI 工具包运行它想要在自己的线程或进程中运行的任何事件循环时间>。由于 GUI 库是出了名的糟糕(崩溃或退出而没有警告),“自己的进程”版本实际上可能是最好的主意——您可以通过管道与您的 UI 通信,并像在主要过程。线程当然有自己的好处:不需要序列化与 GUI 共享的数据,也不需要担心用户在不杀死 GUI 的情况下杀死主程序或反之亦然的情况(因为不能单独杀死线程)。

【讨论】:

  • 而且我认为我是唯一一个如此不喜欢 GUI 工具包的人......呵呵。是的,如果我找不到更好的方法,使用多个线程可能会起作用——尽管我有点不喜欢那个解决方案,因为我必须编写一些代码来防止两个线程同时摆弄相同的数据结构,因为这意味着额外的不必要的上下文切换,因为它可能会使调试和分析变得更加困难。
  • “不必要的上下文切换”的代价是一个神话。即使它很昂贵,您所说的也是每个 GUI 事件或输出发生一次的事情,而不是每秒发生数百万次。实际上,即使在单个内核上,在同一进程的线程之间切换的延迟也是微不足道的。不需要刷新页表/TLB,只需换出通用寄存器和其他一些琐碎的事情。延迟在个位数微秒的范围内。在多核上(现在是常态)根本没有开关。另一个线程最终在另一个核心上运行。
【解决方案2】:

核武器。

https://github.com/Immediate-Mode-UI/Nuklear

Nuklear 是一个 GUI 工具包,它只创建小部件、按钮、标签等,但不使用自己的渲染后端。您必须为其提供渲染后端。 您可以使用 Xlib/X11 进行渲染。 Xlib 不需要主循环。你可以这样做:

  1. 创建到 X11 服务器的连接并创建一个窗口
  2. 初始化窗口(设置事件掩码、加载字体等)
  3. 初始化 Nuklar 上下文
  4. 检查来自 X11 套接字的事件并将其发送到 Nuklar
  5. 将您的 GUI 绘制到 Nuklear
  6. 将图形基元从 Nuklear 发送到 X11-Server。
  7. 做你需要做的所有其他事情
  8. 使用 XConnectionNumber() 获取 X11-Socket 的文件描述符
  9. 使用 pselect() 等待此文件描述符进行读取以及您等待的所有其他内容。
  10. 在第 4 步重复。

Nuklear 有一个示例头文件,它提供了组合 Nuklear 和 Xlib 所需的函数,可以帮助您完成第 4 步和第 6 步: https://github.com/Immediate-Mode-UI/Nuklear/blob/master/demo/x11/nuklear_xlib.h

Nuklear 有这个特点和缺点:

  • 您可以完全控制它。
  • 它是用 C89 编写的
  • 它本身不需要任何外部库,甚至不需要标准头文件
  • 单头实现
  • 您可以使用所需的渲染后端。
  • 没有不相关的功能,如文件处理或图像加载。您需要其他库或为此编写自己的函数。
  • 与 Qt 等工具包相比,图形可能性有限

【讨论】:

    【解决方案3】:

    我没试过,但 GTK+ 至少有gtk_main_iteration_do() 函数,它消耗单个事件。该事件是一个 GDK 事件(不是原始 X11 事件),所以这可能不是您想要的。

    另一方面,GTK 也有some functions for working with the events,所以也许你可以把一些东西粘在一起。我对 libev 不是很熟悉,所以不确定。

    【讨论】:

    • 问题是我的程序/libev 需要知道何时需要调用该方法,除非我从 libev 观看 glib 正在观看的所有内容,或者我正在调用该方法,否则这实际上是不可能的一遍又一遍,浪费资源。
    • @thejh 您可以通过观察 X 连接套接字(仅)知道事件是否可用。一旦你在套接字上有一些数据,运行while (gtk_events_pending()){ gtk_main_iteration(); }
    • @thejeh 请注意,例如glib/gtk 也有一个事件循环,你可以用与使用 libev 相同的方式集成它 - 你添加自己的套接字、计时器或你拥有的其他东西,并以与 libev 类似的方式接收回调
    【解决方案4】:

    所有工具包都支持这种操作模式。

    您需要在自己的事件引擎中观察 X 服务器连接套接字。一旦数据可用,您就可以(在伪代码中)

    while (PendingEvent())
      ProcessEvent()
    

    每个工具包都有自己的 ProcessEvent() 版本,也许还有 PendingEvent() 版本(但您始终可以为此使用 XPending(Display*))。即,

    • Gtk+ 有gtk_events_pending()gtk_main_iteration()
    • 基于 Xt 的工具包有 XtAppPending()XtDispatchEvent()
    • (c++) wxWidgets 有wxApp::.Pending()wxApp::Dispatch()
    • (c++) Qt有QApplication::processEvents(),你也可以实现自己的QAbstractEventDispatcher和/或QEventLoop

    积极开发的C工具包并不多,我认为Gtk+是唯一合理的选择。

    编辑 至少使用 GTK,此技术不适用于添加工具包的超时,即闪烁光标不会闪烁,除非有一些其他工具包事件要处理。定期调用 gtk_main_iteration_do(FALSE) 即使没有未决事件“修复”这个问题,但在不同的线程中执行工具包循环会更加健壮。

    【讨论】:

    • 如果工具包要设置超时时间怎么办?例如,对于闪烁的光标?
    • 嗯。一个好问题。我最近没用过这个。我最后一次尝试 Motif 是镇上唯一的游戏。我不记得闪烁光标有什么特别的问题。
    猜你喜欢
    • 1970-01-01
    • 2015-05-31
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多