-
mcriley821
Hey all,
I’m modifying the keyboard example “Special Characters.py” to include all Unicode characters. I did this by scraping the Unicode code point range and category name from Wikipedia. It has a navigation view with all the categories in a scroll view, and then presents all the Unicode characters of that category.
Anyhow, the problem is that I get spotty printability, but since the character is a valid Unicode character I can’t prevent it from displaying as 🠂. I don’t understand the problem since sys.maxunicode (1114111) suggests this shouldn’t be a problem...
Is it due to the possibility that the iOS system font doesn’t have a rendering of these glyphs? How can I install Google Noto and access these glyphs (if that’s the issue).
Code:
#! python3 import keyboard import ui with open('uni_blocks.txt', 'r') as block_file: blocks = block_file.readlines() class BlocksView (ui.View): def __init__(self, *args, **kwargs): ui.View.__init__(self, *args, **kwargs) self.glyph_sv = None self.cat_sv = ui.ScrollView( flex='WH', frame=self.bounds ) self.buttons = [] for block in blocks: cells = block.split(' ') start = cells[0] end = cells[1] name = ' '.join(cells[2:]) btn = self.create_button(name) btn.action = self.cat_selected btn.start = start btn.end = end self.cat_sv.add_subview(btn) self.buttons.append(btn) self.add_subview(self.cat_sv) def layout(self): if self.cat_sv.on_screen: bw = ui.get_screen_size()[0] x, y = 2, 2 for button in self.buttons: button.frame = (x, y, bw, 20) y += 24 self.cat_sv.content_size = (0, (len(self.buttons) + 1) * 24 + 40) def cat_selected(self, sender): # present another scroll view of unicode titled buttons self.glyph_sv = ui.ScrollView( name=sender.title, frame=self.bounds, flex='WH' ) w = ui.get_screen_size()[0] x = y = 2 rows = 1 for i in range(int(sender.start, 16), int(sender.end, 16)): char = chr(i) if char.isprintable() and not char.isspace(): btn = self.create_button(chr(i)) btn.action = self.insert_char btn.number = i btn.frame = (x, y, 35, 35) x += 39 if x > w - 39: x = 2 y += 39 rows += 1 self.glyph_sv.add_subview(btn) self.glyph_sv.content_size = (w, (rows + 1) * 39 + 40) self.navigation_view.push_view(self.glyph_sv, False) def create_button(self, name): btn = ui.Button(title=name) btn.font = ('<System>', 18) btn.background_color = (1, 1, 1, 0.1) btn.tint_color = 'white' btn.corner_radius = 4 btn.size_to_fit() return btn def insert_char(self, sender): if keyboard.is_keyboard(): keyboard.insert_text(sender.title) else: print(sender.title, sender.number) def main(): w = ui.get_screen_size()[0] v = BlocksView( frame=(0, 0, w, 133), name='Categories', flex='WH' ) nav_view = ui.NavigationView(v) nav_view.title_color = 'white' if keyboard.is_keyboard(): keyboard.set_view(nav_view, 'expanded') else: # For debugging in the main app: nav_view.bg_color = 'black' nav_view.bar_tint_color='black' nav_view.present('fullscreen', hide_title_bar=True) if __name__ == '__main__': main()
Source .txt:
https://pastebin.com/raw/Y9FsuykqThanks!
-
mcriley821
I think I figured out a solution that will work. Since the
doubletap
gesture was recognizing double-taps and gain access toself
by inlining the objc method, I realized that’s all I really needed. I can initialize the TextView witheditable=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()
-
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?
-
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).
-
mcriley821
I'm doing a project with
ui
andobjc_util
(typical). I'm getting this strange behavior when overriding aui.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 passself
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 trueself
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!
- make
-
mcriley821
No. You can’t turn off WiFi via Pythonista, unless you use Shortcuts.
What problems are you having with Shortcuts?
-
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 zicWould 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?
-
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
-
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!