• blmacbeth

    I was playing around with the location module the other day and was wondering about how it deals with scripts closing without manually turning off location tracking. So I made a context Manager for the location module

    import location
    
    
    class LocationManager (object):
        def __enter__(self):
            location.start_updates()
    
        def __exit__(self):
            location.stop_updates()
    

    You can use it like so:

    with LocationManager():
        # Do stuff that requires `location` here...
        ...
    

    Now, this may be completely unnecessary, but I have found it useful for applications where constant location updates are not needed (e.g. get my location every 15 minutes or set my initial location for some one off script).

    @omz also think this may be a good feature for the built-in location module to adopt (if possible). So we can have both location.start(stop)_updates() or with location: ....

    Let me know what y'all think (criticism is welcome).

    B.

    EDIT
    A bit less verbose code with the help of @ccc on contexlib

    import contexlib
    import location
    
    @contexlib.contexmanager
    def location_enabled():
        location.start_updates()
        yield
        location.stop_updates()
    

    the use case is still the same.

    with location_enabled():
        # Do location stuff here...
        ...
    

    posted in Pythonista read more
  • blmacbeth

    @bistrot Add the #! python2 she-bang at the top of the script to tell it to use the (possibly) nom-default python 2.7 interpreter.

    posted in Pythonista read more
  • blmacbeth

    @omz and @Cethric thanks for the help. It seems to work.

    posted in Pythonista read more
  • blmacbeth

    I have a script where I'm trying to get the system attributes; eventually this will gather memory information. I have isolated the crash to this line of code:

    from ctypes import POINTER
    from objc_util import ObjCClass, ObjCInstance, c, c_void_p
    
    
    NSHomeDirectory = c.NSHomeDirectory
    NSHomeDirectory.restype = c_void_p
    NSHomeDirectory.argtype = []
    
    NSFileManager = ObjCClass('NSFileManager')
    
    LP_c_void_p = POINTER(c_void_p)
    
    def get_system_attributes():
        file_manager = NSFileManager.defaultManager()
        error = LP_c_void_p()
        attributes = file_manager.attributesOfFileSystemForPath_error_(
            ObjCInstance(
                NSHomeDirectory()
            ).cString(),
            error
        )
        return attributes
    
    get_system_attributes()
    

    attributesOfFileSystemForPath_error_ takes a string and an error pointer. It crashes when it gets to the error inside that function. I think it crashes due to an NSError object being created. Any ideas on how to stop the crash?

    posted in Pythonista read more
  • blmacbeth

    @Phuket2 quoted from the Apple developer forums:

    Retina Flash
    iPhone 6s and 6s Plus contain a custom display chip that allows the retina display to briefly flash 3 times brighter than its usual maximum illuminance. No new API was added to support this feature. Since iOS 4, AVCaptureDevice has supported the -hasFlash, -isFlashModeSupported: and -flashMode properties. The iPhone 6s and 6s Plus front-facing cameras are the first front-facing iOS cameras to respond YES to the -hasFlash property. By setting the front-facing camera's flashMode to AVCaptureFlashModeOn or AVCaptureFlashModeAuto, the retina flash fires when a still image is captured (see AVCaptureStillImageOutput’s captureStillImageAsynchronouslyFromConnection:completionHandler:), just as the True Tone flash fires for rear-facing camera stills.

    Hope this. Only works on 6s, 6s+, and SE.
    B.

    posted in Pythonista read more
  • blmacbeth

    @Tizzy sorry for not explaining myself better. @dgelessus is correct about the ? being the important part.

    I checked MySQL and it has its own syntax for variable arguments that just so happens to be %s. Shame on them…

    posted in Pythonista read more
  • blmacbeth

    @Tizzy it is good practice to not do statement = 'SELECT * FROM ' + table_name' or even statement = 'SELECT * FROM ' + %s' % table_name as this can cause security issues with SQL injection. Most database packages (read: modules) will have something along the lines of

    statement = '''
    SELECT *
    FROM ?
    '''
    with db.connect as conn:
        result = conn.execute(statement, (table_name,))
    

    This is a more secure way of accessing batabases. The other way is good enough for person projects, but keep that in mind or little Bobby Tables will make your life awful as a DBA.

    B.

    posted in Pythonista read more
  • blmacbeth

    @omz said:

    Btw, you can just pass a prefix to ObjCClass.get_names() instead of doing the filtering yourself (in the current beta, it would have to be a byte string though).

    Did not know that…

    posted in Pythonista read more
  • blmacbeth

    Hey @omz, I got back in to playing around with objc_util and found this:

    >>> dir(OMJavaScriptSyntaxHighlighter)
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/var/mobile/Containers/Bundle/Application/61407674-7331-47C2-B308-BDB7BBE52166/Pythonista3.app/Frameworks/PythonistaKit3.framework/pylib/site-packages/objc_util.py", line 380, in __dir__
        py_method_name = sel_name.replace(':', '_')
    TypeError: a bytes-like object is required, not 'str'
    

    This error come from the way Python3.* encodes ASCII vs Unicode strings. When you run the following:

    om_classes = [cls for cls in ObjCClass.get_names() if cls.startswith(b'OM')]
    for cls in om_classes:
        print(cls)
    

    You get the following (truncated for brevity):

    b'OMAutoresizingMaskView'
    b'OMBarButton'
    b'OMBarButtonItem'
    b'OMBaseSyntaxHighlighter'
    b'OMBasicPythonCompletionProvider'
    b'OMBasicPythonCompletionProviderTokenizedLine'
    b'OMButtonFieldSpecifier'
    b'OMCSSSyntaxHighlighter'
    b'OMCaretView'
    b'OMCheckCell'
    b'OMCheckFieldSpecifier'
    …
    b'OMUIWidgetViewScrollView'
    b'OMUIWidgetViewSegmentedControl'
    b'OMUIWidgetViewSlider'
    b'OMUIWidgetViewStickyNote'
    b'OMUIWidgetViewSwitch'
    b'OMUIWidgetViewTableView'
    b'OMUIWidgetViewTextField'
    b'OMUIWidgetViewTextView'
    b'OMUIWidgetViewWebView'
    

    Notice the b before the string. So, when you want to run string operations, which are now encoded as UTF-8, it throws a TypeError because ASCII is (for whatever reason) a byte-like object, not a string.

    To fix this you can have .get_names() return strings, or add the b prefix to the strings in replace() method in objc_util.

    posted in Pythonista read more
  • blmacbeth

    Noticed some bugs in the above code, so here are my fixes to make it work (in Pythonista3, at least).

    # coding: utf-8
    
    import ui
    from objc_util import *
    
    class BlurView (ui.View):
        def __init__(self, style=1, *args, **kwargs):
            ui.View.__init__(self, **kwargs)
            self._style = style
            self.effect_view = None
            self.setup_effect_view()
        
        @on_main_thread
        def setup_effect_view(self):
            if self.effect_view is not None:
                self.effect_view.removeFromSuperview()
            UIVisualEffectView = ObjCClass('UIVisualEffectView')
            UIVibrancyEffect = ObjCClass('UIVibrancyEffect')
            UIBlurEffect = ObjCClass('UIBlurEffect')
            UILabel = ObjCClass('UILabel')
            # Brooks patch for Rect not having a 'slice' 
            bounds = self.bounds.as_tuple()
            frame = (bounds[:2], bounds[2:])
            self.effect_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
            effect = UIBlurEffect.effectWithStyle_(self._style)
            self.effect_view.effect = effect
            self.effect_view.setAutoresizingMask_(18)
            ObjCInstance(self).addSubview_(self.effect_view)
            vibrancy_effect = UIVibrancyEffect.effectForBlurEffect_(effect)
            self.vibrancy_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
            self.vibrancy_view.effect = vibrancy_effect
            self.effect_view.contentView().addSubview_(self.vibrancy_view)
            
        @property
        def style(self):
            return self._style
        
        @style.setter
        def style(self, value):
            if value != self._style:
                self._style = value
                self.setup_effect_view()
        
        # Brooks fix @on_mains_thread to @on_main_thread
        @on_main_thread
        def add_vibrant_label(self, label):
            self.vibrancy_view.contentView().addSubview_(ObjCInstance(label))
    
    def main():
        image_view = ui.ImageView(frame=(0, 0, 320, 320))
        image_view.image = ui.Image.named('test:Mandrill')
        blur_view = BlurView(style=2, frame=image_view.bounds.inset(40, 40))
        image_view.add_subview(blur_view)
        label = ui.Label(frame=blur_view.bounds)
        label.text = 'Hello World'
        label.font = ('HelveticaNeue', 40)
        label.alignment = ui.ALIGN_CENTER
        blur_view.add_vibrant_label(label)
        image_view.present('sheet')
    
    main()
    

    The biggest problem was that the ui.Rect() object does not have the ability to use slices. This was fixed with the ui.Rect().as_tuple() function. The second problem was just a typo.

    B

    posted in Pythonista read more
  • blmacbeth

    In both the latest betas (Pythonista2 and Pythonista3) when running web_view.eval_js('alert(document.title)'), the alert is unable to be dismissed.

    Test code:

    import ui
    web_view = ui.WebView(frame=(0,0,500,500))
    web_view.present('sheet')
    web_view.load_url('https://www.google.com')
    web_view.eval_js('alert(document.title)')
    

    posted in Pythonista read more
  • blmacbeth

    If I'm not too late to the game, I have been using Interactive Editor for Python as my main "IDE" for coding Python on my Mac (it works on all platforms). It works very similarly to Pythonista: editor; file browser; interactive console; and other really nice tools. I particularly like the workspace tool, which showed you all of your currently instanciated stuff.

    posted in Pythonista read more
  • blmacbeth

    @omz You're the man! Thanks for that.

    posted in Pythonista read more
  • blmacbeth

    @omz I have been playing around with UIBlurView and UIVibrancyEffect, but have not been able to get the UIVibrancyEffect to work (text does not show up). Any suggestions on how to get it to work? I'm away from my iPad right now and can post some code later, if needed. But, my code is based on the following Objective-C code:

    // Blur effect
    UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
    UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
    [blurEffectView setFrame:self.view.bounds];
    [self.view addSubview:blurEffectView];
    
    // Vibrancy effect
    UIVibrancyEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:blurEffect];
    UIVisualEffectView *vibrancyEffectView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
    [vibrancyEffectView setFrame:self.view.bounds];
    
    // Label for vibrant text
    UILabel *vibrantLabel = [[UILabel alloc] init];
    [vibrantLabel setText:@"Vibrant"];
    [vibrantLabel setFont:[UIFont systemFontOfSize:72.0f]];
    [vibrantLabel sizeToFit];
    [vibrantLabel setCenter: self.view.center];
    
    // Add label to the vibrancy view
    [[vibrancyEffectView contentView] addSubview:vibrantLabel];
    
    // Add the vibrancy view to the blur view
    [[blurEffectView contentView] addSubview:vibrancyEffectView];
    

    Thanks,
    B

    posted in Pythonista read more
  • blmacbeth

    @Phuket2 I wish I have a cool trick up my sleeve! But to comment on your response about not calling super: it is best practice to call super when subclassing. This is because it allows you to refactor your code with less changes made afterwards.

    For example: let's say I someday make a kick-ass version of UIView and UIViewController that make ui.View look like children's toys. So, you want to incorporate the new view classes into your old code. The way you have, you would need to change every instance of ui.View.__init__(...). By calling super you no longer have to do all that tedious work. It makes life slightly easier.

    I'm not saying that what your doing is wrong; I have plenty of classes that do the same thing. But o have started using super because of the refactoring issue.

    Now, this could all be a load of bullshit I am feeding you, so go look it up yourself and let me know if I'm correct! 😛

    B.

    posted in Pythonista read more
  • blmacbeth

    @Phuket2 Ha! You caught me. That may be a problem… it should look like:

    super(ShadowView, self).__init__(*args, **kwargs)
    

    I must have been in a zone to miss that. There is no need to repeat self in the __init__ call because super(ShadowView, self) returns an instance of the superclass, which will pass self implicitly.

    posted in Pythonista read more
  • blmacbeth

    Ask and he shall receive. It's not perfect, yet. There are some clipping problems I can't figure out.

    # coding: utf-8
    from objc_util import *
    import ui
    
    UIColor = ObjCClass('UIColor')
    
    def Color(red=0, green=0, blue=0, alpha=1):
        return UIColor.colorWithRed_green_blue_alpha_(red, green, blue, alpha)
    
    class ShadowView (ui.View):
        def __init__(self, *args, **kwargs):
            super(ShadowView, self).__init__()
            self.pntr = ObjCInstance(self)
            self.pntr.layer().setMasksToBounds_(False) ## Go ahead and do this.
            
        @property 
        def corner_radius(self):
            return self.pntr.layer().cornerRadius()
            
        @corner_radius.setter
        def corner_radius(self, val):
            self.pntr.layer().setCornerRadius_(val)
            
        @property
        def border_color(self):
            return self.pntr.layer().borderColor()
            
        @border_color.setter
        def border_color(self, color):
            self.pntr.layer().setBorderColor_(Color(*color).CGColor())
            
        @property
        def border_width(self):
            return self.pntr.layer().borderWidth()
            
        @border_width.setter
        def border_width(self, val):
            self.pntr.layer().setBorderWidth_(val)
            
        @property 
        def opacity(self):
            return self.pntr.layer().opacity()
            
        @opacity.setter
        def opacity(self, val):
            self.pntr.layer().setOpacity_(value)
            
        @property 
        def hidden(swlf):
            return self.pntr.layer().hidden()
            
        @hidden.setter
        def hidden(self, val):
            self.pntr.layer().setHidden_(val)
            
        @property 
        def masks_to_bounds(self):
            return self.pntr.layer().masksToBounds()
            
        @masks_to_bounds.setter
        def masks_to_bounds(self, val):
            self.pntr.layer().setMasksToBounds_(val)
            
        @property 
        def mask(self):
            return self.pntr.layer().mask()
            
        @mask.setter
        def mask(self, new_mask):
            self.pntr.layer().setMask_(new_mask)
            
        @property
        def double_sided(self):
            return self.pntr.layer().doubleSided()
            
        @double_sided.setter
        def double_sided(self, val):
            self.pntr.layer().setDoubleSided_(val)
            
        @property
        def shadow_opacity(self):
            return self.pntr.layer().shadowOpacity()
            
        @shadow_opacity.setter
        def shadow_opacity(self, val):
            self.pntr.layer().setShadowOpacity_(val)
            
        @property
        def shadow_radius(self):
            return self.pntr.layer().shadowRadius()
            
        @shadow_radius.setter
        def shadow_radius(self, val):
            self.pntr.layer().setShadowRadius_(val)
            
        @property
        def shadow_offset(self):
            return self.pntr.layer().shadowOffset()
            
        @shadow_offset.setter
        def shadow_offset(self, offset):
            ## offset should be a tuple, but I'll take a CGSize
            if isinstance(offset, CGSize):
                self.pntr.layer().setShadowOffset_(offset)
            elif isinstance(offset, tuple):
                self.pntr.layer().setShadowOffset_(CGSize(*offset))
            else:
                raise TypeError("Cannot use type %s. Use CGSize or tuple" % type(offset))
                
        @property
        def shadow_color(self):
            return self.pntr.layer().shadowColor()
            
        @shadow_color.setter
        def shadow_color(self, color):
            if isinstance(color, UIColor.CGColor()):
                self.pntr.layer().setShadowColor_(color)
            elif isinstance(color, tuple) and len(color) == 4:
                self.pntr.layer().setShadowColor_(Color(*color).CGColor())
            else:
                raise ValueError('Cannot use type %s. Use UIColor or tuple' % type(color))
                
            @property
            def shadow_path(self):
                return self.pntr.layer().shadowPath()
                
            @shadow_path.setter
            def shadow_path(self, path):
                self.pntr.layer().setShadowPath_(path)
                
            @property
            def style(self):
                return self.pntr.layer().style()
                
            @style.setter
            def style(self, style):
                self.pntr.layer().setStyle_(style)
                
    if __name__ == '__main__':  
        view = ui.View(frame=(0,0,500,500))
        box  = ShadowView(frame=(0,0,100,100))
        
        view.background_color = 'white'
        box.background_color = 'red'
        box.center = view.center
        
        view.add_subview(box)
        
        box.masks_to_bounds = False
        box.corner_radius = 6.
        box.border_color = (0,1,0)
        box.border_width = 6
        box.shadow_radius = 10
        box.shadow_offset = (0,0)
        box.shadow_opacity = 1
        
        view.present('sheet')
    

    I tested most of it, but there may still be some funny-ness.

    B.

    posted in Pythonista read more
  • blmacbeth

    I have been playing around with objc_util again and came up with this piece of code you might find applicable to your code above:

    # coding: utf-8
    from objc_util import *
    import ui
    
    UIColor = ObjCClass('UIColor')
    
    view = ui.View(frame=(0,0,500,500))
    box  = ui.View(frame=(0,0,100,100))
    
    view.background_color = 'white'
    box.background_color = 'red'
    box.center = view.center
    
    view.add_subview(box)
    
    box_pntr = ObjCInstance(box)
    ## Note: this allows for shadows to be drawn
    box_pntr.layer().setMasksToBounds_(False)
    box_pntr.layer().setCornerRadius_(6)
    ## Note: CGColor is needed in order for this to work
    box_pntr.layer().setBorderColor_(UIColor.cyanColor().CGColor())
    box_pntr.layer().setBorderWidth_(3)
    box_pntr.layer().setShadowRadius_(10)
    box_pntr.layer().setShadowOffset_(CGSize(0,0))
    box_pntr.layer().setShadowOpacity_(1.0)
    
    view.present('sheet')
    

    Hope you find this useful.

    posted in Pythonista read more
  • blmacbeth

    That's weird. I wrote and tested the script on my iPad. Did you run the exact script in the linked post? Should be the second script that doesn't crash.

    Here is the code, if you don't care to guess.

    B.

    # coding: utf-8
    from objc_util import *
    import ctypes
    import ui
    
    SUIViewController = ObjCClass('SUIViewController')
    UIAlertController = ObjCClass('UIAlertController')
    UIAlertAction     = ObjCClass('UIAlertAction')
    
    def ok_pressed(sender):
        print 'OK pressed', ObjCInstance(sender), dir(ObjCInstance(sender))
    
    alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_(ns('My Alert'), ns('My Message'), 0)
    alert_action_block = ObjCBlock(ok_pressed, None, [c_void_p])
    default_action = UIAlertAction.actionWithTitle_style_handler_(ns('OK'), 0, alert_action_block)
    alert.addAction_(default_action)
    ##rvc.presentModalViewController_animated_(alert, True)
    
    ## Stop Crashes
    retain_global(alert_action_block)
    
    def button_tapped(sender):
        super_view = sender.superview
        super_view_pntr = ObjCInstance(super_view)
        vc = SUIViewController.viewControllerForView_(super_view_pntr)
        vc.presentModalViewController_animated_(alert, True)
        
    view = ui.View(frame=(0,0,500,500))
    view.name = 'Demo'
    view.background_color = 'white'
    button = ui.Button(title='Tap me!')
    button.center = (view.width * 0.5, view.height * 0.5)
    button.flex = 'LRTB'
    button.action = button_tapped
    view.add_subview(button)
    view.present('sheet')
    

    posted in Pythonista read more
  • blmacbeth

    I know this! I made a post a while back when I was experimenting with objc_util. The basic idea is to get the SUIViewController (a special Pythonista subclass of UIViewController) and use that to present the alert. Things tend to crash otherwise…

    Here is a link to my post, which has snippets of code that can get you started.

    B

    https://forum.omz-software.com/topic/2314/share-code-uialertcontroller

    posted in Pythonista read more
Internal error.

Oops! Looks like something went wrong!