This command worked for me:
url = 'https://raw.githubusercontent.com/ywangd/stash/dev/getstash.py'; import requests as r; exec(r.get(url).text.replace('master', 'dev'))
Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
This command worked for me:
url = 'https://raw.githubusercontent.com/ywangd/stash/dev/getstash.py'; import requests as r; exec(r.get(url).text.replace('master', 'dev'))
I have created a modified version of dropboxlogin.py that generates short-lived access tokens. So far, it seems to work with dropbox version 6.4.0 that is provided with Pythonista.
First run will authorize your application and store a long-lived refresh token in your device keychain.
# simple test app
import dropboxlogin
dropbox_client = dropboxlogin.get_client()
print('Getting account info...')
account_info = dropbox_client.users_get_current_account()
print('linked account:', account_info)
Useful links:
Migrating App access tokens
Dropbox OAuth Guide
I was able to update dropbox to the latest version with this work around script which downloads the dropbox wheel and unzips the contents in site-packages. I had to use Stash pip to install the requests, six and stone. I'm still testing to see if it is better to stick with the original Pythonista dropbox version or to use the upgraded versions of dropbox, requests and six.
# workaround to install latest dropbox in pythonista
# install requirements using stash pip
# pip install requests
# pip install six
# pip install stone
import sys, os
import requests
from zipfile import ZipFile
sitepath = os.path.expanduser('~/Documents/site-packages-3')
os.chdir(sitepath)
from zipfile import ZipFile
url = 'https://files.pythonhosted.org/packages/ef/64'
url += '/2ef3c03ac039f9e5f29c0f557dc3f276241ef3c1627903a5f6a8c289cf24/'
url += 'dropbox-11.7.0-py3-none-any.whl'
dest_path = './dropbox.zip'
r = requests.get(url)
with open(dest_path, 'wb') as f:
f.write(r.content)
print('download complete...')
with ZipFile(dest_path, 'r') as zip:
# extracting all the files
print('Extracting all the files now...')
zip.extractall()
print('Done!')
os.remove(dest_path)
@kizik You may want to try something like this.
import urllib3
urllib3.disable_warnings()
import requests
url = 'https://jsonplaceholder.typicode.com/posts/1'
data = {'somekey': 'somedata'}
try:
r = requests.get(url, headers={}, params=data, verify=False)
response = r.json()
print(response)
except:
print("Exception u: %s" % str(sys.exc_info()))
@dthomson You may want to look at this post.
https://forum.omz-software.com/topic/7014/how-to-refresh-dropbox-access-token-with-the-older-dropbox-library-included-with-pythonista/2
I installed dropbox version 11.7.0 a year ago and I appears to still be working today.
This code works on my iPhone 12.
I added @ui.in_background to def select_author(sender)
I will also test the Lock code.
import ui
#
def show_list_dialog(items=None, *args, **kwargs):
items = items or []
tbl = ui.TableView(**kwargs)
tbl.data_source = ui.ListDataSource(items)
my_sel = {'value': None}
class MyTableViewDelegate (object):
def tableview_did_select(self, tableview, section, row):
print('items:', tableview.data_source.items)
print('section, row:', section, row)
my_sel['value'] = tableview.data_source.items[row]
tableview.close()
print('in delegate: my_sel',my_sel['value'])
tbl.delegate = MyTableViewDelegate()
tbl.present(style='sheet')
tbl.wait_modal()
print('after wait: ', my_sel['value'])
return my_sel['value']
#
w, h = ui.get_screen_size()
view = ui.View(name="Frank's Library", bg_color='lightblue', frame=(0, 0, w, h))
@ui.in_background
def select_author(sender):
author_unique = ['Baldacci,David', 'Barr,Nevada', 'Bartol,Amy', 'Bernt,Eric', 'Brandt,Kylie', 'Bruni,Riccardo', 'Bryndza,Robert', 'Burke,James Lee', 'Burton,Mary', 'Butcher,Jim', 'Carlson,Tucker', 'Carrol,Sean', 'Case,Andrew', 'Chernov,Ron', 'Clayton,MegWaite', 'Coben,Harlan', 'Cole,Bobby', 'Connelly,Michael', 'Cornwell,Bernard', 'Cornwell,Patricia', 'Cowie,Amber', 'Crombie,Deborah', 'Crosby,Ellen', 'Cross,Katie', 'Cussler,Clive', 'Delaney,Matthew B.J.', 'Dilts,Tyler', 'Dorsey,Tim']
f = (0, 0, 400, 300)
selected_author = show_list_dialog(author_unique, frame=f, name='Select an Author')
print('list dialog returned:', selected_author)
global author_result
author_result.text = str(selected_author)
return
author_button = ui.Button(title='Author', frame=(10,5,350,100), border_color='black', border_width=2, background_color='#EAECEE', action=select_author, font=('Arial Rounded MT Bold',18))
author_button.width=70
author_button.height=40
view.add_subview(author_button)
#
global author_result
author_entry=''
author_result = ui.TextView(name='author',frame=(100,5,250,40), border_color='black', border_width=3, text=author_entry, font=('Arial Rounded MT Bold',16))
view.add_subview(author_result)
view.present('sheet')
#nav_view = ui.NavigationView(view)
#nav_view.present('sheet')
@
@felciano There appear to be 2 issues.
You will need to use Dropbox 6.4.0 and you will need to store your refresh_token somewhere else besides keychain.
You can modify dropboxlogin like this:
#refresh_token = keychain.get_password('dropbox', 'refresh_token')
refresh_token = "your_refresh_token"
You can then remove the dropbox folders from site-packages-3 or try this script which removes site-packages-3 from sys.path when running from a share sheet and temporarily reverts back to dropbox 6.4.0.
import sys
import appex
import console
print('file', appex.get_file_path())
def test():
print(sys.path[1])
if appex.is_running_extension(): # if share sheet
sys.path.remove(sys.path[1]) # remove site-packages-3 from sys.path
import dropbox
print('dropbox version', str(dropbox.__version__))
import dropboxlogin
dropbox_client = dropboxlogin.get_client()
print('Getting account info...')
account_info = dropbox_client.users_get_current_account()
print('linked account:', account_info)
test()
If you are just trying to upload a file from a share sheet, this script does not use the dropbox module:
import sys, os, json, shutil
import appex
import console
import keychain
import requests
from requests.auth import HTTPBasicAuth
app_key = "your_app_key"
app_secret = "your_app_secret"
refresh_token = "your_refresh_token"
def dropbox_token(refresh_token):
data = {'refresh_token': refresh_token, 'grant_type': 'refresh_token'}
try:
r = requests.post('https://api.dropbox.com/oauth2/token', data=data,
auth = HTTPBasicAuth(app_key, app_secret))
result = r.json()
except:
print(str(sys.exc_info()))
return { 'refresh_token': None, 'access_token': None, 'error': {'.tag': str(sys.exc_info())} }
#print('dbx:', result)
return result
def upload_file(source_filename, path):
with open(source_filename, 'rb') as f:
data = f.read()
parms = {}
parms['path'] = path
parms['mode'] = 'overwrite'
print (json.dumps(parms))
headers = {'Authorization': 'Bearer %s' % (access_token,),'Dropbox-API-Arg': json.dumps(parms),'Content-Type': 'application/octet-stream' }
url = 'https://content.dropboxapi.com/2/files/upload'
try:
r = requests.post(url, stream=True, headers=headers, data=data)
except:
print("Exception requests: %s" % str(sys.exc_info()))
result = r.json()
return result
local_path = os.path.expanduser('~/Documents/tmp')
access_token = dropbox_token(refresh_token)['access_token']
files = appex.get_file_paths()
if files != None and files != []:
for i in range(len(files)):
print('Input path: %s' % files[i])
basename = os.path.basename(files[i])
filename=os.path.join(local_path, basename)
shutil.copy(files[i], filename)
upload_file(basename, '/remote/dropbox/path/' + basename)
@ccc This link is for Microsoft OneDrive. My Dropbox space is limited.
The original link is a more permanent and reliable alias to this URL.
https://onedrive.live.com/?authkey=!AC94lHutmY315L4&id=98B88F62EF7BB2B3!125&cid=98B88F62EF7BB2B3
Ok. Now I see how to include source.
import requests
import urllib
import os
import ui
import json
def list_folder(folder_path='/'):
headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Content-Type': 'application/json' }
if folder_path == '/': folder_path = ''
data = {'path':folder_path}
r = requests.post('https://api.dropboxapi.com/2/files/list_folder', headers=headers, data=json.dumps(data))
result = r.json()
return result.get('entries', None)
def download_file(path, dest_filename, progress=None):
data = {'path':path}
headers = {'Authorization': 'Bearer %s' % (TOKEN,),'Dropbox-API-Arg': json.dumps(data)}
url = 'https://content.dropboxapi.com/2/files/download'
r = requests.post(url, stream=True, headers=headers)
You also need to replace 'path' with 'path_dislpay' in twice in def load_folder.
@ui.in_background
def load_folder(self):
infos = list_folder(self.path)
items = []
if self.path != '/':
items.append({'title': '..', 'image': 'ionicons-arrow-up-c-32', 'up': True})
if not infos:
import console
console.alert('Error', 'Could not load folder. Please check if you entered the access token correctly.', 'OK', hide_cancel_button=True)
self.status_label.hidden = True
return
for info in infos:
path = info.get('path_display')
name = os.path.split(path)[1]
if name.startswith('.'):
continue
is_dir = info.get('is_dir', False)
item = {'title': name, 'image': 'ionicons-folder-32' if is_dir else 'ionicons-ios7-download-outline-32', 'accessory_type': 'disclosure_indicator' if is_dir else 'none', 'is_dir': is_dir, 'path': info['path_display']}
@cvp I agree!!! I almost give up on this forum. I hope your continued presence here indicates that your health is headed in the right direction.
@FloMaster I'm also not an expert with SSH, but are you using the full path to the key file? You might try this.
client.connect('IP', username='florian', key_filename=os.path.expanduser('~/path to key'))
Pythonnista uses a very old version of paramiko.
@LankyDonkey
You may be able to run your dialogs in a new thread and save the @ui.in_background for the playback.
from threading import Thread, current_thread
def _in_background(fn, *args, **kwargs):
d1 = Thread(target=fn, args=args)
d1.start()
def in_background(fn):
def run(*args, **kwargs):
def _run():
return fn(*args, **kwargs)
if current_thread().name.startswith('Dummy'):
return _in_background(_run)
else:
return(fn(*args, **kwargs))
return run
@in_background
def search_button_tapped(self,sender):
@LankyDonkey Hello!
You may want to try with a @ui.in_background decorator.
@ui.in_background
def search_button_tapped(self,sender):
form_list_of_sections = []
search_dicts = []
Also, I have created a more complete dialogs replacement that might work for you here:
https://gist.github.com/sbbosco/cb44f88960c16a33704cc9f92980a113
You can test the replacement module with this script:
import sys, os, time
from datetime import datetime
import ui
import dialogs
# from uidialogs import dialogs
"""
test for modified dialog module:
https://gist.github.com/sbbosco/cb44f88960c16a33704cc9f92980a113
"""
today = datetime.now()
sections = [('Special Filters',
[{'key': 'limit', 'title': 'Artist Limit (0 for None):',
'type': 'number', 'value': '30'},
{'key': 'switch', 'title': 'Switch: ', 'type': 'switch', 'value': True },
{'key': 'date', 'title': 'Date: ', 'type': 'datetime', 'format': '%m-%d-%Y %H:%m',
'style': 'inline', 'value': today },
{'key': 'country_count', 'title': 'Country Count: ', 'type': 'check'},
{'key': 'best_artists', 'title': 'Best Artists: ', 'type': 'check'},
{'key': 'best_albums', 'title': 'Best Albums: ', 'type': 'check'},
{'key': 'best_genres', 'title': 'Best Genres: ', 'type': 'check'},
{'key': 'best_years', 'title': 'Best Years: ', 'type': 'check'},
{'key': 'best_decades', 'title': 'Best Decades: ', 'type': 'check'},
{'key': 'one_hit_wonders', 'title': 'One Hit Wonders: ', 'type': 'check'} ]),
('Year Range',
[{'key': 'year_from', 'title': 'Year From:', 'type': 'number', 'value': '1990'},
{'key': 'year_to', 'title': 'Year To: ', 'type': 'number', 'value': '2022'},
{'key': 'one_song', 'title': 'One Song: ', 'type': 'check', 'value': False},
{'key': 'missing_song_info', 'title': 'Missing Song Info:',
'type': 'check', 'value': False},
{'key': 'song_info', 'title': 'Song Info:', 'type': 'check', 'value': False},
{'key': 'sub_and', 'title': 'Subgenre AND:', 'type': 'check', 'value': False},
{'key': 'xmas', 'title': 'Include Xmas Songs?', 'type': 'check', 'value': False},
{'key': 'pos_from', 'title': 'Pos From: ', 'type': 'number', 'value': ''},
{'key': 'pos_to', 'title': 'Pos To:', 'type': 'number', 'value': ''},
{'key': 'missing_pos', 'title': 'Missing Pos:', 'type': 'check', 'value': False},
{'key': 'us_pos_from', 'title': 'US Pos From: ', 'type': 'number', 'value': ''},
{'key': 'us_pos_to', 'title': 'US Pos To:', 'type': 'number', 'value': ''}]),
('Rating',
[{'title': '3', 'type': 'check', 'value': True},
{'title': '4', 'type': 'check'},
{'title': '5', 'type': 'check'}]),
('Speed',
[{'title': 'S5', 'type': 'check'},
{'title': 'S4', 'type': 'check'},
{'title': 'S3', 'type': 'check'},
{'title': 'S2', 'type': 'check'},
{'title': 'S1', 'type': 'check'},
{'title': 'Missing', 'type': 'check'}]),
('Popularity',
[{'title': '[10]', 'type': 'check'},
{'title': '[9]', 'type': 'check'},
{'title': '[8]', 'type': 'check'},
{'title': '[7]', 'type': 'check'},
{'title': '[6]', 'type': 'check'},
{'title': '[5]', 'type': 'check'},
{'title': '[4]', 'type': 'check'},
{'title': '[3]', 'type': 'check'},
{'title': '[2]', 'type': 'check'},
{'title': '[1]', 'type': 'check'},
{'title': '[0]', 'type': 'check'}]),
('Charts',
[{'title': '50s', 'type': 'check'},
{'title': '60s', 'type': 'check'},
{'title': '70s', 'type': 'check'},
{'title': '80s', 'type': 'check'},
{'title': '90s', 'type': 'check'},
{'title': '2000s', 'type': 'check'},
{'title': '2010s', 'type': 'check'},
{'title': '2020s', 'type': 'check'}]),
('Genres',
[{'title': 'Rock', 'type': 'check'},
{'title': 'Pop', 'type': 'check'},
{'title': 'Indie', 'type': 'check'},
{'title': 'Rap', 'type': 'check'},
{'title': 'Thrash', 'type': 'check'},
{'title': 'Country', 'type': 'check'},
{'title': 'Dance', 'type': 'check'},
{'title': 'Disco', 'type': 'check'},
{'title': 'Soul', 'type': 'check'},
{'title': 'Post-punk', 'type': 'check'},
{'title': 'Punk', 'type': 'check'}]),
('Sub Genres',
[{'title': 'Aor', 'type': 'check'},
{'title': 'Modernindie', 'type': 'check'},
{'title': 'Modernrock', 'type': 'check'},
{'title': 'Summer', 'type': 'check'},
{'title': 'Classicrock', 'type': 'check'},
{'title': 'Northernsoul', 'type': 'check'},
{'title': 'Indierock', 'type': 'check'},
{'title': 'Drumnbass', 'type': 'check'},
{'title': 'Funk', 'type': 'check'},
{'title': 'Hairmetal', 'type': 'check'},
{'title': 'Blues', 'type': 'check'},
{'title': 'Reggae', 'type': 'check'},
{'title': 'Breakdance', 'type': 'check'},
{'title': 'Garage', 'type': 'check'},
{'title': 'Heavymetal', 'type': 'check'},
{'title': 'House', 'type': 'check'},
{'title': 'Cheese', 'type': 'check'},
{'title': 'Billy', 'type': 'check'},
{'title': 'Grime', 'type': 'check'},
{'title': 'Rare', 'type': 'check'},
{'title': 'Yachtrock', 'type': 'check'},
{'title': 'Synthpop', 'type': 'check'}]),
('Occasions',
[{'title': 'Christmas', 'type': 'check'},
{'title': 'Bonfire', 'type': 'check'},
{'title': 'Halloween', 'type': 'check'},
{'title': 'Birthday', 'type': 'check'}]),
('Tempo',
[{'title': 'Slow', 'type': 'check'},
{'title': 'Normal', 'type': 'check'},
{'title': 'Fast', 'type': 'check'},
{'title': 'Not slow', 'type': 'check'}])]
def display(playlist_filter):
result = dialogs.datetime_dialog(title='Date', done_button_title='Done', style='inline')
print('result', result)
print('form dialog standard')
diag = dialogs.form_dialog(title = 'Song Filter', sections=sections)
print('done')
return diag
@ui.in_background
def button_tapped(sender):
#result = display(None)
result = dialogs.form_dialog(title = 'Song Filter', sections=sections)
if result:
print(result['year_to'])
print(result['switch'])
print(result['date'])
print(result['3'])
dialogs.alert('Result', 'Success')
else:
dialogs.alert('Result', 'Failed')
if __name__ == '__main__':
view = ui.View()
view.name = 'Dialog Test'
button = ui.Button(title='Tap me!')
button.background_color = 'gray'
button.tint_color = 'white'
button.frame = (10, 10, 200, 100)
button.action = button_tapped
view.add_subview(button)
view.present('fullscreen')
@omz @LankyDonkey @shinyformica
I have created a new issue here: Pythonista-Issues #707.
@LankyDonkey Issue #1 is fixed in the latest update.
I thought I had the built in form dialog fixed with one line of code c.cells = None
, but scrolling down to 'New Genre' it still crashes.
I'll look at issue #2 in my version.
@LankyDonkey I'm glad it's working for you. There is an updated version.