如何将Python的GTK与gevent集成?

Gtk是一个GUI工具包,绑定到 Python. Gevent是一个基于libevent(新版本上的libev)和greenlets构建的Python网络库,允许在greenlet中使用网络功能而不会阻塞整个过程.

Gtk和gevent都阻塞了调度事件的主循环.如何集成他们的主循环,以便我可以在我的应用程序上接收网络事件和UI事件,而不会阻止另一个?

天真的方法是在Gtk的主循环上注册一个空闲回调,只要没有Gtk事件就会调用它.在这个回调中,我们产生了greenlet,因此可以发生网络事件,同时给出一个小的超时,因此进程不忙等待:

from gi.repository import GLib
import gevent

def _idle():
    gevent.sleep(0.1)
    return True

GLib.idle_add(_idle)

这种方法远非理想,因为我在UI事件处理之间有100毫秒的延迟,如果我将值降低太多,我会浪费太多处理器忙等待.

我想要一个更好的方法,我的过程真的睡着了,而没有事件需要处理.

PS:我已经找到了一个特定于Linux的解决方案(也可能在MacOS下工作).我现在真正需要的是一个可行的Windows解决方案.

解决方法

鉴于当前的gevent API,我认为没有通用的解决方案,但我认为每个平台可能都有特定的解决方案.

Posix解决方案(在Linux下测试)

由于GLib的主循环接口允许我们设置轮询函数,即获取一组文件描述符并在其中一个准备就绪时返回的函数,我们定义一个轮询函数,该函数依赖于gevent的select来知道文件描述符何时准备好.

Gevent不公开poll()接口,而select()接口有点不同,所以我们必须在调用gevent.select.select()时转换参数和返回值.

稍微复杂一点的是,GLib没有通过Python的接口公开允许这个技巧的特定函数g_main_set_poll_func().所以我们必须直接使用C函数,为此,ctypes模块派上用场.

import ctypes
from gi.repository import GLib
from gevent import select

# Python representation of C struct
class _GPollFD(ctypes.Structure):
    _fields_ = [("fd",ctypes.c_int),("events",ctypes.c_short),("revents",ctypes.c_short)]

# Poll function signature
_poll_func_builder = ctypes.CFUNCTYPE(None,ctypes.POINTER(_GPollFD),ctypes.c_uint,ctypes.c_int)

# Pool function
def _poll(ufds,nfsd,timeout):
    rlist = []
    wlist = []
    xlist = []

    for i in xrange(nfsd):
        wfd = ufds[i]
        if wfd.events & GLib.IOCondition.IN.real:
            rlist.append(wfd.fd)
        if wfd.events & GLib.IOCondition.OUT.real:
            wlist.append(wfd.fd)
        if wfd.events & (GLib.IOCondition.ERR.real | GLib.IOCondition.HUP.real):
            xlist.append(wfd.fd)

    if timeout < 0:
        timeout = None
    else:
        timeout = timeout / 1000.0

    (rlist,wlist,xlist) = select.select(rlist,xlist,timeout)

    for i in xrange(nfsd):
        wfd = ufds[i]
        wfd.revents = 0
        if wfd.fd in rlist:
            wfd.revents = GLib.IOCondition.IN.real
        if wfd.fd in wlist:
            wfd.revents |= GLib.IOCondition.OUT.real
        if wfd.fd in xlist:
            wfd.revents |= GLib.IOCondition.HUP.real
        ufds[i] = wfd

_poll_func = _poll_func_builder(_poll)

glib = ctypes.CDLL('libglib-2.0.so.0')
glib.g_main_context_set_poll_func(None,_poll_func)

我觉得应该有一个更好的解决方案,因为这样我们需要知道正在使用的GLib的特定版本/名称.如果GLib在Python中暴露了g_main_set_poll_func(),则可以避免这种情况.此外,如果gevent实现select(),它可以很好地实现poll(),什么会使这个解决方案更简单.

Windows部分解决方案(丑陋和破碎)

Posix解决方案在Windows上失败,因为select()仅适用于网络套接字,而Gtk处理的内容则不然.所以我想在另一个等待UI事件的线程中使用GLib自己的g_poll()实现(Posix上的一个瘦包装器,在Windows上是一个相当复杂的实现),并通过主线程中的gevent方面同步它一个TCP套接字.这是一个非常难看的方法,因为它需要真正的线程(除了你可能会使用的greenlets,如果你使用gevent)和等待线程端的普通(非gevent)套接字.

