ctypes Segmentation Fault when binding rsvg - python

I have a simple python ctypes wrapper around rsvg to be used with cairo. It seems to work on ubuntu 16 but when I try it on ubuntu 18 I get a Segmentation error. I have looked at the cairo docs for an example of how this can be done, so nothing fancy.(https://www.cairographics.org/cookbook/librsvgpython/).
from ctypes import CDLL, Structure, byref, c_byte, c_double, c_int, c_void_p
# CDLL: Instances of this class represent loaded shared libraries.
l = CDLL('librsvg-2.so.2')
class rsvgHandle():
class RsvgDimensionData(Structure):
_fields_ = [("width", c_int),
("height", c_int),
("em", c_double),
("ex", c_double)]
class PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
def __init__(self, path):
self.path = path
error = ''
self.handle = l.rsvg_handle_new_from_file(self.path, error)
def get_dimension_data(self):
svgDim = self.RsvgDimensionData()
l.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
return (svgDim.width, svgDim.height)
def render_cairo(self, ctx):
ctx.save()
z = self.PycairoContext.from_address(id(ctx))
l.rsvg_handle_render_cairo(self.handle, z.ctx)
ctx.restore()
class rsvgClass():
def Handle(self, file):
return rsvgHandle(file)
rsvg = rsvgClass()
svg_handle = rsvg.Handle(fpath)
svg_width, svg_height = svg_handle.get_dimension_data()[:2]
svg_handle.render_cairo(context)
The error occurs inside get_dimension_data at the line l.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
The trace isn't too helpful it says Segmenation Fault and a bunch of these
/usr/lib/x86_64-linux-gnu/librsvg-2.so.2(rsvg_handle_get_dimensions+0)
[0x7ff70b01ed50]
/usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_call_unix64+0x4c)
[0x7ff71f52adae]
/usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_call+0x22f) [0x7ff71f52a71f]
/usr/lib/python2.7/lib-dynload/_ctypes.x86_64-linux-gnu.so(_ctypes_callproc+0x2a4)
[0x7ff71f73dcc4]
/usr/lib/python2.7/lib-dynload/_ctypes.x86_64-linux-gnu.so(+0x106c5)
[0x7ff71f73d6c5]
/usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x5bf6) [0x7ff7247eb366]
/usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x8b5b) [0x7ff7247ee2cb]
/usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7d8) [0x7ff72492a908]
/usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x5bf6) [0x7ff7247eb366]
Does anyone know what could be going on in my ubuntu 18 environment to cause this issue ? Or alternatively how I could render an svg into an open cairo context (as shown in the last line of the code) on ubuntu 18 ? (the readymade python binding for rsvg is removed from ubuntu 18 btw which is why im trying ctypes to begin with)

Related

How to install cairo and rsvg for python

I understand that Stackoverflow is for help with code, but I figured I'd ask anyways.
I found from the post here that it is possible to put a .svg file into a tkinter window, but after days of searching, I still can't find any place where I could install cairo and rsvg from.
I am currently using Windows 10 with python 3.6.
EDIT:
I have found out how to install cairo and rsvg. Cairo is working but
rsvg is not. I have managed to put SVGs in Tkinter with cairo and not rsvg, though. For
anyone curious about this, you may want to check post out:
Putting .SVG images into tkinter Frame.
Thanks in advance.
Edit: Ok, so pip won't work for installing pycairo. Found that out.
And the other options haven't worked for me either. I am about to be
away from my computer, but I'll give you some of the things I found.
This
This
and
This
Sorry I couldn't be more help. Hope you figure it out!
First, use pip install pycairo
Unfortunately, rsvg is unavailable for windows, but cairographics.org have a simple wrapper.
Save the following as rsvg.py in the same folder as your script:
#some code to give rsvg.render_cairo(ctx) ability
#on windows.
import os
try:
import rsvg
WINDOWS=False
except ImportError:
print"Warning, could not import 'rsvg'"
if os.name == 'nt':
print "Detected windows, creating rsvg."
#some workarounds for windows
from ctypes import *
l=CDLL('librsvg-2-2.dll')
g=CDLL('libgobject-2.0-0.dll')
g.g_type_init()
class rsvgHandle():
class RsvgDimensionData(Structure):
_fields_ = [("width", c_int),
("height", c_int),
("em",c_double),
("ex",c_double)]
class PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
def __init__(self, path):
self.path = path
error = ''
self.handle = l.rsvg_handle_new_from_file(self.path,error)
def get_dimension_data(self):
svgDim = self.RsvgDimensionData()
l.rsvg_handle_get_dimensions(self.handle,byref(svgDim))
return (svgDim.width,svgDim.height)
def render_cairo(self, ctx):
ctx.save()
z = self.PycairoContext.from_address(id(ctx))
l.rsvg_handle_render_cairo(self.handle, z.ctx)
ctx.restore()
class rsvgClass():
def Handle(self,file):
return rsvgHandle(file)
In your script, do from rsvg import * and when you need to use it, run:
rC = rsvgClass()
h = rC.Handle("YOUR-FILE-HERE.svg")
s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
ctx = cairo.Context(s)
h.render_cairo(ctx)

