示例#1
0
    def __init__(self):
        global _datasource

        self.name = 'Drag & Drop'

        self.width = min(ui.get_window_size()[0] * 0.8, 700)
        self.height = ui.get_window_size()[1] * 0.8

        path = editor.get_path()
        if path:
            expanded_folder = os.path.dirname(path)
            files = tab.get_paths()
        else:
            expanded_folder = None
            files = None

        root_node = FileNode(os.path.expanduser('~/Documents'), ignore=ignore)
        _datasource = FolderPickerDataSource(root_node, expanded_folder, files)

        tv = ui.TableView(frame=self.bounds, flex='WH')
        tv.delegate = _datasource
        tv.data_source = _datasource
        tv.allows_multiple_selection = False
        tv.allows_selection = True
        tv.allows_multiple_selection_during_editing = False
        tv.allows_selection_during_editing = False
        tv.separator_color = 'clear'
        self.add_subview(tv)

        methods = [tableView_itemsForBeginningDragSession_atIndexPath_]
        protocols = ['UITableViewDragDelegate']
        DragDelegate = create_objc_class('DragDelegate',
                                         methods=methods,
                                         protocols=protocols)
        self._drag_delegate = DragDelegate.alloc().init()

        methods = [
            tableView_canHandleDropSession_,
            tableView_dropSessionDidUpdate_withDestinationIndexPath_,
            tableView_performDropWithCoordinator_
        ]
        protocols = ['UITableViewDropDelegate']
        DropDelegate = create_objc_class('DropDelegate',
                                         methods=methods,
                                         protocols=protocols)
        self._drop_delegate = DropDelegate.alloc().init()

        tv_objc = ObjCInstance(tv)
        tv_objc.setDragDelegate_(self._drag_delegate)
        tv_objc.setDropDelegate_(self._drop_delegate)

        def handle_escape():
            self.close()

        self._handlers = [
            register_key_event_handler(UIEventKeyCode.ESCAPE, handle_escape),
            register_key_event_handler(UIEventKeyCode.DOT,
                                       handle_escape,
                                       modifier=UIKeyModifier.COMMAND)
        ]
示例#2
0
 def customVC(self):
     return create_objc_class(
         "CustomViewController",
         UIViewController,
         methods=[],
         protocols=["OMTabContent"],
     ).new().autorelease()
示例#3
0
	def customVC(self):
		return create_objc_class(
			"CustomViewController",
			UIViewController,
			methods=[wtShare, wtGoBack, wtGoForward, searchBarSearchButtonClicked_],
			protocols=["OMTabContent", "UISearchBarDelegate"],
		).new().autorelease()
示例#4
0
 def customVC(self):
     return create_objc_class(
             "CustomViewController",
             UIViewController,
             methods=[],
             protocols=["OMTabContent"],
     ).new()
示例#5
0
    def __init__(self, l_mpc):
        self._mpc = l_mpc
        self._ndc = ObjCClass('NSNotificationCenter').defaultCenter()

        def willResignActive(_self, _cmd):
            pass

        def didBecomeActive(_self, _cmd):
            pass

        def playbackStateDidChange(_self, _cmd):
            self._mpc.updatePlaybackState()

        def nowPlayingItemDidChange(_self, _cmd):
            self._mpc.setNowPlayingSongArtwork()

        self._method_table = {
            'UIApplicationWillResignActiveNotification':
            willResignActive,
            'UIApplicationDidBecomeActiveNotification':
            didBecomeActive,
            'MPMusicPlayerControllerPlaybackStateDidChangeNotification':
            playbackStateDidChange,
            'MPMusicPlayerControllerNowPlayingItemDidChangeNotification':
            nowPlayingItemDidChange,
        }
        self._mp4p_nc = create_objc_class(
            'NSMP4PNotificationController',
            methods=self._method_table.values()).new()