Windows上太糟糕的UI事件由线程分割,因此默认情况下,一个线程不能等待另一个线程上的事件.在执行某些UI内容之前,不会创建特定线程上的消息队列.所以我不得不在等待的线程上创建一个空的WinAPI消息框(MessageBoxA())(当然有更好的方法),并使用AttachThreadInput()修改线程消息队列,以便它可以看到主线程的事件.这一切都来自ctypes.

import ctypes
import ctypes.wintypes
import gevent
from gevent_patcher import orig_socket as socket
from gi.repository import GLib
from threading import Thread

_poll_args = None
_sock = None
_running = True

def _poll_thread(glib,addr,main_tid):
    global _poll_args

    # Needed to create a message queue on this thread:
    ctypes.windll.user32.MessageBoxA(None,ctypes.c_char_p('Ugly hack'),ctypes.c_char_p('Just click'),0)

    this_tid = ctypes.wintypes.DWORD(ctypes.windll.kernel32.GetCurrentThreadId())
    w_true = ctypes.wintypes.BOOL(True)
    w_false = ctypes.wintypes.BOOL(False)

    sock = socket()
    sock.connect(addr)
    del addr

    try:
        while _running:
            sock.recv(1)
            ctypes.windll.user32.AttachThreadInput(main_tid,this_tid,w_true)
            glib.g_poll(*_poll_args)
            ctypes.windll.user32.AttachThreadInput(main_tid,w_false)
            sock.send('a')
    except IOError:
        pass
    sock.close()

class _GPollFD(ctypes.Structure):
    _fields_ = [("fd",ctypes.c_short)]

_poll_func_builder = ctypes.CFUNCTYPE(None,ctypes.c_int)
def _poll(*args):
    global _poll_args
    _poll_args = args
    _sock.send('a')
    _sock.recv(1)

_poll_func = _poll_func_builder(_poll)

# Must be called before Gtk.main()
def register_poll():
    global _sock

    sock = gevent.socket.socket()
    sock.bind(('127.0.0.1',0))
    addr = sock.getsockname()
    sock.listen(1)

    this_tid = ctypes.wintypes.DWORD(ctypes.windll.kernel32.GetCurrentThreadId())
    glib = ctypes.CDLL('libglib-2.0-0.dll')
    Thread(target=_poll_thread,args=(glib,this_tid)).start()
    _sock,_ = sock.accept()
    sock.close()

    glib.g_main_context_set_poll_func(None,_poll_func)

# Must be called after Gtk.main()
def clean_poll():
    global _sock,_running
    _running = False
    _sock.close()
    del _sock

