Forum Archive

pip install gestures, WKWebView, UI animations and more

mikael

I finally got around to publishing some of my Pythonista libraries to PyPi, so
now you can just pip install the following:

  • pythonista-gestures - multi-touch gestures (readme)
  • pythonista-wkwebview - non-obsolete webview with a JS console (readme)
  • pythonista-scripter - easy UI animations (readme)
  • pythonista-multipeer - ad hoc networking between Apple devices (readme)
  • pythonista-anchor - UI layouts with constraints instead of coordinates (readme)
  • pythonista-docgen - generate a README from the docstrings in the code (readme)

I also took this opportunity to revisit the gestures module, and updated it to be more in line with what I have learned in the intervening years about ObjC/Python interfaces and nicer APIs. This resulted in a not backwards-compatible change, so if you are using the earlier version, you might consider not installing this one.

Basic use of the new API:

import gestures

def swipe_handler(data):
    print(f‘I was swiped left, starting from {data.location}')

label = ui.Label()
gestures.swipe(label, swipe_handler, direction=gestures.LEFT)

Coordinating several gestures is now a bit more straight-forward. For example, if you want to give the swipe gesture priority over a pan:

panner = pan(view, pan_handler)
swiper = swipe(view, swipe_handler, direction=RIGHT)
swiper.before(panner)
becktrex

This looks great! But I can't seem to get the install to work. This is what I get:

pip install pythonista-gestures
Querying PyPI ... 
Downloading package ...
Opening: https://files.pythonhosted.org/packages/e0/76/e1766485ab9b521d9d4c304854f479b87db6df350dc54996aa5c71e4d608/pythonista_gestures-1.0-py2.py3-none-any.whl

Save as: /private/var/mobile/Containers/Data/Application/1596E9F5-6DC7-4159-8555-9611A7AEB2A8/tmp//pythonista_gestures-1.0-py2.py3-none-any.whl (10408 bytes)
     10408 [100.00%]
Installing wheel: pythonista_gestures-1.0-py2.py3-none-any.whl...
<class 'UnicodeDecodeError'>: 'ascii' codec can't decode byte 0xe2 in position 1125: ordinal not in range(128)
mikael

@becktrex, hmm, exact same files install with no problem on my iPhone.

I am sure you have restarted Pythonista and tried again?

@bennr01, would appreciate any hints on how to debug this?

mikael

@becktrex , encoding error could point to some py2 issue, not sure if that helps any.

mikael

Error matches a stash issue.

mikael

@becktrex, are you running the latest stash? (selfupdate)

bennr01

would appreciate any hints on how to debug this?

In general, stashconf py_traceback 1, stashconf py_pdb 1 and pip --verbose install pythonista-gestures.

I just tried and could not replicate the error (it installs successfully).

Error matches a stash issue.

I think that one is a different issue. StaSh pip has two installers: one for source install (via setup.py) and one for wheels (.whl). The linked issue was regarding the source install, while this issue is with wheel installations. Also, I think we already fixed the linked issue, but I am not sure...

mikael

@becktrex, if pip will not co-operate, you can just as well open stash and:

cd site-packages
wget https://raw.githubusercontent.com/mikaelho/pythonista-gestures/master/gestures.py
becktrex

StaSh v0.7.2 on python 3.6.1
pythonista V. 3.4 (330025)
iphone 6S+
iOS 13.3.1
StaSh "selfupdate" returns "Already at latest version"

I've tried this over the last couple of days. Restarted Pythonista, rebooted iphone, etc. with the same result.

I tried pip install again with the above "extras", no joy, results below.

wget from the site-packages directory worked fine.
time to play with my new ̶t̶o̶y̶s̶ tools

```

[~/Documents]$ stashconf py_traceback 1
[~/Documents]$ stashconf py_pdb 1
[~/Documents]$ pip --verbose install pythonista-gestures
Querying PyPI ...
Using pythonista-gestures==1.0...
A binary distribution is available and will be used.
Downloading package ...
Opening: https://files.pythonhosted.org/packages/e0/76/e1766485ab9b521d9d4c304854f479b87db6df350dc54996aa5c71e4d608/pythonista_gestures-1.0-py2.py3-none-any.whl

Save as: /private/var/mobile/Containers/Data/Application/1596E9F5-6DC7-4159-8555-9611A7AEB2A8/tmp//pythonista_gestures-1.0-py2.py3-none-any.whl (10408 bytes)
10408 [100.00%]
Installing wheel: pythonista_gestures-1.0-py2.py3-none-any.whl...
Extracting wheel..
Extraction finished, running handlers...
Running handler 'WHEEL information checker'...
Wheel generated by: flit 2.2.0
Running handler 'dependency handler'...
Cleaning up...
: 'ascii' codec can't decode byte 0xe2 in position 1125: ordinal not in range(128)
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/16663993-01D5-4358-99D0-C11426AFFF9B/Pythonista3/Documents/site-packages/stash/system/shruntime.py", line 547, in exec_py_file
exec(code, namespace, namespace)
File "site-packages/stash/bin/pip.py", line 1492, in
repository.install(pkg_name, ver_spec, dist=dist)
File "site-packages/stash/bin/pip.py", line 1085, in install
self._install(pkg_name, pkg_info, archive_filename, dependency_dist=dist)
File "site-packages/stash/bin/pip.py", line 849, in _install
files_installed, dependencies = wheel.install(self.site_packages)
File "/private/var/mobile/Containers/Shared/AppGroup/16663993-01D5-4358-99D0-C11426AFFF9B/Pythonista3/Documents/site-packages/stash/lib/stashutils/wheels.py", line 385, in install
tfi = handler.handle_install(tp, targetdir)
File "/private/var/mobile/Containers/Shared/AppGroup/16663993-01D5-4358-99D0-C11426AFFF9B/Pythonista3/Documents/site-packages/stash/lib/stashutils/wheels.py", line 286, in handle_install
dependencies = self.read_dependencies_from_METADATA(metadatap)
File "/private/var/mobile/Containers/Shared/AppGroup/16663993-01D5-4358-99D0-C11426AFFF9B/Pythonista3/Documents/site-packages/stash/lib/stashutils/wheels.py", line 310, in read_dependencies_from_METADATA
for line in fin:
File "/var/containers/Bundle/Application/FC2E1425-424E-4F22-88E8-DDBC98DBC701/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/encodings/ascii.py", line 27, in decode
return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1125: ordinal not in range(128)

/var/containers/Bundle/Application/FC2E1425-424E-4F22-88E8-DDBC98DBC701/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/encodings/ascii.py(27)decode()
-> return codecs.ascii_decode(input, self.errors)[0]
(Pdb)

bennr01

After looking into this issue for a bit, it seems like the same bug as in Issue #364. This should have been fixed 12 months ago, just a month after the latest version bump for master. So it is possible that you have a version installed before that version bump.

So, since it seems like the master branch became a bit outdated,I just merged 152 commits from dev into master. This should include the fix as well as a version bump, so could you please try another selfupdate?

becktrex

That did the trick.
selfupdate was successfull.

"pip install pythonista-gestures" installed the first time.

mikael

@bennr01, a big thank you! for all you do to keep us stashed & pipped.

bennr01

@mikael said:

@bennr01, a big thank you! for all you do to keep us stashed & pipped.

It's always a pleasure. Though in this case, the fix was made by yjqiang.

Drizzel

@mikael Just a tiny correction, please correct me if I’m wrong. In the multipeer readme it says one should use to install your module, which doesn’t work. You probably meant .

mikael

@Drizzel, thank you, fixed. Multipeer was my first PyPi exercise, and my naming convention was still evolving – meaning, I did not yet know how to give a module a different name in site-packages and PyPi. :-)

Anxietier

Hi, I tested one demo code in readme of pythonista-wkwebview

class MagicWebView(WKWebView):

  def on_magic(self, message):
    print('WKWebView magic ' + message)

html = '''
<body>
<button onclick="window.webkit.messageHandlers.magic.postMessage(\'spell\')">
Cast a spell
</button>
</body>
'''

v = MagicWebView()
v.load_html(html)

Well it works good, but when I try to load html file, the 'on_magic' doesn't work, here's my code in py file

class MagicWebView(WKWebView):
    def on_magic(self, message):
        print('WKWebView magic ' + message)

v = MagicWebView()
v.present()
# v.load_html(html)
v.load_url('test.html', no_cache=False, timeout=5)
v.clear_cache()

I don't know how to fix it

cvp

@Anxietier try

import os

w = WKWebView()
f = os.path.abspath('test.html')
print(f)
w.load_url(f)
w.present() 
mikael

@Anxietier, I see the online documentation covers this poorly. Here’s the method docstring:

Loads the contents of the given url asynchronously.

If the url starts with file://, loads a local file. If the remaining url starts with /, path starts from Pythonista root.

For remote (non-file) requests, there are two additional options:

  • Set no_cache to True to skip the local cache, default is False
  • Set timeout to a specific timeout value, default is 10 (seconds)
mikael

@Anxietier, you can get this and similar documentation on other methods using the help function, e.g. in Pythonista console:

>>> import wkwebview
>>> help(wkwebview.WKWebView.load_url)
Anxietier

@cvp
thanks, but it seems doesn’t work, here’s my test code

file_name = 'test.copy(2).html'
file_abs_path = os.path.abspath(file_name)
url_file_name = 'file://' + file_name
url_file_abs_path = 'file://' + file_abs_path

with open(file_name, 'r', encoding='utf8') as f:
    html = ''.join(f.readlines())

v = MagicWebView()
v.present()

# v.load_html(html) # can load but button doesn't work

# v.load_url(file_name, no_cache=False, timeout=5) # can load but button doesn't work

# v.load_url(file_abs_path, no_cache=False, timeout=5) # can load but button doesn't work

# v.load_url(url_file_name, no_cache=False, timeout=5) # would let ista crash or load faild

# v.load_url(url_file_abs_path, no_cache=False, timeout=5) # doesn't load

v.clear_cache() 
Anxietier

@mikael
I’m not sure if I described my issue clearly, and here’s my another test code, perhaps that would be clearly

file_name = 'test.copy(2).html'
file_abs_path = os.path.abspath(file_name)
url_file_name = 'file://' + file_name
url_file_abs_path = 'file://' + file_abs_path

with open(file_name, 'r', encoding='utf8') as f:
    html = ''.join(f.readlines())

v = MagicWebView()
v.present()

# v.load_html(html) # can load but button doesn't work

# v.load_url(file_name, no_cache=False, timeout=5) # can load but button doesn't work

# v.load_url(file_abs_path, no_cache=False, timeout=5) # can load but button doesn't work

# v.load_url(url_file_name, no_cache=False, timeout=5) # would let ista crash or load faild

# v.load_url(url_file_abs_path, no_cache=False, timeout=5) # doesn't load

v.clear_cache() 
mikael

@Anxietier, I will have to look at this, maybe something has changed in recent iOS versions.

Meanwhile, this:

html = ''.join(f.readlines())

... looks funny, breaking and joining with no change. Just html = f.read() should work fine.

mikael

@Anxietier, I placed this script:

import wkwebview

class MyWebView(wkwebview.WKWebView):

    def on_magic(self, message):
        print('WKWebView magic ' + message)

wv = MyWebView()
wv.present('fullscreen')

wv.load_url('load_url.html')

... and this HTML as the file load_url.html in the same directory:

<body>
<button onclick="window.webkit.messageHandlers.magic.postMessage('spell')">
Cast a spell
</button>
</body>

And the button works fine. Note that I removed the backlashes in the onclick handler as escaping not necessary in a file.

I suspect you might have some kind of non-printing character issue.

stephen

@mikael said:

@Anxietier, have to look at this, maybe something has changed in recent iOS versions.

Meanwhile, this:

html = ''.join(f.readlines())

... looks funny, breaking and joining with no change. Just html = f.read() should work fine.

much cleaner than i would of. lol


html = ''.join([line for line in f])

Anxietier

@mikael
oh I see, I usually use r_string in py, so I didn't notice \' in doc_string
now it works fine
thanks for help

Anxietier

hi, im back again :D
we know that some browser has non-pic mode (i dont know right name,just like that mean), is it possible to add that feature to wkwebview? for some reason, i just need the full html content (dom-tree maybe?)

mikael

@Anxietier, if you mean ”Reader Mode”, WKWebView does not include it. But you can use a SafariViewController, see @cvp’s code around the middle of this thread.

JonB

If you just want to parse the HTML, use requests. Unless you need JavaScript to run I guess.

@mikael isn't there a delegate that would let you filter by uri? I forget if the should_load_uri or whatever gets called for 's -- in which case he could just return false for non-html urls.

mikael

@JonB, no it does not. Would be good to understand the need a bit more before trying to bend WKWebView to it. For the most part, just fetching the page with requests, then filtering imgs and javascript before giving it to WKwebView might work just as well.

Anxietier

@JonB
yeah i need js in script tag to run. some requests is not easy to fake them you know, so i was wandering if i can let webview run in 'read mode' to simulate 'selenium', cause some imgs spend many data traffic, and i dont wanna take double traffic for one img

Anxietier

@mikael
thanks, it is helpful, i would try to understand them.

mikael

@Anxietier, if web scraping is your thing, then the SafariViewController will not help you. WKWebView is the best tool around for Pythonista, as far as I know.

mikael

@Anxietier, hey, there’s something called content rules that can be configured to just stop loading images, see here. I will give it a try a bit later.

Anxietier

@mikael
well, theres an open source android app names 'H-viewer', using regex&selector to select out imgs, and re-layout them, so that we can just watch imgs. also, theres an app in appstore names 'yealico', it has similar function.
I was trying to fake(reproduce?) that by python in ista, I found one way to do that, 'use two wkwebviews, only present one, the other one load url in background, when it did finish load, use js selector to get imgs url, then show imgs on presented webview', im not sure will background-webview cost data-usage, but I dont want to cost double 'img data-usage'

mikael

@Anxietier, the content rules, which might help or not, require at least two completion blocks, doable but unfortunately I do not have the kind of time I had earlier to spend on these projects.

I would be happy to provide background support if you want to give it a go. I would expect it to be mostly versions of code already used in this component.

Anxietier

@mikael
sorry for reply late, im not sure if i get your point at sec paragraph, cause my eng not even good and machine translation not so reliable.
let me assume you meant that you would give support if i want to try adding read-mode.
thanks for your kind, but i dont know if i have time to do it, and i didnt learn objc before, so maybe it would take long time to finish that (or never :/
btw, content rule is a great direction indeed, i should take some time to learn about that