For reference, @JonB has opened an issue for this feature on the rubicon.objc issue tracker: https://github.com/beeware/rubicon-objc/issues/181
Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Posts made by dgelessus
-
RE: rubicon.objc’s problem
-
RE: help about return type
Sorry for the late reply - I forgot to check the forum recently... The version of ctypes_patch that @JonB linked to is the original version from the pull request that added it. There have been some updates to it that fix a few problems, including the "The ctype {} already has a getfunc" error that @ryubai posted above. I would recommend using the current version of ctypes_patch from the repo and not the old one from the pull request.
-
RE: Is there an equivalent to objc_util, but for Swift?
I'm also not aware of any existing project that allows calling Swift from Python. As others have said above, the other direction (calling Python from Swift) is quite well-supported, but that doesn't help us much here.
In principle, it is entirely possible to call Swift from Python. It would require more work than with Objective-C though.
Objective-C's calling conventions are completely compatible with C, which allows using the standard
ctypes
library to call Objective-C code. The Objective-C API also exposes a lot of information at runtime, which allows inferring method return/argument types so you don't have to set them manually in most cases.With Swift the situation is quite different. As far as I know, Swift itself does not provide nearly as many runtime API functions as Objective-C does, so there is no easy way to get function/method type information at runtime. Swift might not even store this type information in the compiled binaries at all (I haven't checked recently).
There's also the more fundamental problem that Swift's type system and calling convention are completely different from C. In some simple cases the calling conventions overlap enough that you can call some Swift functions using
ctypes
, but IIRC all instance methods (anything with aself
) and functions/methods with certain complex return types (tuples, etc.) are incompatible with the C calling convention.This means that calling Swift from Python would require a custom extension module similar to
ctypes
, but for the Swift calling convention. Internally,ctypes
uses the libffi library, but libffi does not support the Swift ABI (yet?). In any case, since a custom native module is required, a library like this wouldn't be usable in Pythonista unless @omz compiles and includes it in the app.TLDR: It would be possible, but would require quite a bit of work, and couldn't be developed in Pythonista.
-
RE: how can i access dispatch_get_main_queue
If I'm reading the error message correctly,
rubicon.objc
is installed properly. The error aboutctypes.util
/ctypes.macholib
is a known issue in Pythonista: https://github.com/omz/Pythonista-Issues/issues/311Sadly it hasn't been fixed yet, but there is a workaround - you can copy the missing files from the CPython source code. See this comment on the issue for instructions: https://github.com/omz/Pythonista-Issues/issues/311#issuecomment-398715503
-
RE: how can i access dispatch_get_main_queue
The second argument of
dispatch_async
needs to be a block object. You can't pass in a normal Python function, becausectypes
won't know how to convert it. Instead, you have to create the block object manually:def block_code(_self): print("ok") block = ObjCBlock(block_code, None, [c_void_p]) dispatch_async(dispatch_get_main_queue(), byref(block._as_parameter_))
For more details about how
ObjCBlock
works, you can look at theObjCBlock
section of theobjc_util
documentation.By the way,
dispatch_async
doesn't return anything, so you should set itsrestype
toNone
and notc_void_p
. (If you usec_void_p
it will probably still work, but the return value won't be anything useful.) -
RE: how can i access dispatch_get_main_queue
dispatch_get_main_queue
is not a normal C function, so you can't access it normally viactypes
/objc_util
. Instead, you need to reproducedispatch_get_main_queue
's functionality in Python using some custom code:from ctypes import Structure, byref, cast, c_void_p from objc_util import ObjCInstance, c class struct_dispatch_queue_s(Structure): pass _dispatch_main_q = struct_dispatch_queue_s.in_dll(c, "_dispatch_main_q") def dispatch_get_main_queue(): return ObjCInstance(cast(byref(_dispatch_main_q), c_void_p))
You don't need to understand what this code does exactly - you can just copy-paste it into your code and use
dispatch_get_main_queue()
like you would normally.(If anyone is interested in the exact details of what the above code does and where it comes from, see this page. The original C code is very complex, so you need to be familiar with both
ctypes
and the C preprocessor to understand all the translation steps. But again, you don't have to understand all of this - you can just copy-paste and use the finished Python code.) -
RE: Help finding the value of an objc string constant...
@shinyformica Correct - these string constants are actually global variables of type
NSString *
, which means that they contain a pointer to the actual string object.To read the pointer from the global variable, you need to use
ctypes.c_void_p.in_dll(objc_util.c, "ConstantName")
, as @mikael already found above.objc_util.c.ConstantName
will "work" (it doesn't crash), but that treatsConstantName
as a function and not a variable, so it returns a function pointer (which will crash when you try to call it, because normal variables are not valid functions).The value returned by
in_dll
is a regular Objective-C object pointer (which points to theNSString
object), but as ac_void_p
object, so you need to cast it toObjCInstance
manually so that you can use it. Once you do that, you have a regularNSString
object with the value you're looking for.In short:
>>> objc_util.ObjCInstance(ctypes.c_void_p.in_dll(objc_util.c, 'UICollectionElementKindSectionHeader')) <b'__NSCFConstantString': UICollectionElementKindSectionHeader> >>> objc_util.ObjCInstance(ctypes.c_void_p.in_dll(objc_util.c, 'UICollectionElementKindSectionFooter')) <b'__NSCFConstantString': UICollectionElementKindSectionFooter>
Strangely, the value of both string constants is literally their name, so the approach suggested by @mikael (just pass the name of the constant as a string) should have worked. Maybe UIKit expects you to pass in the exact pointer, and not just any string with the same contents? (That would be quite strange though.)
(By the way, please do @-mention me if there's something where you think I can help. Sometimes I only skim the forums very quickly, so I might miss some questions by accident.)
-
RE: iPhone shortcut script
This is probably not possible.
In general, iOS does not allow one app to control another app. Apps can provide Siri shortcuts or a URL scheme to allow limited control by other apps, but this has to be specifically supported by the app, and the developer decides what actions can be triggered by other apps.
If the Sonos app provides Siri shortcuts or a URL scheme to control music playback, you can use that, otherwise you're out of luck. It doesn't make a difference whether you use Pythonista or the Shortcuts app to do the automation - both apps only have access to the control endpoints provided by the other app, and nothing more.
-
RE: Need help for CFURLRef in Objectivec
From an Objective-C perspective,
CFURLRef
is basically identical toNSURL *
, because of "toll-free bridging" between Core Foundation and Objective-C/Foundation. (This also applies to other CF types, likeCFStringRef
/NSString *
,CFArrayRef
/NSArray *
, etc.) So you should be able to create aNSURL
from a string normally (usingobjc_util.nsurl
) and pass theObjCInstance
into the parameter that expects aCFURLRef
. -
RE: Calling superclass method in objc_util subclass?
@mikael I'm not very creative with these things - do you have some example code based on
objc_util
that I could translate to Rubicon?In general the syntax for classes looks like this:
from rubicon.objc import NSInteger, NSObject, ObjCProtocol, objc_method WhateverDelegate = ObjCProtocol("WhateverDelegate") class MyCustomDelegate(NSObject, protocols=[WhateverDelegate]): # the protocols=[...] part is optional of course, if you don't need to implement any protocol @objc_method def someThing_didAThingWithCount_(self, something, count: NSInteger) -> None: print(something, count) something.delegate = MyCustomDelegate.new()
The Python 3 type annotation syntax is used to set each method's return and argument types. You use regular
ctypes
type objects for the types, and Rubicon predefines some common typedefs likeNSInteger
. Anything you don't annotate is assumed to be an Objective-C object (ObjCInstance
).Blocks are similar:
from rubicon.objc import Block, NSInteger @Block def thing_callback(number: NSInteger) -> None: print(number) whatever.doThing("abc", withCallback=callback)
-
RE: Calling superclass method in objc_util subclass?
There should be no problems with using both Rubicon and
objc_util
at the same time, they don't interfere with each other. As usual, there's no proper compatibility between the two libraries, so for example to convert betweenobjc_util.ObjCInstance
andrubicon.objc.ObjCInstance
, you have to cast toc_void_p
in between. Anything on the ObjC side will interact properly though, so you can define an ObjC class using Rubicon and then look it up usingobjc_util
'sObjCClass
.I'd say the main advantages of Rubicon right now are really what @JonB has already said above: the class definition syntax (it's just much more convenient to use the standard Python syntax) and the improved type handling (almost all type encodings are predefined or handled automatically, and when you do need custom structure types, they only need to be "registered" once instead of having to set
restype
/argtypes
on every call). There are also a few other features that are not particularly exciting individually, but they are nice to have:- Slightly better Python interaction for standard ObjC types. Like with
objc_util
you can useobj[key]
forNSArray
andNSDictionary
, but you also get Python-style methods (append
,extend
, etc. forNSArray
anditems
,values
, etc. forNSDictionary
).NSString
also gets operators/methods similar to Pythonstr
, so you often don't need to explicitly convertNSString
s to/fromstr
. isinstance
andissubclass
work with ObjC classes - you can doisinstance(thing, NSString)
instead ofthing.isKindOfClass(NSString)
.- Class definitions can have properties and ivars (though you don't need this very often in practice).
- You can define custom protocols, just like classes (though you don't need this very often in practice, either).
- Calling low-level ObjC runtime functions is more convenient - there are proper classes for
objc_id
,Class
,SEL
, etc. instead of justc_void_p
. - You can return structs from
ctypes
callbacks (CFUNCTYPE
) and from things that use callbacks internally (custom ObjC methods and blocks), which is normally not possible because of actypes
limitation. - Rubicon is not specific to Pythonista, so Rubicon-based code can be reused in Pyto (which has Rubicon bundled as its
objc_util
equivalent) or even in a standalone app.
Of course there's also the disadvantage that Rubicon is not installed by default in Pythonista, so to run Rubicon-based code you need to install Rubicon first (but this should be easy to do using Stash
pip
, because Rubicon is on PyPI). - Slightly better Python interaction for standard ObjC types. Like with
-
RE: Calling superclass method in objc_util subclass?
@mikael Rubicon runs without changes on Pythonista, as far as I know. The main use case for Rubicon is writing Python-based apps using BeeWare's Python-Apple-support, but the Python environment from that build is very similar to Pythonista (I think Pythonista even uses some of the patches from that repo), so Rubicon generally works in Pythonista just as it would in a standalone app.
If any parts of Rubicon don't work in Pythonista, please open an issue on the Rubicon repo. Although Pythonista isn't officially supported, I try to keep it as compatible as possible (Pythonista is my usual environment for testing/debugging Rubicon on iOS). Also, nearly all Rubicon bugs that appear in Pythonista also affect standalone iOS environments, so fixing them in Rubicon is better for everyone.
Though I want to point out a few things that you should know if you want to switch to Rubicon:
- Rubicon's API is not directly compatible with
objc_util
, so you can't just doimport rubicon.objc as objc_util
in your existing code. The API isn't that different though, for the most part you only need to change some function names and rewrite classes to the new syntax.- You could probably even write a "compatibility module" that has the same API as
objc_util
, but calls Rubicon internally. I wanted to try that at some point, but haven't gotten around to it.
- You could probably even write a "compatibility module" that has the same API as
- Rubicon doesn't integrate with other Pythonista modules like
objc_util
does.ui.View().objc_instance
will still return anObjCInstance
fromobjc_util
, and with Rubicon you can't doObjCInstance(ui.View())
(you have to writeObjCInstance(ui.View()._objc_ptr)
instead).- This could probably be added at runtime using monkey-patching though.
- Rubicon isn't very convenient to use in the REPL yet, you don't get code completion suggestions for ObjC methods (I think there's no proper introspection support at all right now). So at the moment
objc_util
is still better for exploring and debugging interactively. - The documentation is not quite complete yet, though I have a work in progress PR that significantly expands the docs. I really need to finish that at some point.
- Rubicon's API is not directly compatible with
-
RE: Issues of Keyboard
@pulbrich FYI, this is a spam thread. The spambot copied the text from an old forum thread (https://forum.omz-software.com/topic/3789/keyboard-issues) and added some spam links in the middle.
The first reply (the one above yours) is also very likely another spammer that is trying to give the thread more visibility. The account that made the reply is new and hasn't made any other posts on the forum, and the reply text is extremely generic (it doesn't mention any specific details about the problem - this is so that the same message can be copied into threads about different topics).
There's sadly not much that we can do about this as normal forum users. I've already reported both posts, but @omz is the only admin/moderator on the forum and he isn't very active currently, so these spam threads often take a few days to get deleted.
-
RE: About using CFFI in Pythonista (and my CFFI-based libraries)
There are also a couple of CFFI-based libraries that I wrote, which have been linked in the other post. It seems that I never made a proper post on the forums about them, so I'll do that here:
The first one is cffipp (found in my pythonista-c-utils repo), which adds a proper C preprocessor to CFFI. By default CFFI's C parsing understands almost no C preprocessing features (only simple constant
#define
s are supported). This library adds support for all important C preprocessor directives and features, which makes it possible to load standard iOS headers into CFFI (as long as they are pure C, such as Core Foundation). The repo includes patched versions of a few headers, to make them compatible with CFFI and improve parsing performance.Unfortunately, cffipp is now quite outdated - I wrote it three years ago based on CFFI 1.6 and the headers for iOS 9. The CFFI and header patches include a lot of copied code, so updating it to newer CFFI and iOS SDK versions would take a bit of work. If anyone is interested in using this, let me know, I might be able to get it updated.
The second library is objc-cffi, which is basically a CFFI-based version of
objc_util
with a few extra convenience features. This library is fairly incomplete - although it has a few features thatobjc_util
doesn't (introspection, better automatic type detection for methods) it's missing some very important features for practical use (class definitions, block support, collection syntax).I also found out quite soon that CFFI is not well suited as the base for this library. To interact with the Objective-C runtime I need to create a lot of types dynamically, which is very slow with CFFI, because every type needs to be constructed as C source code and then parsed by CFFI. I also ran into name collision issues, because every FFI object has a single namespace and does not allow multiple definitions (the Objective-C runtime sometimes returns different type information for the same type, depending on context).
Around this time I also started contributing to rubicon-objc, which has also been mentioned a few times on this forum - it's basically
objc_util
, but not Pythonista-specific (also works on Mac and other platforms with Objective-C). I've ported most of objc-cffi's convenience features over to rubicon-objc now, and I'm still working on improving rubicon-objc, so I would definitely recommend that over objc-cffi. -
About using CFFI in Pythonista (and my CFFI-based libraries)
This is in response to the questions/replies from @cvp and @JonB in https://forum.omz-software.com/topic/5660/install-pymunk-on-pythonista-get-cffi-error about how to use CFFI in Pythonista. I'm putting it in a separate thread since it's not really relevant to the original question (getting a specific library working in Pythonista).
For context: CFFI is a library for calling C code from Python. It has a similar purpose as ctypes, with one big difference being that CFFI lets you write type and function declarations using standard C syntax, without having to "translate" them to another syntax/API first as with ctypes.
By default, CFFI uses its own custom C extension to call C code, and also has various features that make use of a proper C preprocessor/compiler. Of course this doesn't work by default on Pythonista, since there is no C toolchain and you can't compile or load custom extension modules. However, in addition to the default C extension backend, CFFI also includes a ctypes-based pure-Python backend (
cffi.backend_ctypes.CTypesBackend
), which works in Pythonista, with no modifications needed to CFFI itself.The installation process is very straightforward: you download the source and copy the
cffi
package intosite-packages
, as with any other module. We can safely ignore the C parts, since we don't use them. (You can probably also install CFFI via Stash, but I haven't tried it.) Once it's installed, you can create anFFI
object with the ctypes backend like this:import cffi.backend_ctypes ffi = cffi.FFI(backend=cffi.backend_ctypes.CTypesBackend())
After that, you can use the
ffi
object as you normally would - see the CFFI docs. Note that the docs sometimes refer to different modes of operation for CFFI (ABI/API and in-line/out-of-line). Only in-line ABI mode can be used with the ctypes backend in Pythonista, all other modes require a working C compiler.Unfortunately, this often doesn't help with getting CFFI-based libraries to work on Pythonista - in many cases these libraries rely on third-party or custom C code that isn't available in Pythonista. CFFI is simply the interface between Python and C, so even if you have CFFI working it won't be of any use without the C libraries that you want to call.
That's not to say that CFFI is of no use in Pythonista. Almost everything that you can do with ctypes can also be done with CFFI, and usually with a much nicer and more convenient API, which makes it very useful for calling C APIs (Unix/iOS and CPython).
-
RE: python3.7.2 and python 3.6.8 has been released
@techyogi The current release of Pythonista (version 3.2) includes Python 3.6.
-
RE: Please Help: extended keyboard second row is missing !
@T451f The bottom row of Pythonista's extended keyboard uses the system standard shortcut bar (where in other apps you have buttons for undo, redo, cut, copy, paste). Check in your iOS keyboard settings (in the Settings app, not in Pythonista) that you have the shortcut bar enabled.
@cvp I think the second keyboard row is only available on old iOS versions (iOS 10 and below IIRC) because of technical reasons. If your iOS version is newer than that, you can't use the second keyboard row.
-
RE: How can I transfer folder from Ver 3 to Ver 2
You can use the following commands in the Pythonista 3 Python console to copy the folder back:
>>> import os, shutil >>> shutil.copytree(os.path.expanduser("~/Documents/foldername"), os.path.expanduser("~/../Documents/foldername"))
where
foldername
is the name of the folder you want to copy (assuming that it's in the root of Pythonista 3's local files). You can also useshutil.move
instead ofshutil.copytree
if you want to move the folder rather than copy it (which may be a good idea if it's very big).By the way, is there any reason in particular why you're still using Pythonista 2? Pythonista 3 supports both Python 2 and 3, so there's generally no need for Pythonista 2 if you have Pythonista 3. In fact Pythonista 2 can no longer be bought on the App Store and won't receive any more updates.
-
RE: Tweet.py example
The
twitter
module no longer works on recent iOS versions, because iOS no longer has any special native support for Twitter. Quoting the in-app docs for thetwitter
module:Warning: Unfortunately, this module no longer works on iOS 11 and later because the underlying APIs have been removed from iOS.