示例#6
0
 def customVC(self):
     return create_objc_class(
         "CustomViewController",
         UIViewController,
         methods=[
             wtShare, wtGoBack, wtGoForward, searchBarSearchButtonClicked_
         ],
         protocols=["OMTabContent", "UISearchBarDelegate"],
     ).new().autorelease()
示例#7
0
    def __new__(cls, *args, **kwargs):
        objc_class = getattr(cls, '_objc_class', None)
        if objc_class is None:
            objc_class_name = cls.__name__ + '_ObjC'
            objc_superclass = getattr(
                cls, '_objc_superclass', objc_util.NSObject)
            objc_debug = getattr(cls, '_objc_debug', True)
            
            #'TempClass_'+str(uuid.uuid4())[-12:]
            
            objc_methods = []
            objc_classmethods = []
            for key in cls.__dict__:
                value = getattr(cls, key)
                if (inspect.isfunction(value) and 
                    '_self' in inspect.signature(value).parameters
                ):
                    if getattr(value, '__self__', None) == cls:
                        objc_classmethods.append(value)
                    else:
                        objc_methods.append(value)
            '''
            objc_methods = [value
                for value in cls.__dict__.values()
                if (
                    callable(value) and 
                    '_self' in inspect.signature(value).parameters
                )
            ]
            '''
            if ObjCDelegate in cls.__mro__:
                objc_protocols = [cls.__name__]
            else:
                objc_protocols = getattr(cls, '_objc_protocols', [])
            if not type(objc_protocols) is list:
                objc_protocols = [objc_protocols]
            cls._objc_class = objc_class = objc_util.create_objc_class(
                objc_class_name,
                superclass=objc_superclass,
                methods=objc_methods,
                classmethods=objc_classmethods,
                protocols=objc_protocols,
                debug=objc_debug
            )
        
        instance = objc_class.alloc().init()

        for key in dir(cls):
            value = getattr(cls, key)
            if inspect.isfunction(value):
                if not '_self' in inspect.signature(value).parameters:
                    setattr(instance, key, types.MethodType(value, instance))
                if key == '__init__':
                    value(instance, *args, **kwargs)
        return instance
 def __init__(self):
   # create delegate
   methods = [
     renderer_didAddNode_forAnchor_, #renderer_didUpdateNode_forAnchor_,
     #renderer_didRemoveNode_forAnchor_
   ]
   protocols = ['ARSCNViewDelegate']
   pyARSCNViewDelegate = create_objc_class(
     'pyARSCNViewDelegate', methods=methods, protocols=protocols)
   self.view_did_load()
   self.view_will_appear(pyARSCNViewDelegate)
def createSearchDelegateClass():
    methods = [
        searchBar_textDidChange_, searchBarTextDidBeginEditing_,
        searchBarTextDidEndEditing_, searchBarCancelButtonClicked_,
        searchBarSearchButtonClicked_, updateSearchResultsForSearchController_
    ]
    protocols = ['UISearchBarDelegate', 'UISearchResultsUpdating']
    sd = create_objc_class('sd',
                           NSObject,
                           methods=methods,
                           protocols=protocols,
                           debug=True)
    return sd
示例#10
0
def createTableViewDelegateClass(tm):
    methods = [
        tableView_cellForRowAtIndexPath_, tableView_numberOfRowsInSection_,
        numberOfSectionsInTableView_, tableView_didSelectRowAtIndexPath_
    ]
    protocols = ['UITableViewDataSource', 'UITableViewDelegate']
    TVDataSourceAndDelegate = create_objc_class('TVDataSourceAndDelegate',
                                                NSObject,
                                                methods=methods,
                                                protocols=protocols,
                                                debug=True)
    TVDataSourceAndDelegate.tm = tm
    return TVDataSourceAndDelegate
示例#11
0
def createSearchDelegateClass():

    methods = [
        searchBar_textDidChange_, searchBarTextDidBeginEditing_,
        searchBarTextDidEndEditing_, searchBarCancelButtonClicked_,
        searchBarSearchButtonClicked_
    ]
    protocols = ['UISearchBarDelegate']
    try:
        sd = ObjCClass('sd')
        return sd
    except ValueError:
        sd = create_objc_class('sd',
                               NSObject,
                               methods=methods,
                               protocols=protocols,
                               debug=False)
        return sd