到目前为止,应用程序运行并对点击和其他用户事件做出正确反应,但窗口内没有任何内容(我可以看到框架和背景缓冲区粘贴到其中).在线程和消息队列的重整中可能缺少某些重绘命令.我不知道如何修复它.有帮助吗?关于如何做的更好的想法?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用OpenCV实现视频去抖 整体步骤: 设置输入输出视频 寻找帧之间的移动:使用opencv的特征检测器,检测前一帧的特征,并使用Lucas-Kanade光流算法在下一帧跟踪这些特征,根据两组点,将前一个坐标系映射到当前坐标系完成刚性(欧几里得)变换,最后使用数组纪录帧之间的运动。 计算帧之间的平
前言 对中文标题使用余弦相似度算法和编辑距离相似度分析进行相似度分析。 准备数据集part1 本次使用的数据集来源于前几年的硕士学位论文,可根据实际需要更换。结构如下所示: 学位论文题名 基于卷积神经网络的人脸识别研究 P2P流媒体视频点播系统设计和研究 校园网安全体系的设计与实现 无线传感器网络中
前言 之前尝试写过一个爬虫,那时对网页请求还不够熟练,用的原理是:爬取整个html文件,然后根据标签页筛选有效信息。 现在看来这种方式无疑是吃力不讨好,因此现在重新写了一个爬取天气的程序。 准备工作 网上能轻松找到的是 101010100 北京这种编号,而查看中国气象局URL,他们使用的是北京545
前言 本文使用Python实现了PCA算法,并使用ORL人脸数据集进行了测试并输出特征脸,简单实现了人脸识别的功能。 1. 准备 ORL人脸数据集共包含40个不同人的400张图像,是在1992年4月至1994年4月期间由英国剑桥的Olivetti研究实验室创建。此数据集包含40个类,每个类含10张图
前言 使用opencv对图像进行操作,要求:(1)定位银行票据的四条边,然后旋正。(2)根据版面分析,分割出小写金额区域。 图像校正 首先是对图像的校正 读取图片 对图片二值化 进行边缘检测 对边缘的进行霍夫曼变换 将变换结果从极坐标空间投影到笛卡尔坐标得到倾斜角 根据倾斜角对主体校正 import
天气预报API 功能 从中国天气网抓取数据返回1-7天的天气数据,包括: 日期 天气 温度 风力 风向 def get_weather(city): 入参: 城市名,type为字符串,如西安、北京,因为数据引用中国气象网,因此只支持中国城市 返回: 1、列表,包括1-7的天气数据,每一天的分别为一个
数据来源:House Prices - Advanced Regression Techniques 参考文献: Comprehensive data exploration with Python 1. 导入数据 import pandas as pd import warnings warnin
同步和异步 同步和异步是指程序的执行方式。在同步执行中,程序会按顺序一个接一个地执行任务,直到当前任务完成。而在异步执行中,程序会在等待当前任务完成的同时,执行其他任务。 同步执行意味着程序会阻塞,等待任务完成,而异步执行则意味着程序不会阻塞,可以同时执行多个任务。 同步和异步的选择取决于你的程序需
实现代码 import time import pydirectinput import keyboard if __name__ == &#39;__main__&#39;: revolve = False while True: time.sleep(0.1) if keyboard.is_pr
本文从多个角度分析了vi编辑器保存退出命令。我们介绍了保存和退出vi编辑器的命令,以及如何撤销更改、移动光标、查找和替换文本等实用命令。希望这些技巧能帮助你更好地使用vi编辑器。
Python中的回车和换行是计算机中文本处理中的两个重要概念,它们在代码编写中扮演着非常重要的角色。本文从多个角度分析了Python中的回车和换行,包括回车和换行的概念、使用方法、使用场景和注意事项。通过本文的介绍,读者可以更好地理解和掌握Python中的回车和换行,从而编写出更加高效和规范的Python代码。
SQL Server启动不了错误1067是一种比较常见的故障,主要原因是数据库服务启动失败、权限不足和数据库文件损坏等。要解决这个问题,我们需要检查服务日志、重启服务器、检查文件权限和恢复数据库文件等。在日常的数据库运维工作中,我们应该时刻关注数据库的运行状况,及时发现并解决问题,以确保数据库的正常运行。
信息模块是一种可重复使用的、可编程的、可扩展的、可维护的、可测试的、可重构的软件组件。信息模块的端接需要从接口设计、数据格式、消息传递、函数调用等方面进行考虑。信息模块的端接需要满足高内聚、低耦合的原则,以保证系统的可扩展性和可维护性。
本文从电脑配置、PyCharm版本、Java版本、配置文件以及程序冲突等多个角度分析了Win10启动不了PyCharm的可能原因,并提供了解决方法。
本文主要从多个角度分析了安装SQL Server 2012时可能出现的错误,并提供了解决方法。
Pycharm是一款非常优秀的Python集成开发环境,它可以让Python开发者更加高效地进行代码编写、调试和测试。在Pycharm中设置解释器非常简单,我们可以通过创建新项目、修改项目解释器、设置全局解释器等多种方式进行设置。
Python中有多种方法可以将字符串转换为整数,包括使用int()函数、try-except语句、正则表达式、map()函数、ord()函数和reduce()函数。在实际应用中,应根据具体情况选择最合适的方法。
本文介绍了导入CSV文件的多种方法,包括使用Excel、Python和R等工具。同时,还介绍了导入CSV文件时需要注意的一些细节和问题。CSV文件是数据处理和分析中不可或缺的一部分,希望本文能够对读者有所帮助。
mongodb是一种新型的数据库,它采用了面向文档的数据模型,具有灵活性、高性能和高可用性等优势。但是,mongodb也存在数据结构混乱、安全性和学习成本高等问题。
当Python运行不了时,我们应该从代码、Python环境、操作系统和硬件设备等多个角度来排查问题,并采取相应的解决措施。