incorrect virtual memory report using ctypes in windows

i am trying to get the total/available virtual memory in python using ctypes, but it seems to be returning a highly incorrect value. below is the code i currently have
import ctypes
import ctypes.wintypes as wintypes
def _get_virtual_size():
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [("dwLength", wintypes.DWORD),
("dwMemoryLoad", wintypes.DWORD),
("ullTotalPhys", ctypes.c_uint64),
("ullAvailPhys", ctypes.c_uint64),
("ullTotalPageFile", ctypes.c_uint64),
("ullAvailPageFile", ctypes.c_uint64),
("ullTotalVirtual", ctypes.c_uint64),
("ullAvailVirtual", ctypes.c_uint64),
("ullAvailExtendedVirtual", ctypes.c_uint64)]
def __init__(self):
ctypes.Structure.__init__(self)
self.dwLength = ctypes.sizeof(self)
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
GlobalMemoryStatusEx = kernel32.GlobalMemoryStatusEx
GlobalMemoryStatusEx.restype = wintypes.BOOL
GlobalMemoryStatusEx.argtypes = [ctypes.POINTER(MEMORYSTATUSEX)]
status = MEMORYSTATUSEX()
if GlobalMemoryStatusEx(status):
return {'totalPage':status.ullAvailPageFile, 'total':status.ullTotalVirtual, 'available':status.ullAvailVirtual}
return None
print _get_virtual_size()
Result: {'available': 140737380003840L, 'totalPage': 13305126912L, 'total': 140737488224256L}
Note : Using psutil the value seems to be reported correctly, but need a solution without using psutil.
import psutil
print psutil.virtual_memory()
Result: svmem(total=17114841088L, available=13436207104L, percent=21.5, used=3678633984L, free=13436207104L)
How do i get the same values reported by psutil using the ctypes method.
I am currently running the above code in Python 2.7.10 64Bit on Windows 8.1
Any help would be appreciated.
Try using this code. Not sure if it will work for you. I'm running 32-bit Python on a 64-bit windows 7 pc {Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32}. I have compared the values returned to what is reported in the Performance tab of my Windows Task Manager application.
import ctypes
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("ullAvailExtendedVirtual", ctypes.c_ulonglong)
]
def __init__(self):
# have to initialize this to the size of MEMORYSTATUSEX
self.dwLength = ctypes.sizeof(self)
super(MEMORYSTATUSEX, self).__init__()
stat = MEMORYSTATUSEX()
ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
print("MemoryLoad: %d%%" % (stat.dwMemoryLoad))
print . . . #output other fields here

ctypes dll loading is very slow

I am using ctypes to load a dll to control a Measurement Computing MiniLab board. It works, but takes about 5 seconds to load.
Is there a way to make this faster?
The library contains about 100 functions of which I am only using one. Can I maybe tell ctypes to load only that function or something like that?
import ctypes
class DaqInterface(object):
def __init__(self):
self.dll = ctypes.WinDLL("cbw64.dll")
def set_analog(self, data, channel=0, board_num=0):
res = self.dll.cbAOut(ctypes.c_int(board_num), ctypes.c_int(channel), ctypes.c_int(0), ctypes.c_int(data))
if res != 0:
raise RuntimeError("Daq error: {}".format(res))
if __name__ == '__main__':
daq_interface = DaqInterface()
daq_interface.set_analog(512)
The library: Universal Library
The interface: miniLAB 1008

Import error in relation of DLL functions