示例#12
0
def _start_notification_listener():
    global _listener
    if _listener is not None:
        _logger.debug(
            'External screen notification listener is already running')
        return

    methods = [screenDidConnectNotification_, screenDidDisconnectNotification_]
    listener_class = objc_util.create_objc_class('ExternalScreenListener',
                                                 methods=methods)
    _listener = listener_class.alloc().init()

    NSNotificationCenter = objc_util.ObjCClass('NSNotificationCenter')
    default_center = NSNotificationCenter.defaultCenter()

    default_center.addObserver_selector_name_object_(
        _listener, objc_util.sel('screenDidConnectNotification:'),
        'UIScreenDidConnectNotification', None)
    default_center.addObserver_selector_name_object_(
        _listener, objc_util.sel('screenDidDisconnectNotification:'),
        'UIScreenDidDisconnectNotification', None)

    _logger.debug('External screen notification listener started')
示例#13
0
                         alpha=1.0)


def DemoScene_touchesBegan_withEvent_(_self, _cmd, _touches, event):
    scene = ObjCInstance(_self)
    touches = ObjCInstance(_touches)
    for id, touch in enumerate(touches):
        point = touch.locationInNode_(scene)
        node = random.choice([create_circle_shape, create_box_shape])(point)
        node.fillColor = random_color()
        scene.addChild_(node)


DemoScene = create_objc_class(
    'DemoScene',
    SKScene,
    methods=[DemoScene_update_, DemoScene_touchesBegan_withEvent_],
    protocols=[])


class DemoView(ui.View):

    debug = True

    def __init__(self):
        # Setup SKView
        screen_size = ui.get_screen_size()
        rect = CGRect(CGPoint(0, 0), CGSize(screen_size[0], screen_size[1]))
        skview = SKView.alloc().initWithFrame_(rect)
        # debug
        skview.showsFPS = self.debug
示例#14
0
UNNotificationCategory = objc_util.ObjCClass("UNNotificationCategory")
UNNotificationRequest = objc_util.ObjCClass("UNNotificationRequest")
UNTimeIntervalNotificationTrigger = objc_util.ObjCClass("UNTimeIntervalNotificationTrigger")
UNUserNotificationCenter = objc_util.ObjCClass("UNUserNotificationCenter")
​
def userNotificationCenter_willPresentNotification_withCompletionHandler_(self, _cmd, unc, notification, handler):
	pass
​
def userNotificationCenter_didReceiveNotificationResponse_withCompletionHandler_(self, _cmd, unc, response, handler):
	print(objc_util.ObjCInstance(response))
​
DGUNUNCDelegate = objc_util.create_objc_class(
	"DGUNUNCDelegate",
	methods=[
		userNotificationCenter_willPresentNotification_withCompletionHandler_,
		userNotificationCenter_didReceiveNotificationResponse_withCompletionHandler_
	],
	protocols=[
		"UNUserNotificationCenterDelegate"
	]
)
​
unc = UNUserNotificationCenter.currentNotificationCenter()
unc.requestAuthorizationWithOptions_completionHandler_((1<<2) | (1<<1) | (1<<0), None)
unc.setDelegate_(DGUNUNCDelegate.alloc().init())
doit = UNNotificationAction.actionWithIdentifier_title_options_("doit", "Just DO IT!", 0)
ornot = UNNotificationAction.actionWithIdentifier_title_options_("ornot", "Or not", 0)
foocat = UNNotificationCategory.categoryWithIdentifier_actions_intentIdentifiers_options_("foocat", [doit, ornot], [], 0)
unc.setNotificationCategories_({foocat})
​
def errorhandler_imp(_cmd, error):
	if error is None:
