Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.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
Python 3.x Python 3中GTK和Cairo的并行绘图_Python 3.x_Multiprocessing_Pygobject_Pycairo_Python Cffi - Fatal编程技术网

Python 3.x Python 3中GTK和Cairo的并行绘图

Python 3.x Python 3中GTK和Cairo的并行绘图,python-3.x,multiprocessing,pygobject,pycairo,python-cffi,Python 3.x,Multiprocessing,Pygobject,Pycairo,Python Cffi,我正在制作一个GTK应用程序,它将绘制复杂的图像,这可能需要很长时间才能完成。因此,我无法在DrawingArea的“draw”回调中绘制。我决定使用Python的多处理模块,该模块允许真正的并行性,并且不存在GTK和线程安全问题 Python的多处理模块使用Pickle协议在进程之间进行通信。GTK和Cairo对象不实现该协议。我的解决方案是将Cairo曲面转换为字节,然后通过管道将其发送到另一个进程 经过一些讨论,我发现Cairo有办法获取和设置ImageSurface的内部数据,但是pyc

我正在制作一个GTK应用程序,它将绘制复杂的图像,这可能需要很长时间才能完成。因此,我无法在DrawingArea的“draw”回调中绘制。我决定使用Python的多处理模块,该模块允许真正的并行性,并且不存在GTK和线程安全问题

Python的多处理模块使用Pickle协议在进程之间进行通信。GTK和Cairo对象不实现该协议。我的解决方案是将Cairo曲面转换为字节,然后通过管道将其发送到另一个进程

经过一些讨论,我发现Cairo有办法获取和设置ImageSurface的内部数据,但是pycairoforpython3不支持它。(pycairoforpython2似乎得到了支持,但我有一些理由使用python3。)