I'm using python 2.7.2.
i'm using DLL to talk with external hardware , by using the below lines:
main.py
comm_dll = ctypes.cdll.LoadLibrary("extcomm.dll")
ret_val = comm_dll.open(0)
the ret_val is needed in all other DLL functions because several hardware of the same type can be connected to the same PC
the comm_dll is needed in several modules that needs access to this DLL functions
My question is how I make other modules to know comm_dll and ret_val variables
I try to import them from main by import from main comm_dll,ret_val or by using global keyword on both of the variables and then import them
No matter what I do , other modules failed on import statement
I know I can pass these variables to all the functions that uses them , but its seems big overhead
what is the pythonic way to do such import?
Note : ret_val type is ctypes.c_int
CODE
main.py
import ctypes
from drv_spi import *
def main():
comm_dll = ctypes.cdll.LoadLibrary("extcomm.dll")
comm_dll.open.argtypes = [ctypes.c_int]
comm_dll.open.restypes = ctypes.c_int
comm_handle = comm_dll.open(0)
drv_spi_init()
main()
drv_spi.py
import ctypes
def drv_spi_init():
comm_dll.spi_config.argtypes = [ctypes.c_int, ctypes.c_int]
comm_dll.spi_config.restypes = ctypes.c_int
ret_val = comm_dll.spi_config(comm_handle,0x45)
I get an error of NameError: global name 'comm_dll' is not defined
using from main import comm_dll is not working either because main to run again...
It sounds like you should probably implement your hardware device as a class. Something like
class MyHardwareDevice(object):
comm_dll = ctypes.cdll.LoadLibrary("extcomm.dll")
def __init__(self):
pass # or whatever initialization you need
def connect(self, port_number):
self.ret_val = comm_dll.open(port_number)
Then you can use the class like
device = MyHardwareDevice()
device.connect(0)
# your ret_val is now available as device.ret_val

creating an object from a ctype.c_void_pointer

I am doing the following in python
import ctypes, ctypes.util
from gi.repository import WebKit, JSCore, GLib, Gtk
import sys
webkit = ctypes.CDLL(ctypes.util.find_library('webkitgtk-3.0'))
jscore = ctypes.CDLL(ctypes.util.find_library('javascriptcoregtk-3.0'))
def inject_js(view, frame):
"""
void
evalscript(WebKitWebFrame *frame, JSContextRef js, char *script, char* scriptname) {
JSStringRef jsscript, jsscriptname;
JSValueRef exception = NULL;
jsscript = JSStringCreateWithUTF8CString(script);
jsscriptname = JSStringCreateWithUTF8CString(scriptname);
JSEvaluateScript(js, jsscript, JSContextGetGlobalObject(js), jsscriptname, 0, &exception);
JSStringRelease(jsscript);
JSStringRelease(jsscriptname);
}
"""
offset = sys.getsizeof(object())
frame = ctypes.POINTER(ctypes.c_void_p).from_address(id(frame) + offset)
adr = webkit.webkit_web_frame_get_global_context(c_frame)
js = ctypes.cast(js_ctx_adr, ctypes.c_void_p)
js_objref_adr = jscore.JSContextGetGlobalObject(js_ctx_ref) #segfaults here
window = Gtk.Window()
view = WebKit.WebView()
window.add(view)
window.show_all()
view.connect('document-load-finished', inject_js)
view.load_uri("http://google.com")
mainloop = GLib.MainLoop()
mainloop.run()
I am trying to use ctypes to access a non-introspectable method, so far I am successful in creating a pointer to gtk/gobject stuff. However the js intance I am trying to cast should not be a pointer but rather the object itself, or something similar.
==> WebKitWebFrame *frame, JSContextRef js (not a pointer)
How can I do that. Right now it just segfaults
The argument types and return type need to be setup explicitly on ctypes functions. ctypes uses a default return type of "C int", which is likely why the segfault is occurring. See: Specifying the required argument types
jscore.JSContextGetGlobalObject.argtypes = [ctypes.c_void_p]
jscore.JSContextGetGlobalObject.restype = ctypes.c_void_p
webkit.webkit_web_frame_get_global_context.argtypes = [ctypes.c_void_p]
webkit.webkit_web_frame_get_global_context.restype = ctypes.c_void_p
JSContextRef and JSGlobalContextRef are typedefs to opaque struct pointers, so using c_void_p can work as the argument type: JavaScriptCore/API/JSBase.h
I think the use of sys.getsizeof(object()) and from_address is ok. It is used in the PyGObject unittests because it ensures the code will run correctly with debug builds of Python (where the PyObject struct has some extra fields and is of a different size). See: git.gnome.org/browse/pygobject/tree/tests/test_everything.py?id=3.9.90#n36
As a side note, PyGObject exposes a pointer to the underlying GObject as a PyCapsule via the attribute "__gpointer__". Unfortunately, this is not very useful because ctypes does not marshal the pointer held in PyCapsules automatically, nor does the pointer address seem accessible on the PyCapsule in Python.
With the argtypes/restype setup mentioned (and variable name fixes), the callback no longer segfaults:
def inject_js(view, frame):
offset = sys.getsizeof(object())
c_frame = ctypes.c_void_p.from_address(id(frame) + offset)
js_ctx_ptr = webkit.webkit_web_frame_get_global_context(c_frame)
js_obj_ptr = jscore.JSContextGetGlobalObject(js_ctx_ptr)

Categories