示例#15
0
Original wrapper code by Samer in forums.
Added iOS 14 features.
"""

import ui
from objc_util import ObjCClass, CGRect, create_objc_class, ObjCInstance, UIColor

UIPageControl = ObjCClass('UIPageControl')


def changePage(_self, _cmd):
    self = ObjCInstance(_self)
    self.page_control.set_page(self.page_control.pageControl.currentPage())


ChangePageClass = create_objc_class("ChangePageClass", methods=[changePage])


class PageControl(ui.View):
    def __init__(self, **kwargs):

        self.scrollView = ui.ScrollView(
            delegate=self,
            paging_enabled=True,
            shows_horizontal_scroll_indicator=False,
            bounces=False,
            frame=self.bounds,
            flex='WH',
        )

        self.pageControl = UIPageControl.alloc().init().autorelease()
示例#16
0
    dat = objc.classes.AVCapturePhotoOutput.DNGPhotoDataRepresentationForRawSampleBuffer_previewPhotoSampleBuffer_(
        buf or objc.ffi.NULL,
        prevbuf or objc.ffi.NULL,
        restype="id",
        argtypes=["id", "id"])
    print("Updated dat")
    dat.writeToFile_atomically_(os.path.expanduser("~/Documents/foo.dng"),
                                False)
    print("Wrote foo.dng")


DGCaptureDelegate = objc.Class(
    objc_util.create_objc_class(
        name="DGCaptureDelegate",
        superclass=objc_util.NSObject,
        protocols=["AVCapturePhotoDelegate"],
        methods=[
            captureOutput_didFinishProcessingRawPhotoSampleBuffer_previewPhotoSampleBuffer_resolvedSettings_bracketSettings_error_,
        ],
    ))

if __name__ == "__main__":
    sess = objc.classes.AVCaptureSession.new()

    sess.setSessionPreset_(objc.libc.AVCaptureSessionPresetPhoto)

    cam = objc.classes.AVCaptureDevice.defaultDeviceWithMediaType_(
        objc.libc.AVMediaTypeVideo)

    capin = objc.classes.AVCaptureDeviceInput.deviceInputWithDevice_error_(
        cam, objc.ffi.NULL)
    capout = objc.classes.AVCapturePhotoOutput.new()
示例#17
0
    def __init__(self, stash, parent, superview, width, height):

        # Create the actual TextView by subclass SUITextView
        UIKeyCommand = ObjCClass('UIKeyCommand')

        def kcDispatcher_(_self, _cmd, _sender):
            key_cmd = ObjCInstance(_sender)
            stash.user_action_proxy.kc_pressed(str(key_cmd.input()), key_cmd.modifierFlags())

        def keyCommands(_self, _cmd):
            key_commands = [
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('C',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('D',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('P',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('N',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('K',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('U',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('A',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('E',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('W',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('L',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('Z',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('[',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_(']',
                                                                       CTRL_KEY_FLAG,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('UIKeyInputUpArrow',
                                                                       0,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('UIKeyInputDownArrow',
                                                                       0,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('UIKeyInputLeftArrow',
                                                                       0,
                                                                       'kcDispatcher:'),
                UIKeyCommand.keyCommandWithInput_modifierFlags_action_('UIKeyInputRightArrow',
                                                                       0,
                                                                       'kcDispatcher:'),
            ]
            commands = ns(key_commands)
            return commands.ptr


        self.kc_handlers = {
            ('C',
             CTRL_KEY_FLAG): parent.controlCAction,
            ('D',
             CTRL_KEY_FLAG): parent.controlDAction,
            ('P',
             CTRL_KEY_FLAG): parent.controlPAction,
            ('N',
             CTRL_KEY_FLAG): parent.controlNAction,
            ('K',
             CTRL_KEY_FLAG): parent.controlKAction,
            ('U',
             CTRL_KEY_FLAG): parent.controlUAction,
            ('A',
             CTRL_KEY_FLAG): parent.controlAAction,
            ('E',
             CTRL_KEY_FLAG): parent.controlEAction,
            ('W',
             CTRL_KEY_FLAG): parent.controlWAction,
            ('L',
             CTRL_KEY_FLAG): parent.controlLAction,
            ('Z',
             CTRL_KEY_FLAG): parent.controlZAction,
            ('[',
             CTRL_KEY_FLAG): parent.dummyAction,
            (']',
             CTRL_KEY_FLAG): parent.dummyAction,
            ('UIKeyInputUpArrow', 0):    parent.arrowUpAction,
            ('UIKeyInputDownArrow', 0):  parent.arrowDownAction,
            ('UIKeyInputLeftArrow', 0):  parent.arrowLeftAction,
            ('UIKeyInputRightArrow', 0): parent.arrowRightAction,
        }

        _ShTerminal = create_objc_class('_ShTerminal', ObjCClass('SUITextView'), [keyCommands, kcDispatcher_])

        self.is_editing = False

        self.superview = superview

        self._delegate_view = ui.TextView()
        self._delegate_view.delegate = stash.user_action_proxy.tv_delegate

        self.tvo = _ShTerminal.alloc().initWithFrame_(((0, 0), (width, height))).autorelease()
        self.tvo.setAutoresizingMask_(1 << 1 | 1 << 4)  # flex Width and Height
        self.content_inset = (0, 0, 0, 0)
        self.auto_content_inset = False

        # This setting helps preventing textview from jumping back to top
        self.non_contiguous_layout = False

        # Allow editing to the text attributes
        # self.editing_text_attributes = True

        ObjCInstance(self.superview).addSubview_(self.tvo)
        self.delegate = self._delegate_view

        # TextStorage
        self.tso = self.tvo.textStorage()
        
        # init baseclass and set attributes depending on settings
        # we have to do this this late because setting a few of these attributes requires self.tvo to be set
        ShBaseTerminal.__init__(self, stash, parent)
        
        self.default_font = UIFont.fontWithName_size_('Menlo-Regular', self.font_size)
        self.bold_font = UIFont.fontWithName_size_('Menlo-Bold', self.font_size)
        self.italic_font = UIFont.fontWithName_size_('Menlo-Italic', self.font_size)
        self.bold_italic_font = UIFont.fontWithName_size_('Menlo-BoldItalic', self.font_size)
        
        self.autocapitalization_type = ui.AUTOCAPITALIZE_NONE
        self.autocorrection_type = 1
        self.spellchecking_type = 1
示例#18
0
    def gestureRecognizer_shouldRecognizeSimultaneouslyWithGestureRecognizer_(
            _self, _sel, _gr, _other_gr):
        self = ObjCInstance(_self)
        other_gr = ObjCInstance(_other_gr)
        return other_gr in self.other_recognizers
        
    # Custom ObjC action target
    def gestureAction(_self, _cmd):
        self = ObjCInstance(_self)
        ...
        
    GestureHandlerObjC = objc_util.create_objc_class(
        'GestureHandlerObjC',
        methods=[
            gestureAction,
            gestureRecognizer_shouldRecognizeSimultaneouslyWithGestureRecognizer_,
        ],
        protocols=['UIGestureRecognizerDelegate'],
    )

    class GestureHandler2(ObjCPlus):
        
        _objc_class = GestureHandlerObjC
        
        # Vanilla Python __init__
        def __init__(self):
            self.other_recognizers = []
            
        # Vanilla Python method
        @objc_util.on_main_thread
        def before(self):
示例#19
0
# coding: utf-8

# https://forum.omz-software.com/topic/2941/any-idea-how-to-make-a-delagate-for-objc_utl-to-use

# Create and return a new ObjCClass that implements the given methods.

import objc_util

objc_util.create_objc_class(name,
                            superclass=NSObject,
                            methods=[],
                            classmethods=[],
                            protocols=[],
                            debug=True)
示例#20
0
    tag = ObjCInstance(picker_view).tag()
    print(f'Did select {_data[tag - 1][row]}')


methods = [
    numberOfComponentsInPickerView_, pickerView_numberOfRowsInComponent_,
    rowSize_forComponent_, pickerView_rowHeightForComponent_,
    pickerView_attributedTitleForRow_forComponent_,
    pickerView_didSelectRow_inComponent_
]

protocols = ['UIPickerViewDataSource', 'UIPickerViewDelegate']

UIPickerViewDataSourceAndDelegate = create_objc_class(
    'UIPickerViewDataSourceAndDelegate',
    NSObject,
    methods=methods,
    protocols=protocols)


# UIPickerView wrapper which behaves like ui.View (in terms of init, layout, ...)
class UIPickerViewWrapper(ui.View):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._picker_view = UIPickerView.alloc().initWithFrame_(
            ObjCInstance(self).bounds()).autorelease()
        ObjCInstance(self).addSubview_(self._picker_view)

    def layout(self):
        self._picker_view.frame = ObjCInstance(self).bounds()
def mail_compose(
        subject: str = "",
        recipients: Optional[List[str]] = None,
        body: str = "",
        filename: str = '',
        mime_type: str = '',
        dismiss_callback: Optional[Callable[[], None]] = None) -> None:
    """ Modal mail compose view.

    Display a modal mail compose view, with pre-populated contents, on top of
    the currently displayed view.

    The function returns immediately. Use argument `dismiss_callback` if your
    application needs to be notified when the compose view is dismissed.


    Arguments
    ---------

    subject: `str`, defaults to ``""``
        Mail subject.

    recipients: `Optional[List[str]]`, defaults to ``None``
        List of mail addresses for the recipients of the email.

    body: `str`, defaults to ``""``
        Mail body.

    filename: `str`, defaults to ``''``
        If non-empty, name of a file to be attached to the email.

    mime_type: `str`, defaults to ``''``
        If `filename` is not empty, indicates the mime type of its contents,
        for instance ``'image/gif'``

    dismiss_callback: `Optional[Callable[[], None]]`, defaults to ``None``
        When set to a callable, it is called (with no arguments) when the mail
        composition view is dismissed. """
    def mailComposeController_didFinishWithResult_error_(
            self, sel, controller, result, error):
        nonlocal dismiss_callback
        mail_vc = ObjCInstance(controller)
        mail_vc.setMailComposeDelegate_(None)
        mail_vc.dismissViewControllerAnimated_completion_(True, None)
        ObjCInstance(self).release()
        if dismiss_callback:
            dismiss_callback()

    try:
        MailDelegate = ObjCClass('MailDelegate')
    except ValueError:
        MailDelegate = create_objc_class(
            'MailDelegate',
            superclass=NSObject,
            methods=[mailComposeController_didFinishWithResult_error_],
            protocols=['MFMailComposeViewControllerDelegate'])
        objc_util.retain.append(
            mailComposeController_didFinishWithResult_error_)
    MFMailComposeViewController = ObjCClass('MFMailComposeViewController')
    mail_vc = MFMailComposeViewController.alloc().init().autorelease()
    delegate = MailDelegate.alloc().init().autorelease()
    objc_util.retain.append(delegate)
    mail_vc.setMailComposeDelegate_(delegate)
    # Find a view controller which is not already presenting, see
    # https://forum.omz-software.com/topic/2060/presenting-viewcontroller/2
    root_vc = UIApplication.sharedApplication().keyWindow().rootViewController(
    )
    while root_vc.presentedViewController():
        root_vc = root_vc.presentedViewController()
    mail_vc.setSubject_(subject)
    if recipients is not None:
        mail_vc.setToRecipients_(recipients)
    mail_vc.setMessageBody_isHTML_(body, body.startswith('<html>'))
    if filename and os.path.exists(filename):
        mail_vc.addAttachmentData_mimeType_fileName_(
            NSData.dataWithContentsOfFile_(os.path.abspath(filename)),
            mime_type, filename)
    root_vc.presentViewController_animated_completion_(mail_vc, True, None)