Created
December 17, 2017 06:35
-
-
Save jsbain/874c3aead15a8be3c847f5c46c6397db to your computer and use it in GitHub Desktop.
objc_classes.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from objc_util import * | |
import weakref | |
''' Attempt at making more natural objc class in pythonista. | |
decorate class wth @objc_class. | |
set superclass and protocols if desired. | |
decorate objc methods with @objc_method | |
e.g | |
@objcclass | |
class MySearchResultUpdater(object): | |
protocol=['UISearchControllerUpdating'] | |
superclass=NSObject | |
def __init_(self): | |
self.tv=None | |
@objcmethod | |
def updateSearchResultsForSearchController_(_self,_sel, controller): | |
self=MySearchResultUpdater(_self) # access self from callback | |
tv=self.tv #instance variable! | |
if ObjCInstance(controller).active(): | |
sb=ObjCInstance(controller).searchBar() | |
filterTerm=str(sb.text()) | |
tv.data_source.filter_items(filterTerm) | |
else: | |
tv.data_source.filter_items('') | |
you can access self by using YourClassName(_self), then get to py instance vars, methods, etc | |
The way this works is that the objc class is created, and attached to the py class. | |
''' | |
def objcmethod(fcn): | |
'''fcn gets wrapped as static., and marked so we can find it. | |
''' | |
fcn._objcmethod=True | |
return staticmethod(fcn) | |
def get_objc_methods(cls): | |
'''find all tagged methods. note, this only works with protocol''' | |
methods=[] | |
for m in cls.__dict__.values(): | |
try: | |
if m.__func__._objcmethod: | |
methods.append(m.__func__) | |
except AttributeError: | |
pass | |
return methods | |
def objcclass(cls): | |
'''decorator which creates an objcclass''' | |
methods=get_objc_methods(cls) | |
if hasattr(cls,'superclass'): | |
superclass=cls.superclass | |
else: | |
superclass=ObjCClass('NSObject') | |
if hasattr(cls,'protocols'): | |
protocols=cls.protocols | |
else: | |
protocols=[] | |
cls._objcclass=create_objc_class(cls.__name__, | |
superclass=superclass, | |
methods=methods, | |
protocols=protocols, debug=True) | |
oldinit=cls.__init__ | |
cls.instances={} | |
def __init__(self,*args,**kwargs): | |
oldinit(self, *args, **kwargs) | |
self.objcinstance=cls._objcclass.new() | |
self._objc_ptr=self.objcinstance.ptr | |
cls.instances[self.objcinstance.ptr]=weakref.ref(self) | |
def get_instance(*args): | |
if args: | |
return cls.instances[args[0]]() | |
else: | |
return cls() | |
cls.__init__=__init__ | |
return get_instance | |
def ObjCBlockWrapper(argtypes=[c_void_p], restype=None): | |
def wrapper(func): | |
return ObjCBlock(func,argtypes=argtypes,restype=restype) | |
return wrapper | |
''' | |
@objcclass | |
class JBQLDatasource(object): | |
protocols=['QLPreviewControllerDataSource', | |
'QLPreviewControllerDelegate'] | |
def __init__(self): | |
self.items=[] #list of NSURLs | |
@objcmethod | |
def numberOfPreviewItemsInPreviewController_(_self,_sel,pvc): | |
print('called') | |
return len(JBQLDatasource(_self).items) | |
@objcmethod | |
def previewController_previewItemAtIndex_( _self, _sel, controller, index): | |
self=JBQLDatasource(_self) | |
return self.items[index].ptr | |
j=JBQLDatasource() | |
import os | |
for f in os.listdir('.'): | |
if '.py' not in f: | |
j.items.append(nsurl(os.path.abspath(f))) | |
pyk=ObjCClass("PYKConsoleQuickLookController").new() | |
pyk.setCurrentItems=ns(j.items) | |
pvc=QLPreviewController.new() | |
pvc.dataSource=j.objcinstance | |
import ui | |
v=ui.View(frame=(0,0,400,400)) | |
v.content_mode=ui.CONTENT_SCALE_ASPECT_FIT | |
ObjCInstance(v).addSubview_(pvc.view()) | |
pvc.view.frame=ObjCInstance(v).bounds | |
v.present('sheet') | |
''' | |
def ObjCBlockWrapper(argtypes=c_void_p, restype=None): | |
def deco(func): | |
def wrapper(*args,**kwargs): | |
return func(*args,**kwargs) | |
def blk(*args,**kwargs): | |
ObjCBlock(wrapper,restype,argtypes).__call__ | |
#return wrapper | |
return deco | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ui | |
from ctypes import py_object | |
from objc_util import * | |
import sys | |
from objc_classes import objcmethod, objcclass | |
UISearchController=ObjCClass('UISearchController') | |
class SearchableTableView(ui.View): | |
'''Works like a tableview, except that search bar is shown. | |
TODO: Extra search_delegate attrib, exposes: | |
update_search_results(tv, searchbar) | |
scope_button_titles(tv) | |
''' | |
def __init__(self,*args,**kwargs): | |
#setup a tableview | |
ui.View.__init__(self,*args,**kwargs) | |
tv=ui.TableView(name='tv1',*args,**kwargs) | |
self.bounds=tv.frame | |
tv.flex='wh' | |
self.add_subview(tv) | |
searchController=UISearchController.alloc().initWithSearchResultsController_(None) | |
updater = MySearchResultUpdater() | |
searchController.searchResultsUpdater = updater | |
self.updater=updater | |
updater.tv=self | |
searchController.dimsBackgroundDuringPresentation = False #True prevents selection | |
searchController.definesPresentationContext = False | |
searchController.hidesNavigationBarDuringPresentation=False #don't change this' | |
#searchController.searchBar().scopeButtonTitles=ns(['A','B']) # this doesnt work.. | |
#searchController.searchBar().sizeToFit() | |
self.tv=tv | |
self.searchController = searchController | |
ObjCInstance(self.tv).tableHeaderView = searchController.searchBar() | |
def __getattribute__(self,attr): | |
'''Mock most attributes to tableview, except for the ones listed''' | |
attrlist=['left_button_items', 'y', 'flex', 'touch_enabled', 'superview', 'x', 'transform', 'navigation_view', 'present', 'on_screen', 'close', 'bring_to_front', 'frame', 'center', 'width', 'send_to_back', 'add_subview', 'name', 'autoresizing', 'subviews', 'bounds', 'wait_modal', 'remove_subview', 'right_button_items','tv'] | |
if not (attr.startswith('_') or attr in attrlist) and hasattr(self.tv,attr): | |
return getattr(self.tv, attr) | |
else: | |
return object.__getattribute__(self,attr) | |
def __setattr__(self,attr,value): | |
attrlist=['left_button_items', 'y', 'flex', 'touch_enabled', 'superview', 'x', 'transform', 'navigation_view', 'present', 'on_screen', 'close', 'bring_to_front', 'frame', 'center', 'width', 'send_to_back', 'add_subview', 'name', 'autoresizing', 'subviews', 'bounds', 'wait_modal', 'remove_subview', 'right_button_items','tv'] | |
if not (attr.startswith('_') or attr in attrlist) and hasattr(self,'tv') and hasattr(self.tv,attr): | |
setattr(self.tv, attr,value) | |
else: | |
object.__setattr__(self,attr,value) | |
@objcclass | |
class MySearchResultUpdater(object): | |
'''TODO: implement this as a standard delegate, and have a python search_delegate''' | |
protocol=['UISearchControllerUpdating'] | |
def __init_(self): | |
self.tv=None | |
@objcmethod | |
def updateSearchResultsForSearchController_(_self,_sel, controller): | |
tv=MySearchResultUpdater(_self).tv | |
if ObjCInstance(controller).active(): | |
sb=ObjCInstance(controller).searchBar() | |
filterTerm=str(sb.text()) | |
tv.data_source.filter_items(filterTerm) | |
else: | |
tv.data_source.filter_items('') | |
class FilteredListDataSource(ui.ListDataSource): | |
'''TOdo: deleting and moving items does not work. ''' | |
def __init__(self, items=None, filter=''): | |
ui.ListDataSource.__init__(self,items) | |
self._allitems=list(self._items) | |
def filter_items(self,filt): | |
if not filt: | |
self._items=list(self._allitems) | |
else: | |
self._items=[item for item in list(self._allitems) if filt in item] | |
self.reload() | |
@property | |
def items(self): | |
return self._items | |
@items.setter | |
def items(self, value): | |
self._allitems = ui.ListDataSourceList(value, self) | |
self._items=self._allitems | |
self.reload() | |
if __name__=='__main__': | |
L=FilteredListDataSource(['hello','world','this','is','a','test']) | |
stv=SearchableTableView(frame=(0,0,200,700)) | |
stv.data_source=stv.delegate=L | |
stv.present('panel') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment