I have an application written in Python using GTK3 through the GObject introspection (Python 2.7 and PyGObject 3.14). I am trying to load a web page using WebKit and access the contents of all the resources it loads. I'm able to accomplish this by connecting to the resource-load-finished signal of the WebKitWebView object I am using to load the page.
Within my signal handler I use the WebKitWebResource object in the web_resource parameter to access the loaded data. Everything works fine with the GLib.GString returned from get_data() when it does not contain a NULL byte, I can access what I need using data.str. However when the data does contain a NULL byte, which is often the case when the MIME type of the loaded resource is an image, data.len is correct but data.str only contains the data up to the first NULL byte. I can access the raw bytes by calling data.free_to_bytes() which returns a GLib.GBytes instance, however when the signal handler returns the application segfaults. I'm trying to access all the data within the loaded resource.
I hope the following code helps demonstrate the issue.
from gi.repository import Gtk
from gi.repository import WebKit
def signal_resource_load_finished(webview, frame, resource):
gstring = resource.get_data()
print(resource.get_mime_type())
desired_len = gstring.len
# gstring.str is missing data because it returns the data up to the first NULL byte
assert(gstring.str == desired_len) # this assertion fails
# calling this causes a segfault after the handler returns, but the data is accessible from gbytes.get_data()
#gbytes = gstring.free_to_bytes()
#assert(len(gbytes.get_data()) == desired_len) # this assertion succeeds before the segfault
return
webview = WebKit.WebView()
webview.connect('resource-load-finished', signal_resource_load_finished)
webview.connect('load-finished', Gtk.main_quit)
# lol cat for demo purposes of a resource containing NULL bytes (mime type: image/png)
webview.load_uri('http://images5.fanpop.com/image/photos/30600000/-Magical-Kitty-lol-cats-30656645-1280-800.png')
Gtk.main()
You don't want to use free_to_bytes as this will not only give you the bytes you want, but also release the string from memory without Python knowing about it - which, as you discovered, crashes your program. Unfortunately there isn't a corresponding get_bytes method as GLib.String wasn't really designed to hold binary data.
In fact I'd consider it a mistake in the WebKit API that the resource payload is only available as a GLib.String. They seem to have corrected this mistake in WebKit2: http://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebResource.html
Consider switching to WebKit2 if you can (from gi.repository import WebKit2).
Related
Hi during the creation of multiple objects in pygtk application, calling some of these object return that they are uninitialised when passing there contents to signals (noting the value are showen correctly in the application)
sections = config.sections()
for section in sections:
box= gtk.Table (3,len(config.options(section)),False)
box.set_col_spacings(2)
box.set_row_spacings(2)
box.show()
label = gtk.Label(section)
label.show()
notebook.append_page (box,label)
for i,option in enumerate(config.options(section)):
optionlabel = gtk.Label(option)
optionvalue = gtk.Entry ()
optionvalue.set_text(config.get(section,option))
--> optionvalue.connect("activate", enter_callback,optionvalue, label, optionlabel)
box.attach(optionlabel,0,1,i,i+1,xoptions=gtk.SHRINK|gtk.FILL,yoptions=gtk.SHRINK)
box.attach(optionvalue,1,2,i,i+1,yoptions=gtk.SHRINK)
box.show_all()
at first I thought that the variables are not in scope but they were ( I've tested several objects and found them working the last 3 lines
Linuxcnc ini.py:70: GtkWarning: IA__gtk_entry_get_text: assertion "GTK_IS_ENTRY (entry) failed
print (widget.get_text())
None
<gtk.Label object at 0x9f44a54 (uninitialized at 0x0)>
<gtk.Label object at 0x9f44c0c (uninitialized at 0x0)>
0
<gtk.Label object at 0x9f4c8ec (GtkLabel at 0xa1a3350)>
<gtk.Label object at 0x9f4dc0c (GtkLabel at 0xa1a3450)>
For this one I think you may need to look in the Gtk system library itself.
(The source code that is used to produce ".so", ".dll" or ".dylib" file, that has the routines that your Python program is accessing).
You can look a the contents of such binary files with commands like "strings", "nm", and "objdump".
But based the nature of the error, it looks like the fix will be done in the source code of the library itself, not the Python program.
Sorry I cannot give more detail than that.
I'm working on a little program that uses libsecret. This program should be able to create a Secret.Service...
from gi.repository import Secret
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
... get a specific Collection from that Service...
# 2 is the index of a custom collection I created, not the default one.
collection = service.get_collections()[2]
... and then list all the Items inside that Collection, this by just printing their labels.
# I'm just printing a single label here for testing, I'd need all of course.
print(collection.get_items()[0].get_label())
An important detail is that the Collecction may initially be locked, and so I need to include code that checks for that possibility, and tries to unlock the Collection.
# The unlock method returns a tuple (number_of_objs_unlocked, [list_of_objs])
collection = service.unlock_sync([collection])[1][0]
This is important because the code I currently have can do all I need when the Collection is initially unlocked. However if the Collection is initially locked, even after I unlock it, I can't get the labels from the Items inside. What I can do is disconnect() the Service, recreate the Service again, get the now unlocked Collection, and this way I am able to read the label on each Item. Another interesting detail is that, after the labels are read once, I no longer required the Service reconnection to access them. This seems quite inelegant, so I started looking for a different solution.
I realized that the Collection inherited from Gio.DBusProxy and this class caches the data from the object it accesses. So I'm assuming that is the problem for me, I'm not updating the cache. This is strange though because the documentation states that Gio.DBusProxy should be able to detect changes on the original object, but that's not happening.
Now I don't know how to update the cache on that class. I've taken a look at some seahorse(another application that uses libsecret) vala code, which I wasn't able to completely decipher, I can't code vala, but that mentioned the Object.emit() method, I'm still not sure how I could use that method to achieve my goal. From the documentation(https://lazka.github.io/pgi-docs/Secret-1/#) I found another promising method, Object.notify(), which seems to be able to send notifications of changes that would enable cache updates, but I also haven't been able to properly use it yet.
I also posted on the gnome-keyring mailing list about this...
https://mail.gnome.org/archives/gnome-keyring-list/2015-November/msg00000.html
... with no answer so far, and found a bugzilla report on gnome.org that mentions this issue...
https://bugzilla.gnome.org/show_bug.cgi?id=747359
... with no solution so far(7 months) either.
So if someone could shine some light on this problem that would be great. Otherwise some inelegant code will unfortunately find it's way into my little program.
Edit-0:
Here is some code to replicate the issue in Python3.
This snippet creates a collection 'test_col', with one item 'test_item', and locks the collection. Note libsecret will prompt you for the password you want for this new collection:
#!/usr/bin/env python3
from gi import require_version
require_version('Secret', '1')
from gi.repository import Secret
# Create schema
args = ['com.idlecore.test.schema']
args += [Secret.SchemaFlags.NONE]
args += [{'service': Secret.SchemaAttributeType.STRING,
'username': Secret.SchemaAttributeType.STRING}]
schema = Secret.Schema.new(*args)
# Create 'test_col' collection
flags = Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE
collection = Secret.Collection.create_sync(None, 'test_col', None, flags, None)
# Create item 'test_item' inside collection 'test_col'
attributes = {'service': 'stackoverflow', 'username': 'xor'}
password = 'password123'
value = Secret.Value(password, len(password), 'text/plain')
flags = Secret.ItemCreateFlags.NONE
Secret.Item.create_sync(collection, schema, attributes,
'test_item', value, flags, None)
# Lock collection
service = collection.get_service()
service.lock_sync([collection])
Then we need to restart the gnome-keyring-daemon, you can just logout and back in or use the command line:
gnome-keyrin-daemon --replace
This will setup your keyring so we can try opening a collection that is initially locked. We can do that with this code snippet. Note that you will be prompted once again for the password you set previously:
#!/usr/bin/env python3
from gi import require_version
require_version('Secret', '1')
from gi.repository import Secret
# Get the service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Find the correct collection
for c in service.get_collections():
if c.get_label() == 'test_col':
collection = c
break
# Unlock the collection and show the item label, note that it's empty.
collection = service.unlock_sync([collection])[1][0]
print('Item Label:', collection.get_items()[0].get_label())
# Do the same thing again, and it works.
# It's necessary to disconnect the service to clear the cache,
# Otherwise we keep getting the same empty label.
service.disconnect()
# Get the service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Find the correct collection
for c in service.get_collections():
if c.get_label() == 'test_col':
collection = c
break
# No need to unlock again, just show the item label
print('Item Label:', collection.get_items()[0].get_label())
This code attempts to read the item label twice. One the normal way, which fails, you should see an empty string, and then using a workaround, that disconnects the service and reconnects again.
I came across this question while trying to update a script I use to retrieve passwords from my desktop on my laptop, and vice versa.
The clue was in the documentation for Secret.ServiceFlagsāthere are two:
OPEN_SESSION = 2
establish a session for transfer of secrets while initializing the Secret.Service
LOAD_COLLECTIONS = 4
load collections while initializing the Secret.Service
I think for a Service that both loads collections and allows transfer of secrets (including item labels) from those collections, we need to use both flags.
The following code (similar to your mailing list post, but without a temporary collection set up for debugging) seems to work. It gives me the label of an item:
from gi.repository import Secret
service = Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION |
Secret.ServiceFlags.LOAD_COLLECTIONS)
collections = service.get_collections()
unlocked_collection = service.unlock_sync([collections[0]], None)[1][0]
unlocked_collection.get_items()[0].get_label()
I have been doing this
print(collection.get_locked())
if collection.get_locked():
service.unlock_sync(collection)
Don't know if it is going to work though because I have never hit a case where I have something that is locked. If you have a piece of sample code where I can create a locked instance of a collection then maybe I can help
I am currently trying to make a python app do the same thing as a VB app.
The app retrieves an image from an external device over the COM API. I've used win32com and managed to use the Dispatch mechanism to talk with the COM API.
In the VB app, the image is stored like this
pictureData = objResult.Properties('ResultImage')
myImage = AxHost.GetPictureFromIPicture(pictureData)
In my Python app, however on the picture data member, I get PyIUnknown object type. How do I get the image data out of this 'unknown' object?
Let me add that for other 'basic' data like strings, I can see them fine over the Python app.
I found a method that works, maybe not perfect, but if someone Google's this question, I hope it helps.
from win32com.client import Dispatch
import pythoncom
imagedata = data.Properties.Item('ResultImage') #this is samas the VB line in the question , except I have to add 'Item' here
#win32com does not define IPicture type (http://timgolden.me.uk/pywin32-docs/com_objects.html)
ipicture = Dispatch(imagedata.QueryInterface(pythoncom.IID_IDispatch))
import win32ui
import win32gui
#creates a PyCBitMap object
imagebitmap = win32ui.CreateBitmapFromHandle(ipicture.Handle)
#we need a DC handle to save the bitmap file for some reason...
dc_handler = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
#this overwrites any older file without asking
imagebitmap.SaveBitmapFile(dc_handler,'last_pic.bmp')
Questions I still have in my mind are:
are we only forced to interact with IPicture or any non-defined COM (by win32com) in a dynamic way? Is there an elegant way to extend the definitions of interfaces etc.?
I tried using QueryInterface(pythontypes.IID('{7BF80980-BF32-101A-8BBB-00AA00300CAB}'). I got this IID from http://msdn.microsoft.com/en-us/library/windows/desktop/ms680761(v=vs.85).aspx however I got an error within Python that this interface cannot be handled.
Not sure why I cannot use the methods defined for IPicture, but I could access the attributes?
I tried simply to use ipicture.get_Handle() or ipicture.SaveAsFile() but these didn't work.
OK, so in the following code
import win32api
import win32gui
hwnd = win32api.ShellExecute(None, "open", "notepad.exe", "test.txt", None, 6)
rect = win32gui.GetWindowRect(hwnd)
I open Notepad successfully and receive a return value >32 as to indicate the success of the execution. In the docs: http://timgolden.me.uk/pywin32-docs/win32api__ShellExecute_meth.html
The return value is specified as an instance handle, so I expected to be able to use this handle as the paramenter for the GetWindowRect call. Docs: http://timgolden.me.uk/pywin32-docs/win32gui__GetWindowRect_meth.html
In my debugger I can see that hwnd equal an {long}42, and my GetWindowRect call returns a error 1400, Invalid window handle.
So why is the handle wrong, and how can I get a usable handle?
According to Microsoft's documentation the return value is of type HINSTANCE but it is not a true instance and can only be used to compared against various error codes. Historically in 16 bit windows an instance handle was used to identify a particular executable or DLL instance but even then that was not the same as a window handle.
Return value
Type: HINSTANCE
If the function succeeds, it returns a value greater
than 32. If the function fails, it returns an error value that
indicates the cause of the failure. The return value is cast as an
HINSTANCE for backward compatibility with 16-bit Windows applications.
It is not a true HINSTANCE, however. It can be cast only to an int and
compared to either 32 or the following error codes below.
So far as I know, the best way to get a usable window handle is to iterate over the top level windows in the system until you find one with the expected class and title.
Here's a code extract based on something I wrote years ago that looks for windows with matching title and class:
from win32gui import EnumWindows, GetClassName
from win32ui import CreateWindowFromHandle
def toplevelWindows(s, klass):
res = []
def callback(hwnd, arg):
name = GetClassName(hwnd)
w = CreateWindowFromHandle(hwnd)
title = w.GetWindowText()
if s in title or name==klass:
res.append(w)
EnumWindows(callback, 0)
return res
I'm trying to create a python program (using pyUNO ) to make some changes on a OpenOffice calc sheet.
I've launched previously OpenOffice on "accept" mode to be able to connect from an external program. Apparently, should be as easy as:
import uno
# get the uno component context from the PyUNO runtime
localContext = uno.getComponentContext()
# create the UnoUrlResolver
resolver = localContext.ServiceManager.createInstanceWithContext(
"com.sun.star.bridge.UnoUrlResolver", localContext)
# connect to the running office
ctx = resolver.resolve("uno:socket,host=localhost,port=2002;"
"urp;StarOffice.ComponentContext")
smgr = ctx.ServiceManager
# get the central desktop object
DESKTOP =smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
#The calling it's not exactly this way, just to simplify the code
DESKTOP.loadComponentFromURL('file.ods')
But I get an AttributeError when I try to access loadComponentFromURL. If I make a dir(DESKTOP), I've see only the following attributes/methods:
['ActiveFrame', 'DispatchRecorderSupplier', 'ImplementationId', 'ImplementationName',
'IsPlugged', 'PropertySetInfo', 'SupportedServiceNames', 'SuspendQuickstartVeto',
'Title', 'Types', 'addEventListener', 'addPropertyChangeListener',
'addVetoableChangeListener', 'dispose', 'disposing', 'getImplementationId',
'getImplementationName', 'getPropertySetInfo', 'getPropertyValue',
'getSupportedServiceNames', 'getTypes', 'handle', 'queryInterface',
'removeEventListener', 'removePropertyChangeListener', 'removeVetoableChangeListener',
'setPropertyValue', 'supportsService']
I've read that there are where a bug doing the same, but on OpenOffice 3.0 (I'm using OpenOffice 3.1 over Red Hat5.3). I've tried to use the workaround stated here, but they don't seems to be working.
Any ideas?
It has been a long time since I did anything with PyUNO, but looking at the code that worked last time I ran it back in '06, I did my load document like this:
def urlify(path):
return uno.systemPathToFileUrl(os.path.realpath(path))
desktop.loadComponentFromURL(
urlify(tempfilename), "_blank", 0, ())
Your example is a simplified version, and I'm not sure if you've removed the extra arguments intentionally or not intentionally.
If loadComponentFromURL isn't there, then the API has changed or there's something else wrong, I've read through your code and it looks like you're doing all the same things I have.
I don't believe that the dir() of the methods on the desktop object will be useful, as I think there's a __getattr__ method being used to proxy through the requests, and all the methods you've printed out are utility methods used for the stand-in object for the com.sun.star.frame.Desktop.
I think perhaps the failure could be that there's no method named loadComponentFromURL that has exactly 1 argument. Perhaps giving the 4 argument version will result in the method being found and used. This could simply be an impedance mismatch between Python and Java, where Java has call-signature method overloading.
This looks like issue 90701: http://www.openoffice.org/issues/show_bug.cgi?id=90701
See also http://piiis.blogspot.com/2008/10/pyuno-broken-in-ooo-30-with-system.html and http://udk.openoffice.org/python/python-bridge.html