• mcriley821

    @mikael @JonB

    I think I figured out a solution that will work. Since the doubletap gesture was recognizing double-taps and gain access to self by inlining the objc method, I realized that’s all I really needed. I can initialize the TextView with editable=False, enable editing inside the objc method, and start editing. Then in the textview’s delegate, when editing is done, disable editing!

    Thank you guys for the help!

    Here’s the code if anyone is interested:

    from objc_util import *
    import ui
    
    UITap = ObjCClass('UITapGestureRecognizer')
    
    class Node (ui.View):
        class TextViewDelegate (object):
            def textview_did_change(self, tv):
                try:
                    if '\n' == tv.text[-1]:
                        tv.text = tv.text[:-1]
                        tv.end_editing()
                except IndexError:
                    pass
            
            def textview_did_end_editing(self, tv):
                # do whatever with the text
                tv.editable = False
        
        def __init__(self, *args, **kwargs):
            ui.View.__init__(self, *args, **kwargs)
            self.frame = (0,0,*(ui.get_screen_size()))
            self.tv = ui.TextView(
                name='node_label',
                frame=(100, 100, 50, 50),
                delegate=self.TextViewDelegate(),
                bg_color='#b9b9ff',
                editable=False
            )
            
            tvObj = self.tv.objc_instance
            
            def handleTap_(_slf, _cmd, _gesture):
                self.tv.editable = True
                self.tv.begin_editing()
                
            self.target = create_objc_class(
                'fake_target',
                methods=[handleTap_]
                ).new()
        
            self.doubletap = UITap.alloc().initWithTarget_action_(
                self.target,
                'handleTap:'
            )
            self.doubletap.setNumberOfTapsRequired_(2)
            tvObj.addGestureRecognizer_(
                self.doubletap
            )
            self.add_subview(self.tv)
    
        def will_close(self):
            self.doubletap.release()
            self.target.release()
    
    if __name__ == '__main__':
        w, h = ui.get_screen_size()
        view = Node(bg_color='white')
        view.present()
    
    

    posted in Pythonista read more
  • mcriley821

    @cvp TextField is even odder, having no gestureRecognizers. It probably has a view hierarchy with a TextView. Maybe I need to remove gestures from all underlying views of my TextView?

    posted in Pythonista read more
  • mcriley821

    @JonB I’ve seen @mikael’s repo, but based on personal reasons I’d rather not use it. I want to understand how things are working instead of importing.

    Anyhow, inlining the method did not fix the issue. Making tv a self attribute, and changing the method to:

    def handleTap_(_slf, _cmd, _gesture):
        self.tv.begin_editing()
    

    After the first double tap, a single tap suffices to start editing again. I also retained target (self.target).

    posted in Pythonista read more
  • mcriley821

    I'm doing a project with ui and objc_util (typical). I'm getting this strange behavior when overriding a ui.TextView's gestures; the gesture's view is somehow re-overriding the gestures.
    The simplest code example I can muster is as follows:

    from objc_util import *
    import ui
    
    
    UITap = ObjCClass("UITapGestureRecognizer")
    
    
    class Node (ui.View):
        class TextViewDelegate (object):
            def textview_did_change(self, tv):
                if '\n' == tv.text[-1]:
                    tv.text = tv.text[:-1]
                    tv.end_editing()
    
        def __init__(self, *args, **kwargs):
            ui.View.__init__(self, *args, **kwargs)
            self.frame = (0, 0, *(ui.get_screen_size()))
            tv = ui.TextView(
                name="node_label",
                frame=(100, 100, 50, 50),
                bg_color="blue",
                delegate=self.TextViewDelegate()
            )
            # this is where the interesting bit starts
            tvObj = tv.objc_instance
            # remove all the gestures of tv
            for gesture in tvObj.gestureRecognizers():
                tvObj.removeGestureRecognizer_(gesture)
    
            # for a new gesture, we need a target and action
            # how to make self a target? not really sure
            # maybe making a "blank" target with the method 
            # would be enough? since self carries?
            # I tried making a target of self, but crashes
            target = create_objc_class(
                "self",
                methods=[self.handleTap_]
            ).alloc().init()
            # now we have a target and action
            # let's make the actual gesture
            doubletap = UITap.alloc().initWithTarget_action_(
                target,
                "handleTap:"
            )
            # make into an actual doubletap, and add to view
            doubletap.setNumberOfTapsRequired_(2)
            tvObj.addGestureRecognizer_(doubletap)
    
            # add the tv subview with a single gesture: doubletap
            # can confirm only one gesture by uncommenting below
            #print(self.objc_instance.gestureRecognizers())  # None
            #print(tvObj.gestureRecognizers())  # doubletap
            self.add_subview(tv)
    
    # Now, without @static_method, everything is fine up until
    # the below function is called -> results in TypeError of passing
    # 4 args to a 3 arg function, since the first arg is self. However,
    # with @static_method, we have to do some round-about trickery
    # to do what we want. 
        @static_method
        def handleTap_(_slf, _cmd, _gesture):
            gesture = ObjCInstance(_gesture)
            view = gesture.view()
    # More interesting stuff happens now. On the first call of handleTap_,
    # the next line prints only doubletap. On next calls, all the gestures 
    # have been reset
            print(view.gestureRecognizers())
    # we can only start editing now by becoming the first responder,
    # since we can't access self
            view.becomeFirstResponder()
    
    # I assume here that the call to becomeFirstResponder instantiates
    # a new TextView object somehow, yet this new object still contains
    # a doubletap gesture. Re-tapping the TextView in the UI will start
    # editing - no double tapping needed. 
    
    
    if __name__ == "__main__":
        w, h = ui.get_screen_size()
        view = Node(bg_color="white")
        view.present()
    
    

    What can I do to make self my target? Or how can I pass self to my method? Can I create a wrapper for the @static_method wrapper and pass self still? I'm stuck on what to do, since any direction I go seems to be a dead-end:

    • make self into a target = crash
    • @static_method = no reference to the true self instance
    • no @static_method = TypeError
    • can't use global variables, since I hope to have ~10 of these Nodes at a time

    Also, I'd prefer to not use a TextField since they have that awful bounding box. I also think this issue would carry to a TextField anyhow.

    Any ideas are greatly appreciated! I'm stumped!

    posted in Pythonista read more
  • mcriley821

    No. You can’t turn off WiFi via Pythonista, unless you use Shortcuts.

    What problems are you having with Shortcuts?

    posted in Pythonista read more
  • mcriley821

    @ccc
    /bin contains:
    bash bunzip2 bzcat bzip2 bzip2recover cat chgrp chmod chown cp date dd df dir echo egrep false fgrep grep gunzip gzexe gzip kill launchctl ln ls mkdir mknod mktemp mv ps pwd readlink rm rmdir sed sh sleep stty su sync tar touch true uname uncompress vdir zcat zcmp zdiff zegrep zfgrep zforce zgrep zless zmore znew

    /sbin contains:
    dmesg dynamic_pager fsck fsck.sbin fsck_apfs fsck_apfs.sbin fsck_exfat fsck_exfat.sbin fsck_hfs fsck_hfs.sbin fsck_msdos fsck_msdos.sbin fstyp fstyp_msdos fstyp_ntfs fstyp_udf halt launchctl launchd launchd.sbin mount mount.sbin mount_apfs mount_apfs.sbin mount_devfs mount_fdesc mount_hfs mount_hfs.sbin mount_nfs newfs_apfs newfs_apfs.sbin newfs_hfs newfs_hfs.sbin pfctl pfctl.sbin quotacheck umount umount.distrib

    /usr/bin contains:
    7z 7za DumpBasebandCrash IOMFB_FDR_Loader PerfPowerServicesExtended [ abmlite appsearch apt apt-cache apt-cdrom apt-config apt-extracttemplates apt-ftparchive apt-get apt-key apt-mark apt-sortpkgs arch asn1Coding asn1Decoding asn1Parser autopoint awk b2sum base32 base64 basename basenc bashbug brctl c_rehash cap_mkdb captoinfo certtool cfversion chcon chflags chfn chown chsh cksum clear cmp comm compress csplit cut cycc cynject db_archive db_checkpoint db_convert db_deadlock db_dump db_hotbackup db_load db_log_verify db_printlog db_recover db_replicate db_stat db_tuner db_upgrade db_verify deviceinfo diff diff3 dircolors dirmngr dirmngr-client dirname doNotify dpkg dpkg-deb dpkg-divert dpkg-maintscript-helper dpkg-query dpkg-split dpkg-statoverride dpkg-trigger dselect du dumpsexp ecidecid env envsubst event_rpcgen.py expand expr factor faker file fileproviderctl find finger fmt fold footprint funzip gawk getconf getopt gettext gettext.sh gettextize getty gnupg2 gnutls-cli gnutls-cli-debug gnutls-serv gpg gpg-agent gpg-connect-agent gpg-error gpg-error-config gpg-wks-server gpgconf gpgparsemail gpgrt-config gpgscm gpgsm gpgtar gpgv groups gssc head hidutil hmac256 hostid hostinfo hpmdiagnose id idn2 infocmp infotocap install iomfsetgamma ip-print ipcrm ipcs join kbdebug kbxutil killall ksba-config last ldid ldrestart libassuan-config libgcrypt-config link locale locate login logname lsdiagnose lsvfs lz4 lz4c lz4cat lzcat lzcmp lzdiff lzegrep lzfgrep lzgrep lzless lzma lzmadec lzmainfo lzmore md5sum mkfifo mktemp mpicalc msgattrib msgcat msgcmp msgcomm msgconv msgen msgexec msgfilter msgfmt msggrep msginit msgmerge msgunfmt msguniq ncurses6-config ncursesw6-config nettle-hash nettle-lfib-stream nettle-pbkdf2 nfsstat ngettext nice nl nohup notificationWatcher nproc npth-config numfmt ocsptool od openURL openssl p11-kit p11tool pager pagesize passwd paste pathchk pax pbcopy pbpaste pinky pkcs1-conv play plistutil plutil powerlogHelperd pr printenv printf psktool ptx quota realpath recode-sr-latin renice reset restart runcon say sbdidlaunch sbreload scp script sdiff seq sexp-conv sftp sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf snappy sort split srptool ssh ssh-add ssh-agent ssh-keygen ssh-keyscan stat stdbuf sum sw_vers sysdiagnose tabs tac tail tailspin tar taskinfo tee test tic time timeout toe toggleTether tput tr truncate trust tset tsort tty uicache uiduid uiopen unexpand uniq unlink unlz4 unlzma unrar unxz unzip unzipsfx update-alternatives updatedb uptime urlclip users vm_stat watchgnupg wc wget which who whoami xargs xgettext xz xzcat xzcmp xzdec xzdiff xzegrep xzfgrep xzgrep xzless xzmore yat2m yes zip zipcloak zipnote zipsplit zprint

    /usr/sbin contains:
    BTAvrcp BTAvrcp.sbin BTLEServer BTLEServer.sbin BTMap BTMap.sbin BTPbap BTPbap.sbin BlueTool BlueTool.sbin WiFiNetworkStoreModel.momd WirelessRadioManagerd WirelessRadioManagerd.sbin absd absd.sbin ac accton addNetworkInterface addNetworkInterface.sbin addgnupghome applecamerad applygnupgdefaults aslmanager aslmanager.sbin bluetoothd bluetoothd.sbin cfprefsd cfprefsd.sbin chown chroot ckksctl dev_mkdb distnoted distnoted.sbin edquota fairplayd.H2 fdisk filecoordinationd filecoordinationd.sbin hdik ioreg ioreg.sbin iostat ipconfig ipconfig.sbin mDNSResponder mDNSResponder.sbin mDNSResponderHelper mDNSResponderHelper.sbin mediaserverd mediaserverd.sbin mkfile nologin notifyd notifyd.sbin nvram nvram.sbin otctl pppd
    pppd.sbin pwd_mkdb quotaon racoon racoon.sbin reboot repquota rtadvd rtadvd.sbin scutil scutil.sbin spindump spindump.sbin sshd startupfiletool sysctl syslogd syslogd.sbin vifs vipw vsdbutil wifid wifid.sbin zdump zic

    Would that be the best way to do background cli commands to StaSh and report the stdout? Is there another way without launching a Web server?

    posted in Pythonista read more
  • mcriley821

    @ccc I can attest that I can navigate the entire file system with StaSh now.

    Is there a way to use StaSh in a headless manner? I want to make a file browser and editor, since similar packages in Cydia are not yet supported for iOS 13

    posted in Pythonista read more
  • mcriley821

    I recently jailbroke my device running iOS 13.5.1 to allow for tethering (data should be free imo). Anyhow, I really love Pythonista and I'm wondering if the jailbreak gives me any extra capabilities now in Pythonista? I'm really curious to start changing the plist file since that's what stunted a lot of my projects. Excited to hear what you guys can think of!

    posted in Pythonista read more
  • mcriley821

    I’ve been doing something similar. Here’s a list of GATT characteristics that helped me. https://www.bluetooth.com/specifications/gatt/characteristics/

    posted in Pythonista read more

Internal error.

Oops! Looks like something went wrong!