├── AVRoutePickerView.py ├── Apple Dictionnary.py ├── BlurEffect.py ├── Camera in sheet.py ├── GifInImageView.py ├── MKMapView Demo.py ├── PKCanvasView.py ├── SceneKit Cube.py ├── SetNavigationBarColor.py ├── SetTextFieldPad.py ├── SliderWithImages.py ├── Spellcheck.py ├── SpriteKit_demo.py ├── SpriteKit_pendule.py ├── UIBarButtonItemGroup.py ├── UIBezierPath.py ├── UIStepper.py ├── altimeter.py ├── calendar.py ├── contacts objectivec.py ├── label with attributedtext.py ├── print_image.py ├── setIdleTimerDisabled.py ├── shader.py ├── shadow.py ├── ui_view_rotate.py └── webserver.py /AVRoutePickerView.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | from mainthread import mainthread 4 | 5 | class MyView (ui.View): 6 | 7 | def __init__(self, *args, **kwargs): 8 | ui.View.__init__(self, *args, **kwargs) 9 | 10 | @mainthread 11 | def did_appear(self): 12 | # AVRoutePickerView can only be created on a presented view 13 | frame = CGRect(CGPoint(10,10), CGSize(50,50)) 14 | AVRoutePickerView = ObjCClass('AVRoutePickerView').alloc().initWithFrame_(frame) 15 | self_objc = self.__py_view__.managed 16 | self_objc.addSubview_(AVRoutePickerView) 17 | 18 | if __name__ == '__main__': 19 | mv = MyView() 20 | mv.frame = (0,0,500,500) 21 | mv.background_color = ui.COLOR_WHITE 22 | mv.title = 'AVRoutePickerView' 23 | ui.show_view(mv,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /Apple Dictionnary.py: -------------------------------------------------------------------------------- 1 | # even without wifi, thus local 2 | from UIKit import * 3 | from rubicon.objc import * 4 | import pyto_ui as ui 5 | from mainthread import mainthread 6 | 7 | UIReferenceLibraryViewController = ObjCClass('UIReferenceLibraryViewController') 8 | 9 | class MyView(ui.View): 10 | def __init__(self, *args, **kwargs): 11 | super().__init__(*args, **kwargs) 12 | self.background_color = ui.COLOR_LIGHT_GRAY 13 | self.frame = (0,0,600,700) 14 | self.title = 'Dictionary' 15 | 16 | tf = ui.TextField(placeholder='word to search') 17 | tf.frame = (10,10,self.width-20,32) 18 | tf.autocapitalization_type = ui.AUTO_CAPITALIZE_NONE 19 | tf.become_first_responder() 20 | tf.did_end_editing = self.did_end_editing 21 | self.add_subview(tf) 22 | 23 | def did_end_editing(self, sender): 24 | if sender.text: 25 | self.dict(sender.text) 26 | 27 | @mainthread 28 | def dict(self, input): 29 | try: 30 | referenceViewController = UIReferenceLibraryViewController.alloc().initWithTerm_(input).autorelease() 31 | referenceViewController.setTitle_('Definition: {0}{1}{0}'.format('\'', input)) 32 | referenceViewController.setPreferredContentSize_(CGSize(540, 540)) 33 | referenceViewController.setModalPresentationStyle_(2) 34 | #ui.show_view_controller(referenceViewController) 35 | 36 | # an UIViewController has to be presented in an other UIViewController 37 | # let us search UIViewController of the presented UiView 38 | # code from https://forum.omz-software.com/topic/2060/presenting-viewcontroller 39 | viewobj = self.__py_view__.managed 40 | root_view_controller=viewobj.nextResponder 41 | try: 42 | while not root_view_controller.isKindOfClass_(ObjCClass('UIViewController')): 43 | #print(root_view_controller) 44 | root_view_controller=root_view_controller.nextResponder 45 | except AttributeError: 46 | root_view_controller = None #if view is not being presented for example 47 | 48 | 49 | root_view_controller.presentViewController_animated_completion_(referenceViewController, True, None) 50 | 51 | except Exception as e: 52 | print(e) 53 | 54 | v = MyView() 55 | ui.show_view(v,ui.PRESENTATION_MODE_SHEET) 56 | -------------------------------------------------------------------------------- /BlurEffect.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | from mainthread import mainthread 4 | from PIL import Image 5 | from urllib.request import urlopen 6 | 7 | class BlurView(ui.View): 8 | def __init__(self, style=1, *args, **kwargs): 9 | ui.View.__init__(self, **kwargs) 10 | self.title = 'UIBlurEffect' 11 | 12 | #pil = Image.open('mylocal.jpg') 13 | pil = Image.open(urlopen('https://i.imgur.com/CqdrpEb.jpg')) 14 | wi,hi = pil.size 15 | w = 500 16 | h = w * hi/wi 17 | self.frame = (0,0,w,h) 18 | 19 | iv = ui.ImageView() 20 | iv.name = 'img' 21 | iv.image = pil 22 | iv.frame = self.frame 23 | self.add_subview(iv) 24 | self._style = style 25 | self.effect_view = None 26 | 27 | @mainthread 28 | def did_appear(self): 29 | if self.effect_view is not None: 30 | self.effect_view.removeFromSuperview() 31 | UIVisualEffectView = ObjCClass('UIVisualEffectView') 32 | UIBlurEffect = ObjCClass('UIBlurEffect') 33 | frame = CGRect(CGPoint(self.width/4,self.height/4), CGSize(self.width/2, self.height/2)) 34 | self.effect_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease() 35 | effect = UIBlurEffect.effectWithStyle_(self._style) 36 | self.effect_view.effect = effect 37 | self.effect_view.setAutoresizingMask_(18) 38 | self['img'].__py_view__.managed.addSubview_(self.effect_view) 39 | 40 | if __name__ == '__main__': 41 | # different styles, see https://developer.apple.com/documentation/uikit/uiblureffect/style 42 | blur_view = BlurView(style=16) 43 | ui.show_view(blur_view,ui.PRESENTATION_MODE_SHEET) 44 | -------------------------------------------------------------------------------- /Camera in sheet.py: -------------------------------------------------------------------------------- 1 | # https://github.com/jsbain/objc_hacks/blob/master/live_camera_view.py 2 | from rubicon.objc import * 3 | import pyto_ui as ui 4 | from UIKit import * 5 | 6 | class LiveCameraView(ui.View): 7 | def __init__(self,device=0, *args, **kwargs): 8 | # device = 0 = camera back 9 | # 1 = camera front 10 | # 2 = micro 11 | ui.View.__init__(self,*args,**kwargs) 12 | self._session=ObjCClass('AVCaptureSession').alloc().init() 13 | self._session.setSessionPreset_('AVCaptureSessionPresetHigh'); 14 | inputDevices=ObjCClass('AVCaptureDevice').devices() 15 | #print(inputDevices) 16 | self._inputDevice=inputDevices[device] 17 | deviceInput = ObjCClass('AVCaptureDeviceInput').deviceInputWithDevice_error_(self._inputDevice, None); 18 | if self._session.canAddInput_(deviceInput): 19 | self._session.addInput_(deviceInput) 20 | self._previewLayer = ObjCClass('AVCaptureVideoPreviewLayer').alloc().initWithSession_(self._session) 21 | self._previewLayer.setVideoGravity_( 22 | 'AVLayerVideoGravityResizeAspectFill') 23 | rootLayer = self.__py_view__.managed.layer 24 | rootLayer.setMasksToBounds_(True) 25 | self._previewLayer.setFrame_(CGRect(CGPoint(-70, 0), CGSize(self.height,self.height))) 26 | rootLayer.insertSublayer_atIndex_(self._previewLayer,0) 27 | self._session.startRunning() 28 | 29 | def will_close(self): 30 | self._session.stopRunning() 31 | 32 | def layout(self): 33 | if not self._session.isRunning(): 34 | self._session.startRunning() 35 | 36 | rootview = LiveCameraView() 37 | app = ObjCClass('UIApplication').sharedApplication.keyWindow 38 | for window in UIApplication.sharedApplication.windows: 39 | wi = window.bounds.size.width 40 | hi = window.bounds.size.height 41 | break 42 | w = 400 43 | h = w * hi/wi 44 | rootview.frame = (0,0,w,h) 45 | rootview.title = 'Camera in Sheet' 46 | ui.show_view(rootview,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /GifInImageView.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Objectivec allows to use gif files without passing via update Pythonista method of ui.View. 3 | ''' 4 | import io 5 | from PIL import Image 6 | from rubicon.objc import * 7 | import pyto_ui as ui 8 | 9 | class GifImageView(ui.ImageView): 10 | def __init__(self, pil, *args, **kwargs): 11 | ui.ImageView.__init__(self, *args, **kwargs) 12 | self.flex='wh' 13 | duration = pil.n_frames * pil.info['duration'] / 1000 14 | frames = [] 15 | for i in range(pil.n_frames): 16 | pil.seek(i) 17 | frames.append(self.pil2ui(pil)) 18 | UIImageView = self.__py_view__.managed 19 | UIImageView.animationImages = frames 20 | UIImageView.animationDuration = duration 21 | UIImageView.animationRepeatCount = 0 22 | UIImageView.startAnimating() 23 | 24 | def pil2ui(self,pilimage): 25 | with io.BytesIO() as bIO: 26 | pilimage.save(bIO, 'PNG') 27 | uiimage = ObjCClass('UIImage').alloc().initWithData_(bIO.getvalue()) 28 | return uiimage 29 | 30 | if __name__ == '__main__': 31 | v = ui.View() 32 | v.title = 'GifInImageView' 33 | v.frame = (0,0,200,200) 34 | v.background_color = ui.COLOR_WHITE 35 | gif = 'your_own.gif' 36 | pil = Image.open(gif) 37 | w,h = pil.size 38 | g = GifImageView(pil) 39 | g.frame = (50,50,100,100*h/w) 40 | v.add_subview(g) 41 | ui.show_view(v,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /MKMapView Demo.py: -------------------------------------------------------------------------------- 1 | # based on Pythonista example https://gist.github.com/omz/451a6685fddcf8ccdfc5 2 | from rubicon.objc import * 3 | import pyto_ui as ui 4 | import mainthread 5 | import location 6 | import time 7 | 8 | class MapView (ui.View): 9 | @mainthread.mainthread 10 | def __init__(self, *args, **kwargs): 11 | ui.View.__init__(self, *args, **kwargs) 12 | self.title = 'MKMapView' 13 | 14 | maptype_button = ui.ButtonItem('satellite') 15 | maptype_button.action = self.maptype_button_action 16 | self.button_items = [maptype_button] 17 | 18 | MKMapView = ObjCClass('MKMapView') 19 | frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height)) 20 | self.mk_map_view = MKMapView.alloc().initWithFrame_(frame) 21 | self.mk_map_view.mapType = 0 # standard 22 | flex_width, flex_height = (1<<1), (1<<4) 23 | self.mk_map_view.setAutoresizingMask_(flex_width|flex_height) 24 | self_objc = self.__py_view__.managed 25 | self_objc.addSubview_(self.mk_map_view) 26 | self.mk_map_view.release() 27 | 28 | def maptype_button_action(self, sender): 29 | self.mk_map_view.mapType += 1 30 | if self.mk_map_view.mapType > 2: 31 | self.mk_map_view.mapType = 0 32 | sender.title = ['satellite', 'hybrid', 'standard'][self.mk_map_view.mapType] 33 | 34 | @mainthread.mainthread 35 | def add_pin(self, lat, lon, title, subtitle=None, select=False): 36 | '''Add a pin annotation to the map''' 37 | MKPointAnnotation = ObjCClass('MKPointAnnotation') 38 | annotation = MKPointAnnotation.alloc().init().autorelease() 39 | annotation.setTitle_(title) 40 | if subtitle: 41 | annotation.setSubtitle_(subtitle) 42 | annotation.coordinate = (lat,lon) 43 | self.mk_map_view.addAnnotation_(annotation) 44 | if select: 45 | self.mk_map_view.selectAnnotation_animated_(annotation, True) 46 | 47 | def main(): 48 | # Create and present a MapView: 49 | v = MapView() 50 | v.frame=(0, 0, 600, 600) 51 | 52 | location.start_updating() 53 | time.sleep(1) 54 | loc = location.get_location() 55 | #location.stop_updating() 56 | #print(loc) 57 | if loc: 58 | lat, lon = loc[1], loc[0] 59 | v.add_pin(lat, lon, 'Current Location', str((lat, lon))) 60 | 61 | ui.show_view(v,ui.PRESENTATION_MODE_SHEET) 62 | 63 | if __name__ == '__main__': 64 | main() 65 | -------------------------------------------------------------------------------- /PKCanvasView.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | from mainthread import mainthread 4 | import photos 5 | from __image__ import __pil_image_from_ui_image__ 6 | 7 | class MyView(ui.View): 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | 11 | b_clear = ui.ButtonItem() 12 | b_clear.title = 'clear' 13 | b_clear.action = self.b_clear_action 14 | 15 | b_save = ui.ButtonItem() 16 | b_save.title = 'save' 17 | b_save.action = self.b_save_action 18 | self.button_items = [b_clear, b_save] 19 | 20 | def did_disappear(self): 21 | del self.toolPicker 22 | 23 | def b_clear_action(self,sender): 24 | self.canvasView.drawing = ObjCClass('PKDrawing').alloc().init() 25 | 26 | def b_save_action(self,nsender): 27 | uiimage = self.canvasView.drawing.imageFromRect_scale_(self.canvasView.frame,1.0) 28 | pilimage = __pil_image_from_ui_image__(uiimage) 29 | photos.save_image(pilimage) 30 | 31 | @mainthread 32 | def did_appear(self): 33 | # PKCanvasView can only be created on a presented view 34 | view = self.__py_view__.managed 35 | self.canvasView = ObjCClass('PKCanvasView').alloc().initWithFrame_(view.frame) 36 | view.addSubview_(self.canvasView) 37 | self.toolPicker = ObjCClass('PKToolPicker').alloc().init() 38 | self.toolPicker.setVisible_forFirstResponder_(True, self.canvasView) 39 | self.toolPicker.addObserver_(self.canvasView) 40 | self.canvasView.becomeFirstResponder() 41 | 42 | if __name__ == '__main__': 43 | mv = MyView() 44 | mv.frame = (0,0,500,600) 45 | mv.title = 'PKCanvasView + PKToolPicker' 46 | ui.show_view(mv,ui.PRESENTATION_MODE_SHEET) 47 | -------------------------------------------------------------------------------- /SceneKit Cube.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | import math 4 | import mainthread 5 | 6 | 7 | from Foundation import NSBundle 8 | NSBundle.bundleWithPath_('/System/Library/Frameworks/SceneKit.framework').load() 9 | 10 | 11 | SCNView, SCNScene, SCNBox, SCNNode, SCNMaterial, SCNCamera, SCNLight, SCNAction, SCNLookAtConstraint = map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNNode', 'SCNMaterial', 'SCNCamera', 'SCNLight', 'SCNAction', 'SCNLookAtConstraint' ]) 12 | UIColor = ObjCClass('UIColor') 13 | 14 | @mainthread.mainthread 15 | def demo(main_view): 16 | 17 | main_view_objc = main_view.__py_view__.managed 18 | 19 | scene_view = SCNView.alloc().initWithFrame_options_(((0, 0),(main_view.width,main_view.height)), None).autorelease() 20 | scene_view.setAutoresizingMask_(18) 21 | scene_view.setAllowsCameraControl_(True) 22 | main_view_objc.addSubview_(scene_view) 23 | 24 | scene = SCNScene.scene() 25 | scene_view.setScene_(scene) 26 | 27 | root_node = scene.rootNode 28 | 29 | camera = SCNCamera.camera() 30 | camera_node = SCNNode.node() 31 | camera_node.setCamera(camera) 32 | camera_node.setPosition((-30,30,30)) 33 | root_node.addChildNode_(camera_node) 34 | 35 | geometry = SCNBox.boxWithWidth_height_length_chamferRadius_(10, 10, 10, 0) 36 | 37 | geometry_node = SCNNode.nodeWithGeometry_(geometry) 38 | root_node.addChildNode_(geometry_node) 39 | 40 | Materials = [] 41 | colors = [UIColor.redColor, UIColor.blueColor, UIColor.greenColor, UIColor.yellowColor, UIColor.orangeColor, UIColor.cyanColor] 42 | for i in range(0,6): 43 | Material = SCNMaterial.material() 44 | Material.contents = colors[i] 45 | Materials.append(Material) 46 | geometry.setMaterials_(Materials) 47 | 48 | # Add a constraint to the camera to keep it pointing to the target geometry 49 | constraint = SCNLookAtConstraint.lookAtConstraintWithTarget_(geometry_node) 50 | constraint.gimbalLockEnabled = True 51 | camera_node.constraints = [constraint] 52 | 53 | light_node = SCNNode.node() 54 | light_node.setPosition_((30, 0, -30)) 55 | light = SCNLight.light() 56 | #light.setType_('spot') 57 | light.setType_('probe') 58 | #light.setType_('directional') 59 | light.setCastsShadow_(True) 60 | light.setColor_(UIColor.whiteColor) 61 | light_node.setLight_(light) 62 | root_node.addChildNode_(light_node) 63 | 64 | rotate_action = SCNAction.repeatActionForever_(SCNAction.rotateByX_y_z_duration_(0, math.pi*2, 0, 10)) 65 | geometry_node.runAction_(rotate_action) 66 | 67 | 68 | def main(): 69 | v = ui.View() 70 | v.frame=(0, 0, 600, 600) 71 | v.name = 'SceneKit Demo' 72 | demo(v) 73 | ui.show_view(v, ui.PRESENTATION_MODE_SHEET) 74 | 75 | if __name__ == '__main__': 76 | main() 77 | -------------------------------------------------------------------------------- /SetNavigationBarColor.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | 4 | def GetNavigationBar(ui_view): 5 | # ui.View has to be first presented 6 | vo = ui_view.__py_view__.managed 7 | while True: 8 | super = vo.superview() 9 | for sv in super.subviews(): 10 | if str(sv).startswith(' 0: 32 | #if tfb.text != '': 33 | tfb.text = tfb.text[:cursor-1] + tfb.text[cursor:] 34 | cursor = cursor - 1 35 | elif sender.name == 'return': 36 | tfb.resign_first_responder() 37 | return 38 | else: 39 | tfb.text = tfb.text[:cursor] + sender.title + tfb.text[cursor:] 40 | cursor = cursor + 1 41 | 42 | # set cursor 43 | cursor_position = tfobjc.positionFromPosition_offset_(tfobjc.beginningOfDocument, cursor) 44 | tfobjc.selectedTextRange = tfobjc.textRangeFromPosition_toPosition_(cursor_position, cursor_position) 45 | 46 | # design your keyboard 47 | # pad = [{key='functionnality',title='title',icon='icon'},...] 48 | # new row => new row 49 | # nul => no key 50 | # back space => left delete 51 | # delete => right delete 52 | # return => discard the keyboard 53 | # other => append the character 54 | 55 | # count the maximum width of rows 56 | row_max_length = 0 57 | row_length = 0 58 | for pad_elem in pad: 59 | if pad_elem['key'] == 'new row': 60 | if row_length > row_max_length: 61 | row_max_length = row_length 62 | row_length = 0 63 | else: 64 | row_length = row_length + 1 65 | if row_length > row_max_length: 66 | row_max_length = row_length 67 | 68 | v = ui.View() 69 | for window in UIApplication.sharedApplication.windows: 70 | v.width = window.bounds.size.width 71 | break 72 | db = 50 73 | dd = 10 74 | x0 = (v.width-row_max_length*db-(row_max_length-1)*dd)/2 75 | x = x0 76 | y = dd 77 | 78 | for pad_elem in pad: 79 | if pad_elem['key'] == 'new row': 80 | y = y + db + dd 81 | x = x0 82 | elif pad_elem['key'] == 'nul': 83 | x = x + db + dd 84 | else: 85 | b = ui.Button() 86 | b.name = pad_elem['key'] 87 | b.background_color = ui.Color.rgb(1,1,1,1) 88 | b.tint_color = ui.Color.rgb(0,0,0,1) 89 | b.corner_radius = 10 90 | b.title = '' 91 | b.font = ui.Font('Menlo',32) 92 | if 'icon' in pad_elem: 93 | b.image = ui.image_with_system_name(pad_elem['icon']) 94 | elif 'title' not in pad_elem: 95 | b.title = pad_elem['key'] 96 | if 'title' in pad_elem: 97 | b.title = pad_elem['title'] 98 | b.frame = (x,y,db,db) 99 | b.TextField = tf # store tf as key attribute needed when pressed 100 | if 'action' in pad_elem: 101 | b.action = pad_elem['action'] 102 | else: 103 | b.action = key_pressed 104 | v.add_subview(b) 105 | x = x + db + dd 106 | y = y + db + dd 107 | 108 | app = UIApplication.sharedApplication.keyWindow 109 | bot = app.safeAreaInsets.bottom 110 | 111 | v.height = y + bot 112 | 113 | # view of keyboard 114 | vo = v.__py_view__.managed 115 | tfo.setInputView_(vo) 116 | 117 | # color of cursor and selected text 118 | tfo.tintColor = UIColor.redColor.colorWithAlphaComponent(0.5) 119 | 120 | tfo.inputAssistantItem.setLeadingBarButtonGroups(None) 121 | tfo.inputAssistantItem.setTrailingBarButtonGroups(None) 122 | 123 | if __name__ == '__main__': 124 | mv = ui.View() 125 | mv.frame = (0,0,400,400) 126 | mv.background_color = ui.Color.rgb(0.5,0.5,0.5,1) 127 | tf = ui.TextField() 128 | tf.frame = (10,10,100,30) 129 | SetTextFieldPad(tf) 130 | tf.text = '' 131 | tf.become_first_responder() 132 | mv.add_subview(tf) 133 | ui.show_view(mv,ui.PRESENTATION_MODE_SHEET) 134 | -------------------------------------------------------------------------------- /SliderWithImages.py: -------------------------------------------------------------------------------- 1 | # based on Slider Pyto example 2 | import pyto_ui as ui 3 | from rubicon.objc import * 4 | from time import sleep 5 | 6 | slider = ui.Slider(0) 7 | 8 | def did_slide(sender): 9 | print(sender.value) 10 | 11 | def layout(sender): 12 | sleep(0.5) 13 | slider.set_value_with_animation(50) 14 | 15 | view = ui.View() 16 | view.background_color = ui.COLOR_SYSTEM_BACKGROUND 17 | view.layout = layout 18 | 19 | slider.maximum_value = 100 20 | slider.width = 200 21 | slider.center = (view.width/2, view.height/2) 22 | slider.flex = [ 23 | ui.FLEXIBLE_BOTTOM_MARGIN, 24 | ui.FLEXIBLE_TOP_MARGIN, 25 | ui.FLEXIBLE_LEFT_MARGIN, 26 | ui.FLEXIBLE_RIGHT_MARGIN 27 | ] 28 | slider.action = did_slide 29 | 30 | slider_objc = slider.__py_view__.managed 31 | slider_objc.minimumValueImage = ui.image_with_system_name('volume.1') 32 | slider_objc.maximumValueImage = ui.image_with_system_name('volume.3') 33 | slider_objc.setThumbImage_forState_(ui.image_with_system_name('slowmo'), 0) 34 | view.add_subview(slider) 35 | 36 | ui.show_view(view, ui.PRESENTATION_MODE_SHEET) 37 | -------------------------------------------------------------------------------- /Spellcheck.py: -------------------------------------------------------------------------------- 1 | # from Pythonista developer https://gist.github.com/omz/e141f676504b743dbaa8 2 | # mispelled words 3 | from rubicon.objc import ObjCClass 4 | UITextChecker = ObjCClass('UITextChecker') 5 | 6 | def check_word(word, lang='en_US'): 7 | c = UITextChecker.new().autorelease() 8 | check = c.rangeOfMisspelledWordInString_range_startingAt_wrap_language_ 9 | misspelled_range = check(word, (0, len(word)), 0, False, lang) 10 | return (misspelled_range.location != 0) 11 | 12 | test_words = ['foo', 'bar', 'baz', 'quuz', 'cat', 'dog', 'anyy'] 13 | for word in test_words: 14 | print(word, check_word(word)) -------------------------------------------------------------------------------- /SpriteKit_demo.py: -------------------------------------------------------------------------------- 1 | # based on 2 | # https://github.com/tdamdouni/Pythonista/blob/master/omz/SKExample.py 3 | # https://github.com/jbking/pythonista-misc/blob/master/spritekit/skview-demo.py 4 | import random 5 | import pyto_ui as ui 6 | from rubicon.objc import * 7 | from Foundation import NSBundle 8 | from mainthread import mainthread 9 | 10 | import photos 11 | import io 12 | 13 | def pil2ui(pilimage): 14 | with io.BytesIO() as bIO: 15 | pilimage.save(bIO, 'PNG') 16 | uiimage = ObjCClass('UIImage').alloc().initWithData_(bIO.getvalue()) 17 | return uiimage 18 | 19 | NSBundle.bundleWithPath_('/System/Library/Frameworks/SpriteKit.framework').load() 20 | 21 | UIApplication = ObjCClass('UIApplication') 22 | SKView = ObjCClass('SKView') 23 | SKScene = ObjCClass('SKScene') 24 | SKShapeNode = ObjCClass('SKShapeNode') 25 | SKSpriteNode = ObjCClass('SKSpriteNode') 26 | SKUniform = ObjCClass('SKUniform') 27 | SKShader = ObjCClass('SKShader') 28 | SKTexture = ObjCClass('SKTexture') 29 | SKPhysicsBody = ObjCClass('SKPhysicsBody') 30 | UIColor = ObjCClass('UIColor') 31 | 32 | colors = [UIColor.redColor, UIColor.greenColor, UIColor.orangeColor, UIColor.cyanColor, UIColor.magentaColor, UIColor.purpleColor, UIColor.brownColor, UIColor.blackColor, UIColor.whiteColor] 33 | 34 | def get_screen_size(): 35 | app = UIApplication.sharedApplication.keyWindow 36 | for window in UIApplication.sharedApplication.windows: 37 | ws = window.bounds.size.width 38 | hs = window.bounds.size.height 39 | break 40 | return ws,hs 41 | 42 | def create_circle_shape(point): 43 | radius = random.randint(25, 45) 44 | node = SKShapeNode.shapeNodeWithCircleOfRadius_(radius) 45 | node.position = point 46 | node.physicsBody = SKPhysicsBody.bodyWithCircleOfRadius_(radius) 47 | return node 48 | 49 | def create_box_shape(point): 50 | width = random.randint(42, 80) 51 | height = random.randint(42, 80) 52 | size = CGSize(width, height) 53 | node = SKShapeNode.shapeNodeWithRectOfSize_(size) 54 | node.position = point 55 | node.physicsBody = SKPhysicsBody.bodyWithRectangleOfSize_(size) 56 | return node 57 | 58 | def create_sprite(point): 59 | #uiimage = ui.image_with_system_name('person') 60 | size = uiimage.size 61 | tex = SKTexture.textureWithImage_(uiimage) 62 | node = SKSpriteNode.spriteNodeWithTexture_(tex) 63 | node.position = point 64 | node.physicsBody = SKPhysicsBody.bodyWithRectangleOfSize_(size) 65 | #node.physicsBody = SKPhysicsBody.bodyWithTexture_size_(tex, size) 66 | 67 | spritew = SKUniform.uniformWithName_float_('spritew',size.width) 68 | spriteh = SKUniform.uniformWithName_float_('spriteh',size.height) 69 | shader_text5 = ''' 70 | // pixelisation 71 | void main() { 72 | float px = 20.; 73 | float dx = px*cos(u_time/4.)*(1./spritew); 74 | float dy = px*cos(u_time/4.)*(1./spriteh); 75 | vec2 coord = vec2(dx*floor(v_tex_coord.x/dx), dy*floor(v_tex_coord.y/dy)); 76 | gl_FragColor = texture2D(u_texture,coord); 77 | } 78 | ''' 79 | shader = SKShader.shaderWithSource(shader_text5) 80 | shader.uniforms = [spritew,spriteh] 81 | node.setShader_(shader) 82 | 83 | return node 84 | 85 | # the boundaries to keep the shapes in 86 | def addBorder(target_scene, x,y,w,h): 87 | size = CGSize(w,h) 88 | node = SKShapeNode.shapeNodeWithRectOfSize_(size) 89 | node.position = CGPoint(x,y) 90 | node.lineWidth = 2 91 | node.fillColor = UIColor.blueColor 92 | 93 | body = SKPhysicsBody.bodyWithRectangleOfSize_(size) 94 | body.dynamic = False 95 | node.physicsBody = body 96 | target_scene.addChild_(node) 97 | 98 | def random_color(): 99 | return colors[random.randint(1,len(colors))-1] 100 | 101 | # We subclass SKScene 102 | class MyScene(SKScene): 103 | 104 | # Overriding update_ 105 | @objc_method 106 | def update_(self, current_time): 107 | scene = self 108 | for child in scene.children: 109 | if child.position.y < 0: 110 | child.removeFromParent() 111 | 112 | # Overriding touchesBegan_withEvent_ 113 | @objc_method 114 | def touchesBegan_withEvent_(self, touches, event): 115 | scene = self 116 | #print(event) 117 | touch = touches.anyObject() 118 | 119 | point = touch.locationInNode_(scene) 120 | node = random.choice([ 121 | create_circle_shape, 122 | create_box_shape, 123 | create_sprite 124 | ])(point) 125 | node.fillColor = random_color() 126 | scene.addChild_(node) 127 | 128 | class DemoView(ui.View): 129 | debug = True 130 | 131 | def __init__(self, *args, **kwargs): 132 | super().__init__(*args, **kwargs) 133 | 134 | @mainthread 135 | def did_appear(self): 136 | # SKView can only be created on a presented view 137 | # Setup SKView 138 | screen_size = get_screen_size() 139 | sz = CGSize(screen_size[0], screen_size[1]-100) 140 | rect = CGRect(CGPoint(0, 0), sz) 141 | skview = SKView.alloc().initWithFrame_(rect) 142 | # debug 143 | skview.showsFPS = self.debug 144 | skview.showsNodeCount = self.debug 145 | skview.showsPhysics = self.debug 146 | self.__py_view__.managed.addSubview(skview) 147 | self.skview = skview 148 | scene = MyScene.sceneWithSize_(rect.size) 149 | scene.backgroundColor = UIColor.yellowColor 150 | 151 | side_width = 10 152 | side_height = sz.height *0.8 153 | side_y = 0 + side_height/2 154 | side_x = 20 155 | addBorder(scene, side_x, side_y, side_width, side_height) 156 | addBorder(scene, sz.width-side_x, side_y, side_width, side_height) 157 | addBorder(scene, sz.width/2,side_width/2,sz.width,side_width) 158 | 159 | skview.presentScene_(scene) 160 | self.scene = scene 161 | 162 | def did_disappear(self): 163 | self.skview.paused = True 164 | 165 | 166 | if __name__ == '__main__': 167 | pil = photos.pick_photo() 168 | w,h = pil.size 169 | pil = pil.resize((80,int(80*h/w))) 170 | uiimage = pil2ui(pil) 171 | view = DemoView() 172 | view.title = 'SpriteKit' 173 | ui.show_view(view,ui.PRESENTATION_MODE_FULLSCREEN) -------------------------------------------------------------------------------- /SpriteKit_pendule.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | import pyto_ui as ui 3 | from rubicon.objc import * 4 | from Foundation import NSBundle 5 | from mainthread import mainthread 6 | 7 | NSBundle.bundleWithPath_('/System/Library/Frameworks/SpriteKit.framework').load() 8 | 9 | UIApplication = ObjCClass('UIApplication') 10 | SKView = ObjCClass('SKView') 11 | SKScene = ObjCClass('SKScene') 12 | SKLabelNode = ObjCClass('SKLabelNode') 13 | UIColor = ObjCClass('UIColor') 14 | 15 | def get_screen_size(): 16 | app = UIApplication.sharedApplication.keyWindow 17 | for window in UIApplication.sharedApplication.windows: 18 | ws = window.bounds.size.width 19 | hs = window.bounds.size.height 20 | break 21 | return ws,hs 22 | 23 | # We subclass SKScene 24 | class MyScene(SKScene): 25 | 26 | # Overriding update_ 27 | @objc_method 28 | def update_(self, current_time): 29 | self.ang = self.ang + self.delta*cos(radians(2*self.ang)) +.05*self.delta 30 | if abs(self.ang) > self.ang_max: 31 | self.delta = -self.delta 32 | self.ang = self.ang + self.delta 33 | self.node.rotation = radians(self.ang) 34 | r = self.size.height/2 35 | x = self.size.width/2+r*sin(radians(-self.ang)) 36 | y = r*cos(radians(self.ang)) 37 | self.node.position = (x,y) 38 | 39 | class DemoView(ui.View): 40 | 41 | def __init__(self, *args, **kwargs): 42 | super().__init__(*args, **kwargs) 43 | 44 | @mainthread 45 | def did_appear(self): 46 | # SKView can only be created on a presented view 47 | # Setup SKView 48 | screen_size = get_screen_size() 49 | sz = CGSize(screen_size[0], screen_size[1]-100) 50 | rect = CGRect(CGPoint(0, 0), sz) 51 | skview = SKView.alloc().initWithFrame_(rect) 52 | skview.preferredFramesPerSecond = 30 53 | self.__py_view__.managed.addSubview(skview) 54 | self.skview = skview 55 | 56 | scene = MyScene.sceneWithSize_(rect.size) 57 | scene.backgroundColor = UIColor.blackColor 58 | 59 | node = SKLabelNode.alloc().init() 60 | node.text = 'Pyto is good for you' 61 | node.fontColor = UIColor.whiteColor 62 | node.fontName = 'Helvetica' 63 | node.fontSize = 20 64 | node.position = CGPoint(sz.width/2, sz.height/2) 65 | #node.physicsBody = SKPhysicsBody.bodyWithRectangleOfSize_(size) 66 | scene.addChild_(node) 67 | scene.node = node 68 | 69 | scene.ang_max = 45 70 | scene.ang = -scene.ang_max 71 | scene.delta = 1 72 | 73 | skview.presentScene_(scene) 74 | self.scene = scene 75 | 76 | def did_disappear(self): 77 | self.skview.paused = True 78 | 79 | if __name__ == '__main__': 80 | view = DemoView() 81 | view.title = 'SpriteKit Pendule' 82 | ui.show_view(view,ui.PRESENTATION_MODE_FULLSCREEN) 83 | -------------------------------------------------------------------------------- /UIBarButtonItemGroup.py: -------------------------------------------------------------------------------- 1 | #.... does not work 2 | from rubicon.objc import * 3 | import pyto_ui as ui 4 | from mainthread import mainthread 5 | 6 | UIBarButtonItem = ObjCClass('UIBarButtonItem') 7 | UIBarButtonItemGroup = ObjCClass('UIBarButtonItemGroup') 8 | 9 | class ActionTarget(NSObject): 10 | 11 | @objc_method 12 | def btnAction(self, btn): 13 | global tv 14 | #print('btnAction') 15 | #print(self) 16 | #print(btn) 17 | tv.text = '' 18 | 19 | #@mainthread 20 | def main(): 21 | global tv 22 | tv = ui.TextView() 23 | tv.title = 'UIBarButtonItemGroup' 24 | tv.frame = (0, 0, 200,200) 25 | tv.become_first_responder() 26 | 27 | target = ActionTarget.new().autorelease() 28 | #b1 = UIBarButtonItem.alloc().initWithTitle_style_target_action_('clear', 0, target, SEL('btnAction')).autorelease() 29 | uiimage = ui.image_with_system_name('clear') 30 | b1 = UIBarButtonItem.alloc().initWithImage_style_target_action_(uiimage, 0, target, SEL('btnAction')).autorelease() 31 | group = UIBarButtonItemGroup.alloc().initWithBarButtonItems_representativeItem_([b1], None).autorelease() 32 | tv.__py_view__.managed.inputAssistantItem.trailingBarButtonGroups = [group] 33 | ui.show_view(tv,ui.PRESENTATION_MODE_SHEET) 34 | b1.field = tv.__py_view__.managed 35 | 36 | if __name__ == '__main__': 37 | main() -------------------------------------------------------------------------------- /UIBezierPath.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | from math import pi 4 | 5 | UIColor = ObjCClass('UIColor') 6 | class MyClass(ui.View): 7 | def __init__(self, *args, **kwargs): 8 | super().__init__(*args, **kwargs) 9 | 10 | path = ObjCClass('UIBezierPath').alloc().init() 11 | path.moveToPoint_((0, 0)) 12 | path.lineToPoint_((0, 50)) 13 | path.lineToPoint_((50, 50)) 14 | path.addArcWithCenter_radius_startAngle_endAngle_clockwise_((50,25),25,pi/2,-pi/2,False) 15 | path.lineToPoint_((50, 0)) 16 | path.closePath() 17 | 18 | shapeLayer = ObjCClass('CAShapeLayer').alloc().init() 19 | shapeLayer.path = path.CGPath 20 | shapeLayer.fillColor = UIColor.cyanColor.CGColor 21 | shapeLayer.strokeColor = UIColor.redColor.CGColor 22 | shapeLayer.lineWidth = 2.0 23 | shapeLayer.position = CGPoint(10, 10) 24 | 25 | self.__py_view__.managed.layer.addSublayer(shapeLayer) 26 | 27 | if __name__ == '__main__': 28 | w, h = 400,300 29 | f = (0, 0, w, h) 30 | mc = MyClass() 31 | mc.frame = f 32 | mc.title = 'UIBezierPath: draw in ui.View' 33 | mc.background_color = ui.COLOR_LIGHT_GRAY 34 | ui.show_view(mc,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /UIStepper.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | 4 | class Stepper(ui.View): 5 | 6 | def __init__(self, *args, **kwargs): 7 | ui.View.__init__(self, *args, **kwargs) 8 | 9 | self._stepper = ObjCClass("UIStepper").new() 10 | self._stepper.minimumValue = 0 11 | self._stepper.maximumValue = 1 12 | self._stepper.stepValue = 0.1 13 | objcview = self.__py_view__.managed 14 | objcview.addSubview_(self._stepper) 15 | 16 | self.delegate = MyDelegateClass.alloc().init() 17 | self.delegate.view = self # needed by delegate of stepper 18 | 19 | def setDelegate(self): 20 | # addTarget does not work if called at delegate creation 21 | 22 | # 4096 (1<<12) is the value for UIControlEventValueChanged 23 | self._stepper.addTarget(self.delegate, action=SEL("didChange"), forControlEvents=4096) 24 | 25 | # An Objective-C class for addTarget(_:action:forControlEvents:) 26 | class MyDelegateClass(NSObject): 27 | @objc_method 28 | def didChange(self): 29 | self.view.slider.value = self.view._stepper.value 30 | 31 | v = ui.View() 32 | v.title = 'UIStepper' 33 | v.background_color = ui.COLOR_SYSTEM_BACKGROUND 34 | v.frame = (0,0,300,200) 35 | 36 | sl = ui.Slider(0) 37 | sl.frame = (100,66,100,50) 38 | sl.user_interaction_enabled = False 39 | v.add_subview(sl) 40 | 41 | s = Stepper() 42 | s.setDelegate() 43 | s.frame = (100,134,100,50) 44 | v.add_subview(s) 45 | s.slider = sl # needed by delegate of stepper to set slider value 46 | 47 | ui.show_view(v,ui.PRESENTATION_MODE_SHEET) 48 | -------------------------------------------------------------------------------- /altimeter.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from rubicon.objc import Block, ObjCClass, ObjCInstance, py_from_ns 3 | from rubicon.objc.runtime import objc_id 4 | 5 | pressure = None 6 | 7 | # solution with @Block 8 | @Block 9 | def handlerb(altitudeData:ObjCInstance, err:ObjCInstance) -> None: 10 | print(ObjCInstance(altitudeData).pressure) 11 | 12 | 13 | # solution with block() function 14 | #def handler(_data) -> None: 15 | # nspressure = ObjCInstance(_data).pressure 16 | # global pressure 17 | # pressure = py_from_ns(nspressure) 18 | 19 | #handler_block = Block(handler, None, (objc_id)) 20 | 21 | def get_pressure(): 22 | CMAltimeter = ObjCClass('CMAltimeter') 23 | NSOperationQueue = ObjCClass('NSOperationQueue') 24 | if not CMAltimeter.isRelativeAltitudeAvailable(): 25 | print('This device has no barometer.') 26 | return 27 | altimeter = CMAltimeter.new() 28 | main_q = NSOperationQueue.mainQueue 29 | #altimeter.startRelativeAltitudeUpdatesToQueue_withHandler_(main_q, handler_block) 30 | altimeter.startRelativeAltitudeUpdatesToQueue_withHandler_(main_q, handlerb) 31 | print('Started altitude updates.') 32 | try: 33 | while pressure is None: 34 | pass 35 | finally: 36 | altimeter.stopRelativeAltitudeUpdates() 37 | print('Updates stopped.') 38 | return pressure 39 | 40 | if __name__ == '__main__': 41 | result = get_pressure() 42 | print(result) 43 | del pressure -------------------------------------------------------------------------------- /calendar.py: -------------------------------------------------------------------------------- 1 | # does not work... 2 | import datetime 3 | 4 | from rubicon.objc import Block, ObjCClass, ObjCInstance 5 | 6 | def main(): 7 | 8 | # EKEventStore = calendar database 9 | store = ObjCClass('EKEventStore').alloc().init() 10 | 11 | # Once Pyto has been authorized, this code does not need to be executed 12 | #------- begin of commented 13 | import threading 14 | access_granted = threading.Event() 15 | 16 | @Block 17 | def handler_block(granted: ObjCInstance, err: ObjCInstance) -> None: 18 | print('completion called') 19 | access_granted.set() 20 | 21 | store.requestAccessToEntityType_completion_(0, handler_block) 22 | access_granted.wait() 23 | #------- end of commented 24 | 25 | # Convert string yyyymmdd to NSdate 26 | dateFormat = ObjCClass('NSDateFormatter').alloc().init() 27 | dateFormat.setDateFormat_('yyyyMMdd HH:mm') 28 | 29 | date1 = dateFormat.dateFromString_('20200101 00:01') 30 | date2 = dateFormat.dateFromString_('20201231 23:59') 31 | 32 | calendars_array = [calendar for calendar in store.calendars if str(calendar.title) == 'Sorties'] 33 | print(store.calendars) 34 | predicate = store.predicateForEventsWithStartDate_endDate_calendars_(date1, date2, calendars_array) 35 | events = store.eventsMatchingPredicate_(predicate) 36 | for event in events: 37 | print(event.title) 38 | 39 | 40 | 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /contacts objectivec.py: -------------------------------------------------------------------------------- 1 | #...... not working ........l. 2 | from rubicon.objc import * 3 | from Foundation import * 4 | 5 | import Contacts 6 | 7 | def main(): 8 | 9 | # ObjectiveC contacts 10 | objc_contacts = {} 11 | CNContactStore = ObjCClass('CNContactStore').alloc()#.init()#.autorelease() 12 | print(dir(CNContactStore)) 13 | 14 | # Once Pyto has been authorized, this code does not need to be executed 15 | #------- begin of commented 16 | import threading 17 | access_granted = threading.Event() 18 | 19 | @Block 20 | def handler_block(granted: ObjCInstance, err: ObjCInstance) -> None: 21 | print('handler_block called') 22 | access_granted.set() 23 | 24 | CNContactStore.requestAccessForEntityType_completionHandler_(0, handler_block) 25 | access_granted.wait() 26 | #------- end of commented 27 | 28 | 29 | CNContact = ObjCClass('CNContact') 30 | Containers = CNContactStore.containersMatchingPredicate_error_(None,None) 31 | containers = {} 32 | for Container in Containers: 33 | id = Container.identifier() 34 | containers[id] = Container 35 | #print(dir(Container)) 36 | predicate = CNContact.predicateForContactsInContainerWithIdentifier_(id) 37 | # keys not exactly like in Apple doc 38 | # found a sample here https://github.com/tdamdouni/Pythonista/blob/master/contacts/Add%20Twitter%20Profile%20Picture%20to%20iOS%20Contacts.py 39 | predicate_contacts = CNContactStore.unifiedContactsMatchingPredicate_keysToFetch_error_(predicate, ['familyName','givenName','middleName'], None) 40 | for contact in predicate_contacts: 41 | # crash if attribute not in fetched contacts 42 | name = str(contact.givenName()) + '|' + str(contact.middleName()) + '|' + str(contact.familyName()) 43 | print(name) 44 | cont_per_name = objc_contacts.setdefault(name, []) 45 | cont_per_name.append(id) 46 | 47 | 48 | if __name__ == '__main__': 49 | main() -------------------------------------------------------------------------------- /label with attributedtext.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # experiments with attributed strings 3 | # https://github.com/khilnani/pythonista-scripts/blob/master/thirdparty/ObjC%20Tools/Jsbain-objc_hacks/attribtxt.py 4 | 5 | from rubicon.objc import * 6 | 7 | NSMutableAttributedString=ObjCClass('NSMutableAttributedString') 8 | NSFontAttributeName = 'NSFont' 9 | UIFont=ObjCClass('UIFont') 10 | 11 | attrtext = NSMutableAttributedString.alloc() 12 | strtext='This is a ui.Label with attributed strings!' 13 | attrtext.initWithString_(strtext) 14 | #print(dir(attrtext)) 15 | 16 | sz=6.0 17 | traits=0 18 | for i in range(int(len(strtext)/2)): 19 | f=UIFont.systemFontOfSize_traits_(sz,traits) 20 | nsr=NSRange(i,1) 21 | attrtext.addAttribute_value_range_(NSFontAttributeName,f,nsr) 22 | sz+=2.5 23 | traits+=1 24 | for i in range(int(len(strtext)/2),len(strtext)-1): 25 | f=UIFont.systemFontOfSize_traits_(sz,traits) 26 | nsr=NSRange(i,1) 27 | attrtext.addAttribute_value_range_(NSFontAttributeName,f,nsr) 28 | sz-=2.5 29 | traits+=1 30 | 31 | import pyto_ui as ui 32 | v=ui.View() 33 | v.frame=(0,0,576,576) 34 | v.background_color = ui.Color.rgb(0.7,0.7,0.7,1) 35 | lbl=ui.Label() 36 | bg_color='white' 37 | v.add_subview(lbl) 38 | 39 | lblobj=lbl.__py_view__.managed 40 | lblobj.setAttributedText_(attrtext) 41 | lbl.size_to_fit() 42 | ui.show_view(v,ui.PRESENTATION_MODE_SHEET) 43 | -------------------------------------------------------------------------------- /print_image.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | from mainthread import mainthread 3 | import photos 4 | import io 5 | 6 | def pil2ui(pilimage): 7 | with io.BytesIO() as bIO: 8 | pilimage.save(bIO, 'PNG') 9 | uiimage = ObjCClass('UIImage').alloc().initWithData_(bIO.getvalue()) 10 | return uiimage 11 | 12 | @mainthread 13 | def print_image_orientation(UIImages, orientation = 'P'): 14 | printController = ObjCClass('UIPrintInteractionController').sharedPrintController 15 | printInfo = ObjCClass('UIPrintInfo').printInfoWithDictionary_(None) 16 | # orientation P=Portrait L=Landscape 17 | printInfo.orientation = int(orientation[0].upper() == 'L') 18 | #printInfo.outputType = 0 # UIPrintInfoOutputGeneral 19 | # to avoid margins, we have to set output type as photo 20 | printInfo.outputType = 1 # UIPrintInfoOutputPhoto 21 | printController.printInfo = printInfo 22 | 23 | printController.setPrintingItems_(UIImages) 24 | printController.showsPageRange = True 25 | printController.showsPaperSelectionForLoadedPapers = True 26 | printController.showsNumberOfCopies = True 27 | #print(dir(printController)) 28 | printController.presentAnimated_completionHandler_(0, None) 29 | 30 | pil = photos.pick_photo() 31 | uiimage = pil2ui(pil) 32 | 33 | print_image_orientation([uiimage]) 34 | -------------------------------------------------------------------------------- /setIdleTimerDisabled.py: -------------------------------------------------------------------------------- 1 | from UIKit import * 2 | import mainthread 3 | 4 | app = UIApplication.sharedApplication 5 | 6 | @mainthread.mainthread 7 | def disab(): 8 | app.setIdleTimerDisabled_(True) 9 | 10 | disab() 11 | -------------------------------------------------------------------------------- /shader.py: -------------------------------------------------------------------------------- 1 | # see also http://battleofbrothers.com/sirryan/understanding-shaders-in-spritekit/ 2 | import pyto_ui as ui 3 | from rubicon.objc import * 4 | from Foundation import NSBundle 5 | from mainthread import mainthread 6 | from PIL import Image 7 | import io 8 | 9 | NSBundle.bundleWithPath_('/System/Library/Frameworks/SpriteKit.framework').load() 10 | 11 | UIApplication = ObjCClass('UIApplication') 12 | SKView = ObjCClass('SKView') 13 | SKScene = ObjCClass('SKScene') 14 | SKSpriteNode = ObjCClass('SKSpriteNode') 15 | SKShader = ObjCClass('SKShader') 16 | SKTexture = ObjCClass('SKTexture') 17 | UIColor = ObjCClass('UIColor') 18 | SKUniform = ObjCClass('SKUniform') 19 | 20 | def pil2ui(pilimage): 21 | with io.BytesIO() as bIO: 22 | pilimage.save(bIO, 'PNG') 23 | uiimage = ObjCClass('UIImage').alloc().initWithData_(bIO.getvalue()) 24 | return uiimage 25 | 26 | 27 | def get_screen_size(): 28 | app = UIApplication.sharedApplication.keyWindow 29 | for window in UIApplication.sharedApplication.windows: 30 | ws = window.bounds.size.width 31 | hs = window.bounds.size.height 32 | break 33 | return ws,hs 34 | 35 | class DemoView(ui.View): 36 | 37 | def __init__(self, *args, **kwargs): 38 | super().__init__(*args, **kwargs) 39 | #self.pil = Image.open('water_texture.jpg') 40 | self.pil = Image.open('IMG_8955.jpg') 41 | self.uiimage = pil2ui(self.pil) 42 | 43 | 44 | @mainthread 45 | def did_appear(self): 46 | # SKView can only be created on a presented view 47 | # Setup SKView 48 | screen_size = get_screen_size() 49 | sz = CGSize(screen_size[0], screen_size[1]-100) 50 | rect = CGRect(CGPoint(0, 0), sz) 51 | skview = SKView.alloc().initWithFrame_(rect) 52 | self.__py_view__.managed.addSubview(skview) 53 | self.skview = skview 54 | 55 | scene = SKScene.sceneWithSize_(rect.size) 56 | scene.backgroundColor = UIColor.yellowColor 57 | 58 | #node = SKSpriteNode.alloc().init() 59 | # initWithImageNamed_ does not find file, that is why I load file in FloatingPointError 60 | # file seems to be in a specific Pyto folder, but where? 61 | #node = SKSpriteNode.alloc().initWithImageNamed_('IMG_8955.jpg') 62 | texture = SKTexture.textureWithImage_(self.uiimage) 63 | node = SKSpriteNode.alloc().initWithTexture_(texture) 64 | wi,hi = self.pil.size 65 | w = 300 66 | h = w * hi/wi 67 | node.size = CGSize(w, h) 68 | node.position = CGPoint(screen_size[0]/2, screen_size[1]/2) 69 | 70 | spritew = SKUniform.uniformWithName_float_('spritew',w) 71 | spriteh = SKUniform.uniformWithName_float_('spriteh',h) 72 | 73 | shader_text1 = ''' 74 | // only to use position.y 75 | void main() { 76 | vec2 position = gl_FragCoord.xy; 77 | vec4 color = texture2D(u_texture, v_tex_coord); 78 | //color.r = 0; // ignore red 79 | color.g = position.y/spriteh; // green variable in function of y 80 | gl_FragColor = color; 81 | } 82 | ''' 83 | shader_text2 = ''' 84 | // invert colors 85 | void main() { 86 | // find the current pixel color 87 | vec4 current_color = texture2D(u_texture, v_tex_coord); 88 | // subtract its current RGB values from 1 and use its current alpha; 89 | // multiply by the node alpha so we can fade in or out 90 | gl_FragColor = vec4(1.0 - current_color.rgb, current_color.a) * current_color.a * v_color_mix.a; 91 | } 92 | ''' 93 | shader_text3 = ''' 94 | // ripple effect https://github.com/matthewreagan/SpriteKitShaders/blob/master/SpriteKitShaders/Shaders/simpleLiquidShader.fsh 95 | void main() { 96 | // Set up some animation parameters for the waveform 97 | float speed = u_time * 0.35; 98 | float frequency = 14.0; 99 | float intensity = 0.006; 100 | // Get the coordinate for the target pixel 101 | vec2 coord = v_tex_coord; 102 | // Modify (offset slightly) using a sine wave 103 | coord.x += cos((coord.x + speed) * frequency) * intensity; 104 | coord.y += sin((coord.y + speed) * frequency) * intensity; 105 | // Rather than the original pixel color, using the offset target pixel 106 | vec4 targetPixelColor = texture2D(u_texture, coord); 107 | // Finish up by setting the actual color on gl_FragColor 108 | gl_FragColor = targetPixelColor; 109 | } 110 | ''' 111 | 112 | shader_text4 = ''' 113 | // erase by circles 114 | void main() { 115 | vec4 color = texture2D(u_texture,v_tex_coord); 116 | // center of image is at v_tex_coord = 0.5,0.5 117 | float r = sqrt(pow(v_tex_coord.x-0.5,2.)+pow(v_tex_coord.y-0.5,2.)); 118 | if (r > cos(u_time/2.)) color.rgb = vec3(1.,1.,0.); 119 | gl_FragColor = vec4(color.rgb, 1); 120 | } 121 | ''' 122 | 123 | shader_text5 = ''' 124 | // pixelisation 125 | void main() { 126 | float px = 20.; 127 | float dx = px*cos(u_time/4.)*(1./spritew); 128 | float dy = px*cos(u_time/4.)*(1./spriteh); 129 | vec2 coord = vec2(dx*floor(v_tex_coord.x/dx), dy*floor(v_tex_coord.y/dy)); 130 | gl_FragColor = texture2D(u_texture,coord); 131 | } 132 | ''' 133 | 134 | shader = SKShader.shaderWithSource(shader_text3) 135 | shader.uniforms = [spritew,spriteh] 136 | 137 | node.setShader_(shader) 138 | scene.addChild_(node) 139 | 140 | skview.presentScene_(scene) 141 | self.scene = scene 142 | 143 | def did_disappear(self): 144 | self.skview.paused = True 145 | 146 | 147 | if __name__ == '__main__': 148 | view = DemoView() 149 | view.title = 'GLSL shader' 150 | ui.show_view(view,ui.PRESENTATION_MODE_FULLSCREEN) -------------------------------------------------------------------------------- /shadow.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | 4 | UIColor = ObjCClass('UIColor') 5 | 6 | view = ui.View() 7 | view.title = 'shadow' 8 | view.frame=(0,0,200,200) 9 | box = ui.View() 10 | box.frame=(0,0,100,100) 11 | 12 | view.background_color = ui.COLOR_WHITE 13 | background_color = 'white' 14 | box.background_color = ui.COLOR_RED 15 | box.center = view.center 16 | 17 | view.add_subview(box) 18 | 19 | box_pntr = box.__py_view__.managed 20 | 21 | box_pntr.layer.setMasksToBounds_(False) 22 | box_pntr.layer.setCornerRadius_(6) 23 | box_pntr.layer.setBorderColor_(UIColor.cyanColor.CGColor) 24 | box_pntr.layer.setShadowColor_(UIColor.blueColor.CGColor) 25 | box_pntr.layer.setBorderWidth_(3) 26 | box_pntr.layer.setShadowRadius_(10) 27 | box_pntr.layer.setShadowOffset_(CGSize(0,0)) 28 | box_pntr.layer.setShadowOpacity_(1.0) 29 | 30 | ui.show_view(view,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /ui_view_rotate.py: -------------------------------------------------------------------------------- 1 | from rubicon.objc import * 2 | import pyto_ui as ui 3 | from math import cos,sin,pi 4 | 5 | class MyClass(ui.View): 6 | def __init__(self, *args, **kwargs): 7 | super().__init__(*args, **kwargs) 8 | b = ui.Button() 9 | b.frame = (100,100,100,32) 10 | b.title = 'button' 11 | b.border_width = 1 12 | b.corner_radius = 10 13 | self.add_subview(b) 14 | o = b.__py_view__.managed 15 | a = -pi/4 16 | # no way found to create a CGAffineTransform structure in Pyto 17 | t = o.transform 18 | t.field_0 = cos(a) 19 | t.field_1 = -sin(a) 20 | t.field_2 = sin(a) 21 | t.field_3 = cos(a) 22 | t.field_4 = 0 23 | t.field_5 = 0 24 | o.transform = t 25 | 26 | if __name__ == '__main__': 27 | w, h = 400,300 28 | f = (0, 0, w, h) 29 | mc = MyClass() 30 | mc.frame=f 31 | mc.background_color = ui.COLOR_WHITE 32 | ui.show_view(mc,ui.PRESENTATION_MODE_SHEET) -------------------------------------------------------------------------------- /webserver.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows how to run a basic web server in background. 3 | https://www.itsecguy.com/custom-web-server-responses-with-python/ 4 | """ 5 | 6 | 7 | from http.server import BaseHTTPRequestHandler, HTTPServer 8 | from webbrowser import open 9 | from background import BackgroundTask # For running in background 10 | import notifications as nc 11 | import threading 12 | 13 | # The server request handler 14 | class RequestHandler(BaseHTTPRequestHandler): 15 | def do_GET(self): 16 | 17 | # Send a notification when a request is made 18 | notification = nc.Notification() 19 | notification.message = "Made GET Request" 20 | nc.schedule_notification(notification, 1, False) 21 | 22 | # Send "Hello World" 23 | message = "Hello World!" 24 | 25 | self.protocol_version = "HTTP/1.1" 26 | self.send_response(200) 27 | self.send_header("Content-Length", len(message)) 28 | self.end_headers() 29 | 30 | self.wfile.write(bytes(message, "utf8")) 31 | return 32 | 33 | class task(threading.Thread): 34 | def __init__(self,server): 35 | threading.Thread.__init__(self) 36 | self.server = server 37 | def run(self): 38 | self.server.serve_forever() 39 | 40 | # Run the server 41 | def run(): 42 | server = ('', 80) 43 | httpd = HTTPServer(server, RequestHandler) 44 | httpd.serve_forever() 45 | 46 | # Open the localhost in Safari 47 | open("http://localhost") 48 | 49 | server = ('', 80) 50 | httpd = HTTPServer(server, RequestHandler) 51 | t = task(httpd) 52 | t.start() 53 | 54 | from pyto_ui import Alert 55 | alert = Alert("Hello", "Tap ok to stop webserver") 56 | alert.add_action("Ok") 57 | alert.show() 58 | t.server.shutdown() 59 | 60 | --------------------------------------------------------------------------------