但是,还有另一个Python库支持它。所以我决定:

  • 使用Cairocfi在单独的流程中绘制
  • 将生成的曲面转换为字节
  • 通过管道发送字节并将其转换回cairocffi surface
  • 将cairocffi曲面转换为Pycairo曲面并显示它
  • 这是一个好方法吗?我的代码有任何错误吗?我不太理解
    转换\u magic.py
    文件

    main.py

    #!python3.4
    
    from multiprocessing import Process, Pipe
    
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import GLib,Gtk
    
    import cairocffi
    import cairo # also known as pycairo
    from conversion_magic import _UNSAFE_cairocffi_context_to_pycairo
    
    from math import pi
    import array
    
    # Draw a circle on cairocffi context.
    def circle(cr, radius):
        cr.set_source_rgb(1,0,0)
        cr.paint()
        cr.set_source_rgb(0,1,0)
        cr.arc(radius, radius, radius, 0, 2 * pi)
        cr.fill()
    
    # Drawing process
    def example_target(conn):
        while True:
            # Wait for new task.
            radius = conn.recv()
    
            # Create a cairocffi surface, draw on it and send it as bytes.
            surface = cairocffi.ImageSurface(cairo.FORMAT_ARGB32, 100,100)
            cr = cairocffi.Context(surface)
            circle(cr, radius)
            surface.flush()
            data = surface.get_data()
            conn.send(bytes(data))
    
    def main():
        # Create 2 connections for two-way communication between processes.
        parent,child = Pipe()
    
        proc = Process(target=example_target, args=(child,), daemon=True)
        proc.start()
    
        # Tell process to draw circles with radius i.
        for i in range(0,50):
            parent.send(i)
    
        win = Gtk.Window(default_height=300, default_width=300)
        win.connect("delete-event", Gtk.main_quit)
        drawing_area = Gtk.DrawingArea()
        win.add(drawing_area)
    
        def on_draw(widget,cr):
            # Check if we have new images to draw.
            if parent.poll():
                # Convert recieved data into a cairocffi surface. The arguments of
                # the ImageSurface must be the same as those in example_target.
                data = parent.recv()
                a = array.array('b', data)
                cffi_surf = cairocffi.ImageSurface(cairo.FORMAT_ARGB32,
                                                   100,100, data = a)
    
                # Convert cairocffi surface to pycairo surface.
                cffi_cr = cairocffi.Context(cffi_surf)
                pycairo_cr = _UNSAFE_cairocffi_context_to_pycairo(cffi_cr)
                pycairo_surf = pycairo_cr.get_target()
    
                # Draw pycairo surface to the surface from GTK. Using cairocffi
                # surface would not work here, as GTK uses pycairo.
                cr.set_source_surface(pycairo_surf)
                cr.paint()
            else:
                pass
                # TODO: Implement a buffer that holds the last image we got from
                # parent.recv() and draw it. Not included in this example to make
                # things easier.
    
            return True
    
        drawing_area.connect('draw', on_draw)
    
        # Draw new image after each 100ms.
        def cause_drawing():
            drawing_area.queue_draw()
            return True
        GLib.timeout_add(100, cause_drawing)
    
        win.show_all()
        Gtk.main()
    
    if __name__ == '__main__': main()
    
    # A magical conversion function, taken from cairocffi documentation.
    # http://cairocffi.readthedocs.io/en/latest/cffi_api.html#converting-cairocffi-wrappers-to-pycairo
    
    import ctypes
    import cairo  # pycairo
    import cairocffi
    
    pycairo = ctypes.PyDLL(cairo._cairo.__file__)
    pycairo.PycairoContext_FromContext.restype = ctypes.c_void_p
    pycairo.PycairoContext_FromContext.argtypes = 3 * [ctypes.c_void_p]
    ctypes.pythonapi.PyList_Append.argtypes = 2 * [ctypes.c_void_p]
    
    
    def _UNSAFE_cairocffi_context_to_pycairo(cairocffi_context):
        # Sanity check. Continuing with another type would probably segfault.
        if not isinstance(cairocffi_context, cairocffi.Context):
            raise TypeError('Expected a cairocffi.Context, got %r'
                            % cairocffi_context)
    
        # Create a reference for PycairoContext_FromContext to take ownership of.
        cairocffi.cairo.cairo_reference(cairocffi_context._pointer)
        # Casting the pointer to uintptr_t (the integer type as wide as a pointer)
        # gets the context’s integer address.
        # On CPython id(cairo.Context) gives the address to the Context type,
        # as expected by PycairoContext_FromContext.
        address = pycairo.PycairoContext_FromContext(
            int(cairocffi.ffi.cast('uintptr_t', cairocffi_context._pointer)),
            id(cairo.Context),
            None)
        assert address
        # This trick uses Python’s C API
        # to get a reference to a Python object from its address.
        temp_list = []
        assert ctypes.pythonapi.PyList_Append(id(temp_list), address) == 0
        return temp_list[0]
    
    转换\u magic.py

    #!python3.4
    
    from multiprocessing import Process, Pipe
    
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import GLib,Gtk
    
    import cairocffi
    import cairo # also known as pycairo
    from conversion_magic import _UNSAFE_cairocffi_context_to_pycairo
    
    from math import pi
    import array
    
    # Draw a circle on cairocffi context.
    def circle(cr, radius):
        cr.set_source_rgb(1,0,0)
        cr.paint()
        cr.set_source_rgb(0,1,0)
        cr.arc(radius, radius, radius, 0, 2 * pi)
        cr.fill()
    
    # Drawing process
    def example_target(conn):
        while True:
            # Wait for new task.
            radius = conn.recv()
    
            # Create a cairocffi surface, draw on it and send it as bytes.
            surface = cairocffi.ImageSurface(cairo.FORMAT_ARGB32, 100,100)
            cr = cairocffi.Context(surface)
            circle(cr, radius)
            surface.flush()
            data = surface.get_data()
            conn.send(bytes(data))
    
    def main():
        # Create 2 connections for two-way communication between processes.
        parent,child = Pipe()
    
        proc = Process(target=example_target, args=(child,), daemon=True)
        proc.start()
    
        # Tell process to draw circles with radius i.
        for i in range(0,50):
            parent.send(i)
    
        win = Gtk.Window(default_height=300, default_width=300)
        win.connect("delete-event", Gtk.main_quit)
        drawing_area = Gtk.DrawingArea()
        win.add(drawing_area)
    
        def on_draw(widget,cr):
            # Check if we have new images to draw.
            if parent.poll():
                # Convert recieved data into a cairocffi surface. The arguments of
                # the ImageSurface must be the same as those in example_target.
                data = parent.recv()
                a = array.array('b', data)
                cffi_surf = cairocffi.ImageSurface(cairo.FORMAT_ARGB32,
                                                   100,100, data = a)
    
                # Convert cairocffi surface to pycairo surface.
                cffi_cr = cairocffi.Context(cffi_surf)
                pycairo_cr = _UNSAFE_cairocffi_context_to_pycairo(cffi_cr)
                pycairo_surf = pycairo_cr.get_target()
    
                # Draw pycairo surface to the surface from GTK. Using cairocffi
                # surface would not work here, as GTK uses pycairo.
                cr.set_source_surface(pycairo_surf)
                cr.paint()
            else:
                pass
                # TODO: Implement a buffer that holds the last image we got from
                # parent.recv() and draw it. Not included in this example to make
                # things easier.
    
            return True
    
        drawing_area.connect('draw', on_draw)
    
        # Draw new image after each 100ms.
        def cause_drawing():
            drawing_area.queue_draw()
            return True
        GLib.timeout_add(100, cause_drawing)
    
        win.show_all()
        Gtk.main()
    
    if __name__ == '__main__': main()
    
    # A magical conversion function, taken from cairocffi documentation.
    # http://cairocffi.readthedocs.io/en/latest/cffi_api.html#converting-cairocffi-wrappers-to-pycairo
    
    import ctypes
    import cairo  # pycairo
    import cairocffi
    
    pycairo = ctypes.PyDLL(cairo._cairo.__file__)
    pycairo.PycairoContext_FromContext.restype = ctypes.c_void_p
    pycairo.PycairoContext_FromContext.argtypes = 3 * [ctypes.c_void_p]
    ctypes.pythonapi.PyList_Append.argtypes = 2 * [ctypes.c_void_p]
    
    
    def _UNSAFE_cairocffi_context_to_pycairo(cairocffi_context):
        # Sanity check. Continuing with another type would probably segfault.
        if not isinstance(cairocffi_context, cairocffi.Context):
            raise TypeError('Expected a cairocffi.Context, got %r'
                            % cairocffi_context)
    
        # Create a reference for PycairoContext_FromContext to take ownership of.
        cairocffi.cairo.cairo_reference(cairocffi_context._pointer)
        # Casting the pointer to uintptr_t (the integer type as wide as a pointer)
        # gets the context’s integer address.
        # On CPython id(cairo.Context) gives the address to the Context type,
        # as expected by PycairoContext_FromContext.
        address = pycairo.PycairoContext_FromContext(
            int(cairocffi.ffi.cast('uintptr_t', cairocffi_context._pointer)),
            id(cairo.Context),
            None)
        assert address
        # This trick uses Python’s C API
        # to get a reference to a Python object from its address.
        temp_list = []
        assert ctypes.pythonapi.PyList_Append(id(temp_list), address) == 0
        return temp_list[0]
    

    在Windows上使用GTK 3.18.9和Python 3.4进行了测试。

    您的程序是否使用了并行化?它是否显示整体渲染时间或帧速率的任何变化?这取决于您正在绘制的内容有多复杂。当然,在进程之间移动数据的部分有优化的空间,共享内存可能是最快的方式,看起来更新的python多处理将在本机上支持这一点,但可能还有其他方法可以做到这一点。