Forum Archive

Pythonista 1.6 Beta

omz

So, with Apple's new TestFlight, I can finally have a lot more beta testers, and won't have to worry about device limits so much.

If you'd like to play with the next version of Pythonista before it hits the App Store, please send me an email with your Apple ID, and I'll see what I can do. I don't plan to use up all the slots yet (I might need some later), but if you've been posting here, there's a very good chance that I can give you access to the beta.

As for what to expect, here's a list of the new features in the current beta:

  • Improved support for the iPhone 6 and 6 Plus screen sizes
  • Removing files from the library moves them to a trash folder instead of deleting them immediately.
  • URLs in console output are 'linkified' automatically
  • New reminders module for accessing the iOS Reminders database (read/write)
  • New cb module for connecting to Bluetooth LE peripherals (experimental)
  • New dialogs module as an easy-to-use alternative to the ui module when you just need simple data entry. This also contains functions to import files from the iCloud Drive document picker (iOS 8 only). – note: this is broken in the current beta, but will be fixed in the next few days

Anyone?

Gerzer

I just sent you an email!

Can't wait for the release version!

Gerzer

Bluetooth support sounds awesome!

wradcliffe

I have a couple of interesting Bluetooth LE peripherals in hand right now.

A general purpose Arduino based microcontroller.
https://punchthrough.com/bean/

Part of the big Internet of Things development push. Would be cool to be able to access it from Pythonista.

MIDI interface
http://www.quicco.co.jp/archives/shop_menu/wireless-midi-interface-mi-1-2

I could test accessing these things using your module if that would help. I was just about to ask you if you would consider adding simple support for Core MIDI using this ...

https://github.com/sixohsix/simplecoremidi

I know that Apple just added official support for Bluetooth LE wireless MIDI in IOS8 so accessing this type of peripheral via CoreMIDI may still be the best approach.

wradcliffe

Curious to know what the Bluetooth LE support is for. I have a couple of very interesting peripherals that each require a speciall app to be installed on iOS in order to function. One is for MIDI and the other is a general purpose Arduino style computer.

JonB

How does testflight work ... Does the overwrite existing pythonista install? Or it creates a special version?
Presumably this rewuire ios8? Iirc testflight only works with 8+?

J

LawAbidingCactus

Hi @Omz. Just sent you an email. The reminders module definitely looks interesting!

LawAbidingCactus

@JonB, to my knowledge, it simply installs a provisioning profile separate from the official Pythonista install.

omz

I've just sent out invites to everyone who's asked so far, and I have plenty of slots left.

@JonB When you install the beta, it overwrites the App Store version, but it should keep your data, just like a regular update.

tomkirn

Hi @Omz,

just reading the release notes is awesome. I immediately tried some of the stuff and just have to say WOW. Just one question about the beta testing: Open comments about the the beta here or where to sent my remarks?

Tom

omz

As long as you make it clear that a thread is beta-related, it's totally fine with me to discuss it here.

I've just submitted a new build with a few fixes – most importantly, the dialogs module should actually work now. It'll be available in a few hours (it takes a while for iTunes Connect to process new uploads).

There's also a new twitter module that allows you to access iOS Twitter accounts from Python. This makes it a lot easier to use the Twitter API because authentication is handled automatically. There are some easy-to-use functions to do API requests directly with this module, but you can also use it just for generating Authorization headers that you can then pass to your favorite HTTP library (e.g. requests or urllib), if you want more control.

userista

Bug: passing variables into console.login_alert doesn't work and causes the command to hang (was working in stable).

omz

@hyshai Thanks, I'll look into that. So far, I can't reproduce the hanging, but it definitely doesn't work correctly (it always behaves as if it was cancelled).

techteej

Just started playing with it, truly great. Also noticed that sheet view doesn't have a fixed width and height now, and it responds to the views set size. Wasn't in the release notes so I'm not sure if this was meant to be there.

userista

@omz I narrowed it down. It has nothing to do with a variable. Multiple prompts are the issue e.g.

import console, keychain
keychain.get_password('test', 'test') #requires keychain.master_password which has a prompt
console.login_alert('hello') # this will never be called and the script hangs

another example

import console
console.login_alert('hello')
console.login_alert('goodbye') # this will never be called but the script exits silently

Is this possibly related to the bug of not showing the keyboard automatically when there's an input prompt (specifically the keychain.master_password prompt)?

dgelessus

This sounds interesting, looking forward to the update. Sent you an e-mail.

Does TestFlight require iOS 8? I have an oldish iPhone that can only run iOS 7 that I might be able to test with as well.

omz

I think TestFlight requires iOS 8.

techteej

@omz TestFlight does require iOS 8. How do you get the text entered into a text dialog?

omz

How do you get the text entered into a text dialog?

It's the return value of the text_dialog function.

briarfox

Just mailed you my apple id. Would love to test out BTLE.

Moe

I assume the appex module you teased on Twitter is not integrated/activated in this Beta? Anyway, it's pretty cool you're doing this and I am happy to test the Twitter integration :)

ccc

Feedback on the in-app "What's new in Pythonista?" docs... Change dialgos to dialogs and activate the hyperlink.

dgelessus

@ccc, I believe omz uses the same doc generator that is used for the standard library, in which case it will automatically link once the module name is recognized.

userista

@omz searching in the docs is very wonky. sometimes it works and sometimes it doesn't - just says "Searching......". (using an iPhone 5)

Gerzer

Just looking at the docs has me wowed! One tip would be to include an example script for BLE, as the protocol my be a little foreign to some.

dgelessus

A few things I've noticed so far:

  • The Pythonista.app bundle is now fully moved out of the "Data" folder and no longer easily accessible (sad face)
  • The Trash is invisible when empty (not really a bug, but somewhat counter-intuitive)
  • The Trash didn't appear for me the first time I deleted a file until I opened the settings (thus reloading the file list I suppose)
  • No way to selectively delete files from the Trash or restore multiple files at once
  • The Move menu still takes ages to open
  • Not specifically a beta bug, but in thumbnail view the default syntax highlight style is always used, no matter which one is selected in the settings
the_buch

@omz for reminders.get_reminders(completed=TRUE) is there currently, or could there be in a future version, a way to filter completed reminders with completion date. Perhaps something like reminders completed today, or the past week, or since a given date? That could be nice for keeping track of certain projects or self-quantification, et al. Also would like to see this module in Editorial eventually. Better than using url schemes in all these reminder workflows.

wradcliffe

I was able to use the Bluetooth LE module to talk to a LightBlue Bean and enumerate all of its services and characteristics.

I also tried reading all the values for each characteristic and ran into one problem. The values returned may or may not be text strings and if you simply assume they are and print them to the console you can get the module to throw an exception

TypeError: must be string without null bytes, not str

Trace back says it happened at line 73 in cb.py

After that you can recover but you can also crash completely or leave the module in a bad state and unable to talk to the peripheral.

Here is my code:

import cb
import sound
import time
import struct

class LightBlueBean (object):
    def __init__(self):
        self.peripheral = None

    def did_discover_peripheral(self, p):
        print 'Discovered:', p.name
        if p.name and 'Bean' in p.name and not self.peripheral:
            self.peripheral = p
            print 'Connecting to Bean...'
            cb.connect_peripheral(p)

    def did_connect_peripheral(self, p):
        print 'Connected:', p.name
        print 'Discovering services...'
        p.discover_services()

    def did_fail_to_connect_peripheral(self, p, error):
        print 'Failed to connect: %s' % (error,)

    def did_disconnect_peripheral(self, p, error):
        print 'Disconnected, error: %s' % (error,)
        self.peripheral = None

    def did_discover_services(self, p, error):
        print 'Did discover services...'
        for s in p.services:
            print 'Service:', s.uuid
            if True: # s.uuid == '180D'
                print 'Discovering characteristics...'
                p.discover_characteristics(s)

    def did_discover_characteristics(self, s, error):
        print 'Did discover characteristics...'
        for c in s.characteristics:
            print 'Characteristic:', c.uuid, ' Value:', c.value
            #if c.uuid == '2A37':
            #   self.peripheral.set_notify_value(c, True)
            self.peripheral.read_characteristic_value(c)

    def did_update_value(self, c, error):
        #somevalue = struct.unpack('<B', c.value[1])[0]
        #self.values.append(somevalue)
        #print 'Value: %i' % somevalue
        print 'Update to a value...'
        print 'Characteristic:', c.uuid, ' Value:', c.value

mngr = LightBlueBean()
cb.set_central_delegate(mngr)
print 'Scanning for peripherals...'
cb.scan_for_peripherals()

try:
    while True: pass
except KeyboardInterrupt:
    print 'keyboard interrupt - resetting'
    cb.reset()

Here is the console output:

Scanning for peripherals...
Discovered: Bean
Connecting to Bean...
Connected: Bean
Discovering services...
Disconnected, error: (10, u'The connection has failed unexpectedly.')
keyboard interrupt - resetting
Scanning for peripherals...
Discovered: Bean
Connecting to Bean...
Connected: Bean
Discovering services...
Did discover services...
Service: F000FFC0-0451-4000-B000-000000000000
Discovering characteristics...
Service: 180A
Discovering characteristics...
Service: A495FF10-C5B1-4B44-B512-1370F02D74DE
Discovering characteristics...
Service: A495FF20-C5B1-4B44-B512-1370F02D74DE
Discovering characteristics...
Service: 180F
Discovering characteristics...
Did discover characteristics...
Characteristic: F000FFC1-0451-4000-B000-000000000000  Value: None
Characteristic: F000FFC2-0451-4000-B000-000000000000  Value: None
Did discover characteristics...
Characteristic: 2A23  Value: None
Characteristic: 2A24  Value: None
Characteristic: 2A25  Value: None
Characteristic: 2A26  Value: None
Characteristic: 2A27  Value: None
Characteristic: 2A28  Value: None
Characteristic: 2A29  Value: None
Characteristic: 2A2A  Value: None
Characteristic: 2A50  Value: None
Did discover characteristics...
Characteristic: A495FF11-C5B1-4B44-B512-1370F02D74DE  Value: None
Did discover characteristics...
Characteristic: A495FF21-C5B1-4B44-B512-1370F02D74DE  Value: None
Characteristic: A495FF22-C5B1-4B44-B512-1370F02D74DE  Value: None
Characteristic: A495FF23-C5B1-4B44-B512-1370F02D74DE  Value: None
Characteristic: A495FF24-C5B1-4B44-B512-1370F02D74DE  Value: None
Characteristic: A495FF25-C5B1-4B44-B512-1370F02D74DE  Value: None
Did discover characteristics...
Characteristic: 2A19  Value: None
Update to a value...
Characteristic: F000FFC1-0451-4000-B000-000000000000  Value: None
Update to a value...
Characteristic: F000FFC2-0451-4000-B000-000000000000  Value: None
Update to a value...
Characteristic: 2A23  Value: Update to a value...
Characteristic: 2A24  Value: Bean
Update to a value...
Characteristic: 2A25  Value: Serial Number
Update to a value...
Characteristic: 2A26  Value: 201409080001 Img-B
Update to a value...
Characteristic: 2A27  Value: E
Update to a value...
Characteristic: 2A28  Value: Software Revision
Update to a value...
Characteristic: 2A29  Value: Punch Through Design
Update to a value...
Characteristic: 2A2A  Value: Update to a value...
Characteristic: 2A50  Value: Update to a value...
Characteristic: A495FF11-C5B1-4B44-B512-1370F02D74DE  Value: Update to a value...
Characteristic: A495FF21-C5B1-4B44-B512-1370F02D74DE  Value: 
Update to a value...
Characteristic: A495FF22-C5B1-4B44-B512-1370F02D74DE  Value: 
Update to a value...
Characteristic: A495FF23-C5B1-4B44-B512-1370F02D74DE  Value: 
Update to a value...
Characteristic: A495FF24-C5B1-4B44-B512-1370F02D74DE  Value: 
Update to a value...
Characteristic: A495FF25-C5B1-4B44-B512-1370F02D74DE  Value: 
Update to a value...
Characteristic: 2A19  Value: 1
keyboard interrupt - resetting
dgelessus

Instead of a plain print(data), try using print(data.replace("\x00", "")), with data being the value received from the peripheral. This simply removes any null bytes from the string to stop print from complaining. For debugging purposes you might want to replace null bytes with something like "NULL" so you can find them in the printed text.

userista

Is it new in this version that the various console.alert "types" now return the button number (as an int)? It broke a couple of my scipts...

omz

@wradcliffe

Most strings you're dealing with will probably be binary data, so I'd recommend encoding them as hex before printing (print c.value.encode('hex')). Nevertheless, there are definitely some stability issues when you encounter exceptions in any of the callbacks. I might change this to disconnect when an exception is thrown in a callback, which might make it more likely to recover successfully.

@hyshai

Is it new in this version that the various console.alert "types" now return the button number (as an int)? It broke a couple of my scipts...

The standard alert() function always did this, but I assume you're referring to other alert types? There are definitely some bugs with those right now, looking into it.

@the_buch

for reminders.get_reminders(completed=TRUE) is there currently, or could there be in a future version, a way to filter completed reminders with completion date.

Right now, it's not possible, but I will at the very least add a completion_date attribute to the Reminder class, so you could filter them manually.

@Moe

I assume the appex module you teased on Twitter is not integrated/activated in this Beta?

No, this won't be in 1.6.

@dgelessus

The Pythonista.app bundle is now fully moved out of the "Data" folder and no longer easily accessible (sad face)

That's an iOS 8 thing, nothing I can do about it, though it is still accessible if you have the correct path. Right now, you could do this with scene.get_image_path (which will return a path that is within the app bundle, you'd have to walk up the hierarchy a bit to get the root directory of the app).

omz

@the_buch

Also would like to see this module in Editorial eventually. Better than using url schemes in all these reminder workflows.

Yes, the reminders module will definitely be in the next update of Editorial as well, same goes for twitter and probably dialogs. I don't plan to integrate cb in Editorial though.

wradcliffe

I have been working with the dialogs form_dialog and this test code:

import dialogs
import datetime

dt = datetime.datetime.now()
fields = [
  {'key' : 'the_key0', 'type' : 'switch', 'value' : 'the_value'},
  {'key' : 'the_key1', 'type' : 'text', 'value' : 'the_value'},
  {'key' : 'the_key2', 'type' : 'url', 'value' : 'the_value'},
  {'key' : 'the_key3', 'type' : 'email', 'value' : 'the_value'},
  {'key' : 'the_key4', 'type' : 'password', 'value' : 'the_value'},
  {'key' : 'the_key5', 'type' : 'number', 'value' : '100' },
  {'key' : 'the_key6', 'type' : 'check', 'value' : 'the_value'},
  {'key' : 'the_key7', 'type' : 'datetime', 'value' : dt},
  {'key' : 'the_key8', 'type' : 'date', 'value' : dt},
  {'key' : 'the_key9', 'type' : 'time', 'value' : dt}
]
result=dialogs.form_dialog(title='look at all this stuff', fields=fields)
print 'text:', result

When I look at this, I can't help thinking that all the field data should be aligned right and that there should be labels on each one. Should there be a 'label' key added? How is the user suppose to know what each field represents?

I suppose you could use sections to implement labels, but that seems wrong. Section as a way of breaking up and labeling combinations of like fields is good.

sections = [
  ('switch section', [{'key' : 'the_key0', 'type' : 'switch', 'value' : 'the_value'}]),
  ('text section', [{'key' : 'the_key1', 'type' : 'text', 'value' : 'the_value'}])
]
result=dialogs.form_dialog(title='look at all this stuff', sections=sections)
print 'text:', result

Also - datetime requires/returns a datetime and date and time do as well. Should date and time require and return date and time objects?

Also, Also - email, url, number don't seem to do anything different then text. Shouldn't they be enforcing formatting or something?

omz

How is the user suppose to know what each field represents?

There's a 'title' key you can use, the documentation is a little incomplete right now with regards to the supported keys.

Also - datetime requires/returns a datetime and date and time do as well. Should date and time require and return date and time objects?

Maybe, it was a bit easier to implement this way, and you can easily convert it to a date or time object by calling datetime.date() or datetime.time().

Also, Also - email, url, number don't seem to do anything different then text. Shouldn't they be enforcing formatting or something?

The keyboard for URL and email fields is different (e.g. the email keyboard has an additional '@' key), and they have autocorrection disabled by default.

techteej

text = dialogs.text_dialog(title='Name Your Reminder', autocapitalization=ui.AUTOCAPITALIZE_SENTENCES, spellchecking=None)
r = reminders.Reminder()
r.title = text
r.save()

Gives me a excepted string error.

omz

@techteej Did you use the Done button in the text dialog? If you cancel the dialog (via 'x'), it'll return None.

techteej

It shows up while the dialog is up

omz

@techteej Is that really your entire code?

wradcliffe

There's a 'title' key you can use ...

The title key works perfectly. I noticed that the text color defaults to black except for the checkbox case where it is blue (or same as check color).

Looking forward to more keys assuming now that you are going to have color, font, etc. to really allow full control.

What about field validation? Popping up a special keyboard is nice, but a delegate function called on each keystroke would nail it. I used to work on named entity extraction in text and you can do a lot with simple regex matching, but you need to run code to do credit card numbers (for the checksum). I think mapping field types to entities would be super cool.

techteej

@omz Here is my entire code.

# coding: utf-8
import dialogs
import reminders
import ui

class Data (ui.ListDataSource):
    def __init__(self, items=None):
        ui.ListDataSource.__init__(self, items)

    def tableview_cell_for_row(self, tableview, section, row):
        cell = ui.TableViewCell()
        cell.text_label.text = str(self.items[row])
        return cell

v = ui.load_view('reminders')

def button_action(sender):
    if segment.selected_index == 0:
        todo = reminders.get_reminders(completed=False)
        for r in todo:
            full = r.title
            reminders_table.data_source = Data(items=[full])
            reminders_table.reload()
    elif segment.selected_index == 1:
        done = reminders.get_reminders(completed=True)
        for r in done:
            full = r.title
            reminders_table.data_source = Data(items=[full])
            reminders_table.reload()

def but_action(sender):
    text = dialogs.text_dialog(title='Name Your Reminder', autocapitalization=ui.AUTOCAPITALIZE_SENTENCES, spellchecking=None)
    r = reminders.Reminder()
    r.title = text
    r.save()

segment = v['segmentedcontrol1']
segment.action = button_action
reminders_table = v['reminders']

create_button = ui.ButtonItem()
create_button.image = ui.Image.named('ionicons-ios7-plus-empty-32')
create_button.action = but_action

v.right_button_items = [create_button]
v.present('sheet')
omz

@techteej Similar to console alerts, dialogs don't work from the main UI thread (allowing this would essentially lead to a deadlock). Try decorating your but_action function with @ui.in_background.

techteej

Found a bug. In the UI Editor when delete enabled is off, you can still delete rows in a table.

TutorialDoctor

Will dialogs allow me to create tables with cells (rows and columns)?

This would be most useful, otherwise I don't see an advantage to dialogs over UI.

Pinch to zoom in the UI editor would be nice too, as well as saving UI control presets.

andymitchhank2

The beta doesn't seem to ask for permission to use Location Services when using the location module for the first time.

I thought it might be for all required permissions, but the Reminders seems to work fine.

Anyone have any ideas on this?

omz

Found a bug. In the UI Editor when delete enabled is off, you can still delete rows in a table.

I can't reproduce this. Note however that the "delete enabled" attribute only applies to the default ListDataSource that gets created as a convenience when loading a TableView from a pyui file. If you set a different data source after loading the view, it doesn't have an effect.

Will dialogs allow me to create tables with cells (rows and columns)?
This would be most useful, otherwise I don't see an advantage to dialogs over UI.

No. For the most part, dialogs doesn't really let you do things you couldn't do with the ui module, but things like form_dialog would require a lot more code when done with ui directly. There are also some things that you couldn't do otherwise, like dialogs.import_file() or dialogs.share().

The beta doesn't seem to ask for permission to use Location Services when using the location module for the first time.

Are you sure that you didn't give permission with the previous version? The beta would inherit those permissions (it's the same app after all).

andymitchhank2

I uninstalled the beta, installed the current App Store version and ran a simple get location script. It prompted for Location permissions and retrieved my location.

I then uninstalled the App Store version, installed the beta again and ran the script.

The script prints out the value of location.get_location(). It is currently printing out None in the beta with no prompt to allow access to Location Services.

zencuke

Bluetooth LE? Bless you! I'd love to beta test that. Sending email as soon as I track down your email address ;-)

-steve (aka zencuke)

omz

@andymitchhank Hmm, that does sound like a bug indeed. Which version of iOS are you using?

andymitchhank2

8.1 on an iPad 3 and iPhone 6 Plus. Both are having the same issue.

omz

@andymitchhank Thanks, I've been able to reproduce the issue. Apparently this has to do with some new requirements for location permissions in iOS 8, looking into it.

wradcliffe

I am still working on reproducing an issue that occurs in one of the callbacks in the cb module. What happens is some kind of heap corruption when I call functions in other modules. I was writing some code that used str.append() and getting all kinds of wierd behavior. It looks like either heap corruption or blown stack.

Trouble started when I modified the code trying to print out returned values in a characteristic. I used (print c.value.encode('hex')) in two callbacks and it did not "work". The program just printed nothing but also just returned from the callback and stopped working.

I could use a few hints on how to get a good repro case. I could post my code, but the effect is random and the code needs to access a specific device. Any ideas on how to stress the heap or stack in this callback environment would be appreciated.

polymerchm

And how does one find OMZ's e-mail address? happy to help.

ccc

Click on the word "email" in the very first post in this thread... It is a hyperlink.

Gcarver166

ui.TableView.row_height seems to always be -1 for me. This is new behavior. I can provide repro code if you need it.

techteej

Trying to make a table action so when you select a row it checks off the reminder, is this how that logic would work?

def picked(sender):
    r.title = sender.data_source.items[row]
    if r.title in reminders.get_reminders(completed=False):
        print 'good'
    r.completed = True
omz

ui.TableView.row_height seems to always be -1 for me. This is new behavior. I can provide repro code if you need it.

Thanks, should be fixed in the build after the next one (already uploaded that).

Trying to make a table action so when you select a row it checks off the reminder, is this how that logic would work?

I can't see where r is coming from in that example and why you would set its title when you actually want to set its completion state.

misha_turnbull

Sounds great! Just send you an email (or two--sorry)

omz

@techteej Here's a very simple example of a table view UI to check off reminders:

import reminders
import ui

def table_action(sender):
    item = sender.items[sender.selected_row]
    r = item['reminder']
    r.completed = True
    r.save()
    del sender.items[sender.selected_row]

def main():
    v = ui.TableView()
    v.frame = (0, 0, 500, 500)
    v.name = 'To Do'
    all_reminders = reminders.get_reminders(completed=False)
    items = [{'title': r.title, 'reminder': r} for r in all_reminders]
    data_source = ui.ListDataSource(items)
    data_source.action = table_action
    v.data_source = data_source
    v.delegate = data_source
    v.present('sheet')

main()
zencuke

Has anyone else seen this.

I can't install 1.6 because when I click on "Open in Testflight" when reading the apple invite email in gmail it insists on opening iTunes on the Testflight page. When I click on "OPEN" Testflight then says I have to click the link in the email. Has anyone else been able to install from gmail? Is this a gmail bug? I haven't configured apple email so I can't click the link from there.

Any suggestions?

Edit: Solved it. It was Chrome. Gmail launches Chrome to resolve urls. It required cut and paste of the link from Crome to Safari.

omz

@zencuke Maybe try copying the link from Gmail and then pasting it in Safari?

zencuke

Still reading the doc. (Actually I connected with the SensorTag and my heart rate monitor first ;-) ) Thanks for Reminders. I didn't realize how much useful functionality (Calendars, Alarms, even geo-location alarms) that included.

Reminders question: This may be a limitation in the apple framework but would it be possible to add an action_url to reminder alarms like there is in the notifications module? It would be nice to implement custom behavior when an alarm happens, from snooze to marking a reminder as done or even changing the reminder contents.

omz

Reminders question: This may be a limitation in the apple framework but would it be possible to add an action_url to reminder alarms like there is in the notifications module?

No, that's technically impossible. The notifications are entirely handled by the built-in Reminders app.

dgelessus

Alright, a few more things I noticed. Some of these were already present in 1.5 or are suggestions, but I suppose it doesn't make any difference now that the beta is available to a few people.

  • [major] It is no longer possible for action menu scripts to open input_alerts and such. Instead the script will forever hang and cannot be stopped without killing Pythonista.
  • [major] password_alert always returns None.
  • [major unless you read the source code] In the documentation for dialogs.form_dialog() there is no mention of the "title" dictionary key.
  • [minor] When viewing a non-Python text file it is not possible to edit the action menu list.
  • [minor] site builtins copyright, credits, license, exit and quit are missing. (The last two are not very important in Pythonista though.)
  • [minor] In the tap-and-hold menu of the console's Clear button, the Clear Output button does nothing.
  • [visual] Still no line break after the EXIT (n) output caused by SystemExits.
  • [visual] The console ignores sys.ps1 and sys.ps2, and always uses >>> and ... instead.
  • [suggestion] Perhaps also add the "New Folder" option to the "create" menu of the file list, so folders can be created without needing to go into edit mode.
  • [suggestion] Perhaps make editor scripts available from the "share" button in the console in addition to that in the editor.
  • [suggestion] When entering multiline statements (if, def, etc.) in the console, didn't the ">" previously change to "..."?
  • [suggestion] When entering multiline statements in the console, change the "play" button to "cancel", so that KeyboardInterrupts can be sent to cancel statements mid-input.

PS - Simple test for console-related bugs:

import console

def main():
    try:
        print(console.alert("alert", button1="OK"))
        print(console.input_alert("input_alert"))
        print(console.password_alert("password_alert"))
    except KeyboardInterrupt as ki:
        print(ki)

if __name__ == "__main__":
    main()

Works as a main script, but hangs when added and run as an editor script. Both methods worked fine in 1.5.

TutorialDoctor

Are all features that will be included in the final release included in the beta?

It would be nice to have a phone module. And I have been wanting some way to interact with facebook (mainly for editorial).

omz

@dgelessus Thanks a lot, some very good points there.

@TutorialDoctor

Are all features that will be included in the final release included in the beta?

Yes. I have more things in the pipeline, but probably not for 1.6.

It would be nice to have a phone module. And I have been wanting some way to interact with facebook (mainly for editorial).

Not sure what you'd expect a hypothetical phone module to do. There isn't much phone-related stuff that is accessible from third-party apps.

dgelessus

@TutorialDoctor, you might want to look up tel:// URLs. They are supported by practically all smartphone operating systems, and on iOS I believe they can be used for FaceTime calls on non-iPhones as well. Skype also understands the callto:// protocol, but that appears to be mostly Microsoft-specific. In any case they are normal URLs and can thus be opened using webbrowser.

techteej

@omz Bug,when archiving a folder to send as an email, the email share sheet is never presented.

TutorialDoctor

@dgelessus.

Hehe I figured I'd dig up a way, so shortly after the post I did find out about tel:, callto: mailto: etc.

@omz

I think it might be that i am not using an iPhone (why tel: doesn't show an option for calling) I think that will do though.

Can't wait to see a full feature list for 1.6!

zencuke

Can the new dialogs be used as popups in ui like console alerts can? I'm about to try so I guess I'll be able to answer my own question.

henryiii

What happens if I get added? Will it be listed as an update, etc... I sent an email.

zencuke

@henrii Apple sends you an email telling you what to do.

wradcliffe

I have tested the latest update cb module with my LightBlue Bean and things are working well. I was able to write an Arduino sketch that sends data out on several characteristics and read the data on the iPad. Updates to the characteristics on the Bean are picked up as they occur on the iPad. I have not validated that the serial data transfer (GATT) characteristic is working properly yet, it is not documented very well.

I am not seeing any exceptions or other flaky behavior.

I will be moving on to talking to multiple devices next to see how it scales.

Gcarver166

Selecting an action from the action menu closes the keyboard and clears the cursor position.
I have a number of scripts that use editor.get_selection() that do not work correctly now as when they run this value is not set. I suspect this is also causing some of my scripts that use the selection to crash pythonista.
Not to mention that it is annoying to have to re-open the keyboard after performing an action.

Gcarver166

There does not seem to be a way to set a tint or background color to transparent in the ui editor's new color picker.

tomkirn

@OMZ: Is it possible to change the ui editor's color picker to alternatively manually enter the color code?

omz

Is it possible to change the ui editor's color picker to alternatively manually enter the color code?

I'll see what I can do, currently not possible.

There does not seem to be a way to set a tint or background color to transparent in the ui editor's new color picker.

Yes, that's currently not possible either (unless you want a fully-transparent background, in which case you can use the "⧄" button). I'll think about how to add the possibility of setting an alpha value in the new UI.

Selecting an action from the action menu closes the keyboard and clears the cursor position. I have a number of scripts that use editor.get_selection() that do not work correctly now as when they run this value is not set. I suspect this is also causing some of my scripts that use the selection to crash pythonista. Not to mention that it is annoying to have to re-open the keyboard after performing an action.

Are you referring to the iPhone or iPad version? I would also be very interested in a script that reproducibly crashes when using the editor selection.

wradcliffe

Today I decided to switch from my Bean BLE device to a MIDI interface. I figure that a MIDI keyboard should really stress out the system since it can easily generate one shot data or whole streams. Luckily the device seems to work well and I can send and receive MIDI data to it from a script and the cb module.

I have noticed one with this device. You can pretty easily get the "connection" into a bad state by exiting the script and not formally closing it. I added the following code to the sample and it seems to cure the problem.

try:
    while True: pass
except KeyboardInterrupt:
    print 'keyboard interrupt - resetting'
    if mngr.peripheral != None:
        cb.cancel_peripheral_connection(mngr.peripheral)
    cb.reset()

I have been assuming that cb.reset would close any open connections but that must not be the case. Does the Peripheral object have logic to close an open connection?

Also - I am not yet an expert on Bluetooth LE, but I have read up enough on it to know that the whole concept of connections and how they work is complex. There are various timeouts in place to close down open connections that you may have to set as you work with the core api's. Might be a good idea to review that.

Gcarver166

Here is my script that crashes unless I remove any reference to sel. So that's just a guess by me of the cause.

import editor
import os
from time import sleep

dbname = 'swap.txt'

if __name__ == '__main__':
  fname = editor.get_path()
  sel = editor.get_selection()

  try:
    dbfile = open(dbname,'r')
    newf = dbfile.readline().strip() #strip off the newline character.
    poss = dbfile.readline()
    pos = int(poss)
    dbfile.close()
    editor.open_file(newf)
    sleep(0.5)  #wait a bit for the file to open.
    editor.set_selection(pos, pos)
  except:
    pass

  pos = 0 if sel == None else sel[0]
  data = [fname, '\n', str(pos)]
  dbfile = open(dbname,'w')
  dbfile.writelines(data)
  dbfile.close()
omz

@Gcarver I see, thanks! Does it also crash if you increase the sleep interval to something like 1.5 or 2.0 seconds (just to be sure). It obviously shouldn't crash either way, but if it goes away with a longer interval, that would confirm the suspicion I have about the reason for this.

polymerchm

Before I descend down the rabbit hole, I am warned I might lose my data. True or false? If true, how to back up my goodies?

Gcarver166

Sorry to say any sleep amount still crashes. However commenting out the set_selection call removes the crash.

dgelessus

@polymerchm, I don't think you should lose any data, mine survived the beta upgrade. It's still a good idea to make a backup, ideally a manual one on your computer, before getting the beta, in case something should go wrong.

polymerchm

Indeed, it did not clobber anything. On to exploring. Guess I need to buy some BLE thingie to exercise it. BLE MIDI controller on the way!!!! (always the musician).

wradcliffe

I have some questions about the cb implementation. I have noticed that there is no support for Descriptors or Advertisement info.

Descriptors of characteristics would be nice if they are actually implemented by the BLE device manufactures. It would make it possible to write a nice generic BLE browser app.

Advertisement data is probably more important since this seems to be the way you get access to the signal strength of the remote device. It can also be the main way that all of the data a device produces is transmitted. This is the way that beacons are suppose to work. There is no connection ever made since all the data comes across as part of the advertisement.

Also, looking at the apple sdk, I notice that they have both a central manager and peripheral manager while your implementation rolls them into one. This keeps things simple, but it also makes managing multiple peripherals difficult.

The issue of persistence is an additional concern. I have read that the design of BLE encourages the persistence of service and characteristic uuids after they are discovered so that you don't need to rescan for them every time your app runs. The apple sdk seems to have support for this with calls you can make to do scans for connected peripherals or for specific services.

Is Apple still messing around with all this and changing their APIs a lot? In reading through the online docs, I keep seeing lots of crossed out functions and new ones so it looks kinda rough. I can't even find a mention of how MIDI is suppose to be implemented despite the fact it suppose to already be part of ios8.

misha_turnbull

I think I may have found a few bugs in the dialogs and console modules:

dialogs
I've only really played with the import_file() function, and I've noticed several things. However, I can't seem to find a way to reliably produce one exact problem but whatever the cause it either: crashes the app (jump to homescreen) OR unexpectdly closes the dialog.
So far this has happened when I:

  • 1. switch from tile view to list view
  • - this one's a bit longer
  • 1. Switch to list view
  • 2. Open external source menu
  • 3. Set google drive to `on`
  • `console` I've only noticed one thing with this module and I'm not really sure how to reproduce it, I get varying results every time. It has to do with the set_color() function, I use it in a script I run at least 3 times a day (its a calculator using sympy, GREAT for math class). I call `set_color(0.36458m 0.0, 1.0) and print the following:
  • Imported:
  • console, canvas, random,
  • fmath as fm, numpy as np, decimal as d,
  • * from sympy, * from database.prefixes, Dynamic from containers, ctype from type.ctype,
  • division+generators from __future__
  • Assigned:
  • a, b, c as norm
  • k, i, n as int
  • f, g, h as func
  • Reassigned:
  • fm.m -> m, fm.fr -> frac, fm.cm -> cm
  • Loading constants... (I apolgogize for the bullet points; couldnt get the string to work any other way) And then the script continues. For some reason, the "fm.fr" two lines from the end of the string appears as a lighter blue. I have no clue why. I can provide the rest of the script if you would like but it won't work as I import a bunch of custom modules.
  • omz

    Regarding dialogs.import_file: Yes, I've noticed a few issues as well (mostly the dialog closing unexpectedly), but I don't think there's much I can do about this. Basically everything that happens inside the document picker happens in an entirely different process, so Pythonista isn't actually doing anything while you navigate there... I might be wrong, but it seems to me that those are either iOS 8 or Google Drive bugs.

    As for the set_color thing, I think this has to do with a new feature in the console that tries to highlight URLs automatically. frm.fr looks like a URL, so it gets a blue color (and you could tap it to open the browser). Obviously, this is not what you want in this case – I'll definitely consider making this feature optional.

    wradcliffe

    I have a question about the cb callbacks that I could use some insight on. I have been writing various test scripts and noticing that did_update_value seems to drop values if they are coming in too fast. I can use MIDI devices to generate a LOT of events and noticed that many of them are never logged by my script. It is obvious now that having any print statements or anything else that is slow in there is an issue and can cause data loss and other problems.

    So how do I deal with this during script development and determine what is actually going on if I can't log using print statements?

    How do I determine what is actually going on in the callback? Is the callback run in just one thread and events serialized and buffered by the os or is there no buffering and events that come in while my script is handling the callback simply dropped?

    I am thinking that I need to implement a special logger that sends the info to a queue like in QueueHandler in 3.0. This might be fast enough to support development and debug.

    Any ideas, thought, and suggestions are welcome.

    polymerchm

    Look at my chordcalc repository. The class debugStream allows you to acculumate depug output without calling print to slow things down. You can create multiple streams and dump each separtely when done.

    wradcliffe

    Thanks polymerchm for the suggestion. I remember seeing this now as part of your app.

    class debugStream():
        """ create buffered output to speed up console printing"""
        def __init__(self):
            self.out = ''
    
        def push(self,string,*args):
            self.out += string.format(*args) + '\n'
    
        def send(self):
            print self.out
            self.out = ''
    

    In my case I am in a callback handling a constant stream of MIDI data in the form of "time tick" events. This makes it difficult to know when to push and when to send. I guess I could just add some simple auto flushing logic to this based on the string length.

    Thanks for the simple and practical suggestion.

    henryiii

    A minor suggestion: the gui doesn't have presets for iPhone 6 or 6+. I haven't been able to run too much, though my standard scripts seem to work fine.

    polymerchm

    Potential bug
    in beta

    View.present(style='full_screen', orientation=('landscape'))

    Does not prevent rotation. With or without parens around 'landscape'

    IPad Air 2

    omz

    Potential bug in beta
    View.present(style='full_screen', orientation=('landscape'))

    First off, it should be orientations (plural), not orientation, but I assume that's just a typo (you'd get an error otherwise). More importantly, you should pass a sequence of valid orientations, not a single orientation. It looks like you're trying to pass a tuple, but tuples that only contain a single item must contain a trailing comma, like this: ('landscape',). The method accepts your single string because it expects a sequence, and strings are actually sequences (of characters), but you won't get the proper behavior this way.

    polymerchm

    Thanks. Didn't know about the trailing comma formalism in a in a tuple of text strings. Works just fine now. Sorry for false alarm.

    roosterboy197

    Any chance of getting lxml in Pythonista 1.6 (and the next version of Editorial too, while I'm at it)? I would really, really like to use openpyxl (or python-xlsx) and python-docx to read/edit Excel and Word docs in my Dropbox but they have lxml as a dependency and, as a C-based library, it's not user-installable.

    briarfox

    I've been having a lot of fun with the cb module. So far, I've been talking with a ti sensor tag. I have also been using a piconsole to communicate with a Raspberry pi via btle.

    I'm having one issue and I'm not sure why. I have issue detecting my iphone. It seems to only detect it on rare occasions. Has anyone else been able to see with iphone while it's in discover mode?

    filippocld223

    Ole, remember to reinsert the xcode export!

    zencuke

    @filippocld: I have found that using OMZ's online xCode snapshot is not quite as convenient but it works, in case the export functionality is an issue.

    wradcliffe

    What is this xCode snapshot? Is this the template project currently needed build your own app? Is there a version we should look at that is for 1.6?

    zencuke

    You will find a link to the snapshot mentioned in the 1.5 release notes. As far as I know that is still a snapshot of the 1.5 base but I haven't looked lately.

    roosterboy197

    I would really appreciate a setting to make folders sort alphabetically within the list of files (Mac style) instead of at the top (Windows style). I find the latter incredibly rage-inducing.

    techteej

    @omz Just a few thoughts:

    • [Bug] When archiving a folder to send as an email in the export menu, the email share sheet is never presented.
    • [Feature Request] Would it be possible to get a color picker dialog?
    • [Feature Request] Would it be possible to get a console.hud_alert with a loading icon? Sort of like when creating an archive?
    omz

    [Bug] When archiving a folder to send as an email in the export menu, the email share sheet is never presented.

    I can't reproduce this. iPhone or iPad? Which iOS version? Does it work with a single file?

    [Feature Request] Would it be possible to get a console.hud_alert with a loading icon? Sort of like when creating an archive?

    You can do this with console.show_activity() and console.hide_activity(), e.g.:

    import console
    import time
    
    console.show_activity('Please wait')
    time.sleep(1)
    # do some work here...
    console.hide_activity()
    
    blmacbeth

    @techteej I have a thing for you. It can (AND SHOULD) be cleaned up and or modified to look better.

    import ui
    from random import random
    
    def color_chooser_dialog():
        '''
        Shows a color chooser dialog and returns the color
        as a hex value. I may have it return a tuple, but 
        most Python modules will accept ANY color representation
        '''
        def slider_action(sender):
            # Get the root view:
            v = sender.superview
            # Get the sliders:
            r = v['slider1'].value
            g = v['slider2'].value
            b = v['slider3'].value
            # Create the new color from the slider values:
            v['view1'].background_color = (r, g, b)
            v['label1'].text = '#%.02X%.02X%.02X' % (r*255, g*255, b*255)
    
        def shuffle_action(sender):
            v = sender.superview
            s1 = v['slider1']
            s2 = v['slider2']
            s3 = v['slider3']
            s1.value = random()
            s2.value = random()
            s3.value = random()
            slider_action(s1)
    
        class ColorMixer (ui.View):
            def __init__(self):
                self.val = None
    
                self.background_color = 'white'
                self.frame = (0,0,320,244)
    
                view1 = ui.View(name='view1')
                view1.frame = (6,6,154,59)
                view1.flex = 'R'
    
                label1 = ui.Label(name='label1')
                label1.frame = (168,6,148,59)
                label1.flex = 'L'
                label1.font = ('<system>', 32)
                label1.number_of_lines = 0
                label1.alignment = ui.ALIGN_CENTER
                label1.text = 'FFFFFF'
    
                slider1 = ui.Slider(name='slider1')
                slider1.frame = (6,73,308,34)
                slider1.flex = 'W'
                slider1.tint_color = '#ff0000'
                slider1.value = .5
                slider1.continuous = True
                slider1.action = slider_action
    
                slider2 = ui.Slider(name='slider2')
                slider2.frame = (6,115,308,34)
                slider2.flex = 'W'
                slider2.tint_color = '#00ff00'
                slider2.value = .5
                slider2.continuous = True
                slider2.action = slider_action
    
                slider3 = ui.Slider(name='slider3')
                slider3.frame = (6,157,308,34)
                slider3.flex = 'W'
                slider3.tint_color = '#0000ff'
                slider3.value = .5
                slider3.continuous = True
                slider3.action = slider_action
    
                button1 = ui.Button(name='button1')
                button1.frame = (168,199,146,38)
                button1.flex = 'L'
                button1.title = 'Done'
                button1.font = ('<system>', 15)
                button1.action = self.done_action
    
                button2 = ui.Button(name='button2')
                button2.frame = (6,199,145.5,38)
                button2.flex = 'R'
                button2.title = 'Shuffle'
                button2.font = ('<system>', 15)
                button2.action = shuffle_action
    
                self.add_subview(view1)
                self.add_subview(label1)
                self.add_subview(slider1)
                self.add_subview(slider2)
                self.add_subview(slider3)
                self.add_subview(button1)
                self.add_subview(button2)
    
                shuffle_action(button2)
    
            def done_action(self, sender):
                self.val = self['label1'].text
                self.close()
    
        w = ColorMixer()
        w.present('popover')
        w.wait_modal()
        return w.val
    
    if __name__ == '__main__': print color_chooser_dialog()
    

    Just remember to put @ui.in_background if you call it. I made that mistake too many times.

    techteej

    @omz As for the bug, iPad on iOS 8.1.1 (and I had it on iOS 8.1 as well). Actually doesn't work on a single file either.

    @blmacbeth Just think though, wouldn't dialogs.color_picker_dialog(title='', multiple=False) that returns a color be so much easier?

    Gcarver166

    ui.TableView.row_height is still always -1.

    techteej

    Not sure why line 10 gives me an error.

    # coding: utf-8
    import dialogs
    import reminders
    import ui
    
    v = ui.load_view('reminders')
    reminders_table = v['reminders']
    
    def picked(sender):
        item = sender.items[sender.selected_row] # gives me error
        r = item['reminder']
        r.completed = True
        r.save()
        del sender.items[sender.selected_row]
    
    def grabbed():
        global todo_items, completed_items
        todo = reminders.get_reminders(completed=False)
        todo_items = [{'title': r.title, 'reminder': r} for r in todo]
        done = reminders.get_reminders(completed=True)
        completed_items = [{'title': r.title, 'reminder': r} for r in done]
        reminders_table.data_source = ui.ListDataSource(items=todo_items)
        reminders_table.data_source.action = picked
        reminders_table.reload()
    
    def button_action(sender):
        if segment.selected_index == 0:
            reminders_table.data_source = ui.ListDataSource(items=todo_items)
            reminders_table.data_source.action = picked
            reminders_table.reload()
        elif segment.selected_index == 1:
            reminders_table.data_source = ui.ListDataSource(items=completed_items)
            reminders_table.data_source.action = picked
            reminders_table.reload()
    
    @ui.in_background
    def but_action(sender):
        fields = [{'key' : 'name', 'type' : 'text', 'value' : 'Name your reminder'},]
        result=dialogs.form_dialog(title='Create a Reminder', fields=fields)
        r = reminders.Reminder()
        r.title = result['name']
        r.save()
        segment.selected_index = 0
        grabbed()
    
    segment = v['segmentedcontrol1']
    segment.action = button_action
    reminders_table.data_source.action = picked
    create_button = ui.ButtonItem()
    create_button.image = ui.Image.named('ionicons-ios7-plus-empty-32')
    create_button.action = but_action
    grabbed()
    v.right_button_items = [create_button]
    v.present('sheet')
    
    henryiii

    I've got a bad image loading related bug. If you do this:

    import ui
    
    imageurl = 'http://s9.postimage.org/n92phj9tr/image1.jpg'
    
    v = ui.View()
    v.background_color = 'white'
    
    im = ui.ImageView()
    im.flex = 'LRTBWH'
    im.load_from_url(imageurl)
    
    v.add_subview(im)
    v.present()
    

    Then run, cancel, and run again, Pythonista hangs and has to be closed from multitasking, then often the icon has to be pressed twice just to open it again.

    misha_turnbull

    I've found something that I think is a weird bug...I ran the exact same code on a Windows machine (albeit in Python 3.4) and it worked fine. However in Pythonista it does..well..nothing.

  • maxchar = 128
  • charset = range(maxchar)
  • for n in charset:
  • print "{0:2s} => {1:2s}".format(str(n), chr(n)) The problem seems to be in the `for` loop, every time I run the script nothing is printed. I check the value of n and it is equal to 0. I have no idea why. Apologies for the bullet points..it was the only way I could get newlines by insertings list element HTML tags. They wer'nt present in the code. Also the print statement was indented. (I'm terrible at markdown)
  • JonB

    One thing that is sometimes strange is that certain errors don't show up in the console, but show up as a red dropdown in the filename in the editor. That's what happened when I ran your code. You will notice the filename changes to re, then when you click on it it shows the error. Not sure why some errors show up this way, some show as red arrows, and some show in the console.

    As for the specific problem, apparently you cannot print null bytes ( try print chr(0) in the console to see the exception).

    maxchar = 128
    charset = range(maxchar)
    for n in charset:
        try:
            print "{0:2s} => {1:2s}".format(str(n), chr(n))
        except TypeError:
            print 'type error with n={}'.format(n)
    

    To properly format code, you want three backticks, on a newline, with a newline before it, and likewise at the end. Note this doesn't show up in the preview. Backticks are annoying on ios, I have a shortcut. You can also just use four leading spaces, on each line following a blank line.

    ccc

    And if you put the word python directly after the first three backticks, you get language specific syntax highlighting as in:

    maxchar = 128
    charset = range(maxchar)
    for n in charset:
        try:
            print "{0:2s} => {1:2s}".format(str(n), chr(n))
        except TypeError:
            print 'type error with n={}'.format(n)
    
    ccc

    Python version 2.7.5 --> 2.7.9?

    >>> sys.version
    '2.7.5 (default, Dec  3 2014, 19:04:52) \n[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.54)]'
    

    Python 2.7.9 is scheduled to be released this Wednesday with some nice networking security fixes backported from Python3. It will also bring ensurepip to Python2.

    I know it is a lot of work but is there any chance of an upgrade of the underlying Python interpreter in the next Pythonisa release?

    EDIT: Python 2.7.9 is now officially released

    misha_turnbull

    @JonB, @ccc
    Working now. Many thanks! I'll make a note to myself about the markdown formatting too. :)

    techteej

    @JonB, @ccc Any thoughts on my error?

    JonB

    Tech, perhaps create a new thread, since the problem is likely not related to 1.6.
    Maybe include the traceback, so we know what error you got. Also, be sure to include enough info, like the troubleshooting steps you took. Like what did you see when you printed all of the expressions individually on that line.

    bee18

    Not about technical things, but I believe omz should provide demo video of Pythonista both on the site and in the app store. The video should be able to present the ability of Pythonista much better than just static images, especially the advance features. Omz can take a look at Codea's demo video, it's quite good. And I believe a good demo video would also be able to attract new purchasers.

    Thank you.

    techteej

    I'm also getting the disappearing script bug found on here a lot more in the beta.

    userista

    I too lost a script mysteriously. The file is still there - it's just empty.

    ccc

    Did you guys see disappearing scripts with or without external keyboards?

    techteej

    Both with and without.

    zencuke

    @techteej About your bug. You really didn't provide enough info. The error message itself should have been the minimum. plus anything you figured out while debugging. "I got some random error in this big piece of code" isn't enough to get most people involved. I couldn't even run your code since you didn't include the view file so I just moved on. I assume others did the same. Print out the values of the relevant variables. Simplify the code so it is easier to read from scratch. Save us a minute or two of analysis by commenting on what the line of failing code was trying to do and why you think it should have worked. If you have theories share them. Help us help you.

    TutorialDoctor

    Ole, I saw your post about the new Workflow app, and I went and downloaded it. I see that it has support for Editorial and Pythonista.

    I am supposing you are adding some type of extensions support in this version, I am just wondering if you are now considering adding something a little special for this new (and soon to be very popular) app.

    briarfox

    @TutorialDoctor what is this workflow app you speak of?

    TutorialDoctor

    @briarfox

    Workflow: Powerful Automation Made Simple by DeskConnect

    Link

    It is a way to automate in IOS, like Automator for the mac, or Launch Center Pro. But it is so advanced as to be able to use Pythonista and Editorial to enhance automation.

    You can pass command line arguments from the app to a pythonista script, and use the result elsewhere in the app, or as an extension for other apps.

    These workflows can be saved on the home screen, or can be used from the share sheet as an extension of its own.

    briarfox

    @TutorialDoctor Thank you, Looks very interesting, Just picked it up.

    userista

    Not to make this thread about Workflows.app - but there currently is a bug that when you call a workflow from the action extension which calls a Pythonista script that takes input (args) - Pythonista will open but the script will not run.

    TutorialDoctor

    I have been finding bugs myself, but I expect as much from such an app. That is why I am wondering if this 1.6 update will make the process a little better. The Workflow app is fresh-baby new.

    If Pythonista is getting Bluetooth support, then having Editorial, Pythonista, and Workflow, along with the Switch Controls (if you use it) makes the iPad one of the most productive mobile devices ever, and at this point, I find it more productive than my laptop in some cases.

    Then all we will be waiting for is more power from the hardware side.

    userista

    Does anyone have any example scripts of things that you could do with the new cb BLE module? I have a couple of Light Blue Beans and don't really know where to start.

    briarfox

    @hyshai I started with the examples. Just edit them. I used them to find all the services and characteristics availiable on bt devices. Then I started reading to see what info I could get. See if you can find info on Light Blue Beans in reguards to the offered characteristics and services. I just tinkered and played and have a Raspberry Pi working headless over bt.

    TutorialDoctor

    I have been looking for a way to use an iPod touch as a switch control for the iPad. Anyone know if the BLE module would allow this? I haven't downloaded the beta, because I don't want to overwrite the current version.
    I'd like a software switch control.

    But it would seem the pi could be used to interface the interaction?

    userista

    @briarfox
    Thanks - I'm trying to wrap my head around profiles, services and characteristics and what they each are/do.

    I'm wondering if Pythonista will be able to read serial output https://punchthrough.com/bean/arduino-users-guide/#wireless-serial

    wradcliffe

    @hyshai - Briarfox is correct that you should be able to just hack one of the examples and pickup a "Bean" peripheral and find its services and characteristics. You probably already have the LightBlue App already, so you can use its internal browser to check that your python code is seeing these things. The Bean docs are very unclear on how they expect you to access it and which characteristics to use. What I have learned is that they want you to communicate via one of the provided "Scratch" characteristics and not use the serial characteristic. The Scratch characteristics allow you to easly read and write up to 28 bytes of data. I have written Arduino sketches that do this and verified that the cb module can access these. I would like to get the serial communication method working as well because it allows you to access all the on board sensors without involving the Arduino. The Arduino part of the bean sucks down power rapidly so you can easily write sketches that don't do much but kill you battery within hours.

    That's the good news. The cb module works fine for a single Bean. The bad news is that the cb module does not seem to be designed to support more then one peripheral at a time. I have four beans and have been struggling to write an app that can reliably browse and manage them. The cb callbacks run in multiple threads and I am having difficulty figuring out how to correctly deal with this. The code I currently have is way to ugly to share. You will notice from the docs and samples that did_discover_characteristics, did_write_value, did_update_value to not give you the peripheral that these callback are associated with so you have to match it up yourself and this is tricky.

    There are also some other caveats. There is something going on with "weak references" that you have to be very careful with. The docs tell you to keep a reference to the peripheral object passed to you and this seems to be true of any other objects such as services and characteristics. If you don't keep a reference to these for the duraction of their use, you can see them get garbage collected on you and then you are in trouble.

    I have also found out the hard way that the only reliable way to test a script is to completely restart Pythonista each time. If you don't do this, you can see the same periheral reported during a scan for peripherals with different object addresses. You can have "old" ones and "new" ones that are actually the same peripheral. The code I am currenty working on tries to cache references to all peripherals, services, and characteristics in a tree of dictionaries keyed by the uuid of the object. It should be easy, but I am learning python and don't know how to code it so that it is thread safe yet.

    LawAbidingCactus

    Hi @Omz. Sorry it took so long for me to get back to you, I was somewhat busy for a while.
    Anyway, I found a few possible quirks.

    • [Major] Some scripts are deleted after accessing them, then closing the app. The file remains, but is blank when an attempt is made to access it.
    • [Minor] When typing, a random character is occasionally chosen between spaces, instead of the intended character. This might just be a problem with my keyboard, though.
    LawAbidingCactus

    @TutorialDoctor, I just downloaded Workflow. It's a nice app, but the gallery doesn't display the top row of workflow categories on my iPad Air 2. Anyone else experiencing this problem?

    userista

    @wradcliffe
    Thanks so much for your thorough post - very helpful! I'm going to see what I can come up with using your tips and gotchas.

    wradcliffe

    I had an odd occurrence today in the IDE. I came back to my iPad after leaving it sit for a few hours. Went into Pythonista and found my self browsing in the App bundle section of the environment. It was COOL. When I moved down into Documents folder again it was all over and I can't say how I got into the place. Is there some trick to getting there in the IDE that is know? Now that I know it's possible - I want it!

    UPDATE: I figured it out. Use importfinder to open up the code to a module like "wave" in the editor. Then make Pythonista exit while the editor is open. Start up Pythonista again and you find it browsing the directory where that code exists. It is a long way back from this location using the Back button to get to Documents again.

    LawAbidingCactus

    @wradcliffe, this has always been possible, albeit with the help of a few scripts. I don't think Apple's kernel sandboxing applies to the accessibility of the app bundle. The method in which you were able to access the app bundle is strange, to say the least.

    pacco

    Paramiko module version included in 1.6 beta?

    This is a pretty minor question, but I thought I'd ask: Would someone helping with the 1.6beta mind checking this? (In REPL/Console just enter 'import paramiko;paramiko.__version__' )

    Pythonista 1.5 comes with v1.13.0 of this module. I've been digging into the Paramiko APIs the past few weeks and was just wondering if the included version had been updated.

    Thanks!

    dgelessus

    Just checked, the 1.6 beta has the same version of paramiko as 1.5.

    techteej

    Only a few more days till this beta build expires, a new one coming soon? Still some thoughts of mine:

    • [Bug] When archiving a folder/picking a script to send as an email in the export menu, the email share sheet is never presented.
    • [Bug] Some scripts are deleted after accessing them, then closing the app. The file remains, but is blank when an attempt is made to access it.
    • [Feature Request] Color picker dialog
    • [Feature Request] In form dialog - when a password field is added, can we set the placeholder to be in regular text instead of password (hidden) text?
    • [Feature Request] iOS Calendar Module (similar to reminders module)
    glennholt

    Will 1.6 include functional Xcode export? The current 1.5 template-based approach is broken with the latest Xcode.

    Thanks.

    techteej

    Also what @glennholt said :)

    briarfox

    @omz I'm starting to get a lot of pythonista crashes. It started in the last build and just before the current release, pythonista would not open. After updating to the current build, pythonista was working again. I'm starting to have issues again. When I backspace or paste into pythonista, it crashes back to springboard. It happens very often. I'm using an iPad Air latest iOS. I've cleared ipad memory and rebooted the ipad, neither seem to fix the issue.

    the_buch

    @briarfox @omz I just started getting them too. I tried adding from Gist using the Workflow.app workflow and it crashed. I was at work so didn't have time to fiddle. Trying to open now and it crashes instantly, can't open at all. Reinstalled from test flight with no change. No I haven't tried restarting my device yet. I know.. I'm going to shortly ;)

    Just wanted to let it be known after seeing the above post. Was going to wait until later, you know, after I tried restarting my phone, but I have the time right now so why not? Mine is on iPhone 5, latest 1.6 beta build.

    briarfox

    @the_buch At least it's not just my ipad :)

    pacco

    @dgelessus: tnx! (re: Paramiko ver. in 1.6beta)

    briarfox

    @ole Just updated to the latest 1.6. Just got a crash when I tried to paste ~2 lines. Lost 10 min of work :( Happens with both long press paste and cmd+v

    omz

    @briarfox Do you remember where the text you pasted came from?

    briarfox

    @omz I was text from lower in the script I was working on. Backspace when used to remove new lines occasionaly crashes as well. Ilm not entirely convinced that it is pythonista. I have reset my ipad air without much luck. I'm testing to see if it's possibly the bt keyboard. However, on the crash the current script loses all changes from when it was initialy opened. Seems to be the only way to save progress is to close pythonista after a few minutes work.

    briarfox

    @omz Looks like it's my BT Keyboard, Just switched to an old one and no issues. Sorry for the hasty bug report.

    Scratch that, Same issue with my other keyboard. Does seem to be caused by the BT keyboard. I have not had it happen with used the on screen keyboard.

    dgelessus

    FYI, sliding open the file browser on the left side saves the script as far as I can tell. There's no need to leave the app or open another file.

    briarfox

    @omz I emailed the crash report.

    wradcliffe

    For what it's worth, I have seen Pythonista crash when editing text files that have non native line endings. I have downloaded a lot of code from various projects on Github and via simple copy paste and occasionally run into source code in the editor that looks like it has an extra space on the ends of lines. I think this means that the lines are cr-lf terminated. Usually you can just clean the text up manually by deleting the extra "spaces" at the ends of the lines. However, doing this with some files can cause Pythonista to crash as you backup (delete) one of these characters. The crashes for these files seem more frequent on blank lines (no text other then the single extra "space"). I avoid these crashes these days largely by avoiding copy pasting code into Pythonista from random websites. I wish I had a sample to provide, but could not find one today.

    ccc

    On deleting Carriage Returns causing crashes: http://omz-forums.appspot.com/pythonista/post/5289645013204992 and http://omz-forums.appspot.com/pythonista/post/6431833046646784

    ccc

    The following extra modules in Pythonista are not current:

    | module        | local    | PyPI       |
    | name          | version  | version    |
    | ------------- | -------- | ---------- |
    | bottle        | 0.12.5   | 0.12.8     |
    | cffi          | 0.8.6    | 0.9.2      |
    | Crypto        | 2.6      | 2.6.1      |
    | distutils     | 2.7.4    |            |
    | ecdsa         | 0.11     | 0.13       |
    | html2text     | 2014.4.5 | 2015.2.18  |
    | httplib2      | 0.8      | 0.9        |
    | jedi          | 0.7.0    | 0.8.1      |
    | jinja2        | 2.7      | 2.7.3      |
    | markdown      | 2.2.0    | 2.6.1      |
    | markdown2     | 2.2.1    | 2.3.0      |
    | matplotlib    | 1.3.1    | 1.4.2      |
    | mpmath        | 0.18     | 0.19       |
    | numpy         | 1.8.0    | 1.9.1      |
    | paramiko      | 1.13.0   | 1.15.2     |
    | parsedatetime | 1.3      | 1.4        |
    | pyflakes      | 0.7.3    | 0.8.1      |
    | pygments      | 1.6      | 2.0.2      |
    | pyparsing     | 2.0.1    | 2.0.3      |
    | PyPDF2        | 1.22     | 1.24       |
    | pytz          | 2013b    | 2014.10    |
    | requests      | 2.2.1    | 2.6.0      |
    | simpy         | 3.0.2    | 3.0.6      |
    | six           | 1.6.1    | 1.9.0      |
    | sqlalchemy    | 0.9.7    | 0.9.8      |
    | sympy         | 0.7.4.1  | 0.7.6      |
    | werkzeug      | 0.9.4    | 0.10.1     |
    | xmltodict     | 0.8.7    | 0.9.2      |
    | yaml          | 3.09     | 3.11       |
    | ------------- | -------- | ---------- |
    

    ================

    Also, PIL could be updated via Pillow but this would require major surgery.

    See: https://github.com/cclauss/pythonista-module-versions

    techteej

    @the_buch Can we have a link to that workflow?

    briarfox

    @omz latest beta has fixed my crashing issues. I have not had a crash since updating. Thanks!

    Oscar

    @techteej I can confirm that the Export - Send Email... does not work on iPad. It does work on iPhone though. After "Creating Archive..." the email dialog shows up on iPhone, but it does not on iPad. This is 1.6 (160007).

    wradcliffe

    A possible backward compatibility issue. A number of existing scripts that have been written assume that the default bound/frame of ui.View is something large. It now defaults to something relatively small like 100x100. Net result is that the ui of some existing scripts comes up in a tiny box and unusable. This change in default may be a fix to some other problem so this is just an FYI. I have had to fix several scripts to set a proper frame in the init section of their derived view class.

    It might be a good idea to add a sample that shows best practice for setting sizes of views based on the devices orientation and ui.get_screen_size() and uses flex attributes.

    Also - this is for apps that generate their ui programmatically, not via pui files.

    UPDATE: The 'sheet' presentation option has been changed in 1.6 to allow its frame to be user controlled. The default seems to be 100x100 which is different then what it was in past releases.

    omz

    Email export indeed seems to be broken on iPad. Should be relatively easy to fix.

    @wradcliffe

    A possible backward compatibility issue. A number of existing scripts that have been written assume that the default bound/frame of ui.View is something large. It now defaults to something relatively small like 100x100.

    The default frame/size of ui.View actually hasn't changed, but the 'sheet' presentation mode now uses the current size of a view instead of a fixed (larger) size. This is basically a new feature of iOS 8, and while I could effectively disable this, it can be quite useful. The alternative would be to actually change the default frame of new views (which has always been 100x100 for most view types), but this could break other things...

    techteej

    @omz As for the ui.View thing, putting this in the release notes would help a lot of people, even though it is not a change in the actual app.

    bvwelch

    Having the cb module for BLE is very cool, but I'd like to get the manufacturer specific data while scanning. And the RSSI, and set the scan option to YES. This is for generic advertising BLE peripherals. Also, support for iBeacons would be great -- it is a separate CoreLocation API on IOS.

    wradcliffe

    I second @bvwelch's requests. The support for RSSI and a look at iBeacons is going to be required for many appplications. This allows you to get data without having to connect. The other item I would like is providing the peripheral to all the callbacks. This becomes important as soon as you need to support more then one peripheral. It also becomes a performance issue when you have to somehow match data coming out of a characteristic with the peripheral sending the data. It is tricky to do this on the python side since the callbacks are running in multiple threads.

    Also - the CentralManager supports state preservation and restoration but you have to ask for it and supply a key to the init procedure. This would be extremely useful for writing a real world app that can be terminated and restarted in a variety of ways. Right now you have to do a complete rescan every time you run your script which can take some time.

    userista

    Is it too late to request TouchID support? At the minimum to replace keychain.master_password, but ideally to be able to use within a script.

    omz

    Is it too late to request TouchID support? At the minimum to replace keychain.master_password, but ideally to be able to use within a script.

    Not sure yet, it's definitely on the roadmap, but might not make it into 1.6.

    blmacbeth

    Found a bug that will cause the console to become completely useless.

    I have a program that redirects the sys.stdout stream to a ui.TextField via the following kind of class:

    class OutputField (object):
        def __init__(self, textfield):
            self.out = textfield
    
        def write(self, s):
            self.out.text += s
    

    I set sys.stdout = OutputField(v['textfield1']) and now the console will not do anything; not even autocomplete code. I found a quick fix on my iPhone 6 by simply uninstalling and reinstalling the beta, but I have enough projects on my iPad 3 to make that too impractical at the moment.

    If anyone has an idea to fix this, that would be greatly appreciated. I gave tried sys.stdout = sys.__stdout__, which is supposed to contain the origin system output, but it did nothing.

    This also effects other projects as well; as nothing will print to the console.

    EDIT

    After searching frantically around the web, I finally tried restarting Pythonista (I didn't immediately do this at first because it didn't work the first time I tried on my iPhone). Anyways, I found out that saving a reference to stdout is always a good idea.

    It's late, I hope this edit helps people in the future.

    Saturn031000

    Just wondering, what happens when the beta 'expires'? Does it revert back to 1.5?

    dgelessus

    @blmacbeth, this is due to the unusual way that Pythonista uses to print output to the console. What is stored in sys.__stdout__ is the default Python output stream (a file object pointing to file descriptor 1, which is Unix stdout) but (presumably) because of iOS limitations that goes into nowhere.

    Instead Pythonista has a "secret" module named _outputcapture with a few built-in functions to handle text I/O through the Pythonista console. sys.stdout is also replaced with a StdoutCatcher instance, whose write method encodes the string and shows it using _outputcapture. Because the StdoutCatcher class isn't stored anywhere by default, it is lost when you change sys.stdout and it is hard to restore afterwards.

    The reason why the interactive code completion stops working as well is simple - whenever Pythonista needs a list of possible completions, it creates and runs a function named _pythonista_complete_line, which uses some standard Python module to do the completion and prints all possible results to sys.stdout, where they are caught by Pythonista and displayed in the completion list. (Yes, it is possible to monkey-patch sys.stdout and hack the code completion mechanism.)

    Here's an emergency script that you can run in case the in/out/err streams get lost. It will replace them with objects practically identical to Pythonista's.

    import importcompletion as _importcompletion
    import _outputcapture
    
    if (
        sys.stdin.__class__.__name__ != "StdinCatcher"
        or sys.stdout.__class__.__name__ != "StdoutCatcher"
        or sys.stderr.__class__.__name__ != "StderrCatcher"
    ):
        _outputcapture.CaptureStdout(b"I'm alive.\n")
    
        class StdinCatcher(object):
            def __init__(self):
                self.encoding = "utf8"
    
            def read(self, limit=-1):
                return _outputcapture.ReadStdin(limit)
    
            def readline(self):
                return _outputcapture.ReadStdin()
    
        sys.stdin = StdinCatcher()
    
        _outputcapture.CaptureStdout(b"Rebuilt StdinCatcher and sys.stdin...\n")
    
        class StdoutCatcher(object):
            def __init__(self):
                self.encoding = "utf8"
    
            def flush(self):
                pass
    
            def write(self, s):
                if isinstance(s, str):
                    _outputcapture.CaptureStdout(s)
                elif isinstance(s, unicode):
                    _outputcapture.CaptureStdout(s.encode("utf8"))
    
            def writelines(self, lines):
                for line in lines:
                    self.write(line + "\n")
    
        sys.stdout = StdoutCatcher()
    
        _outputcapture.CaptureStdout(b"Rebuilt StdoutCatcher and sys.stdout...\n")
    
        class StderrCatcher(object):
            def __init__(self):
                self.encoding = "utf8"
    
            def flush(self):
                pass
    
            def write(self, s):
                if isinstance(s, str):
                    _outputcapture.CaptureStderr(s)
                elif isinstance(s, unicode):
                    _outputcapture.CaptureStderr(s.encode("utf8"))
    
            def writelines(self, lines):
                for line in lines:
                    self.write(line + "\n")
    
        sys.stderr = StderrCatcher()
    
        _outputcapture.CaptureStdout(b"Rebuilt StderrCatcher and sys.stderr...\n")
    
    blmacbeth

    @dgelessus Thank you for that excellent explanation. Where did you find all of that out? Is it hidden deep within the documentation, or have you looked through some source code? Just wondering.

    dgelessus

    As far as I know none of this is documented anywhere, but Python has great introspection capabilites and an interactive prompt. With a little brute-forcing I managed to figure out much of the iOS/Python interaction internals.

    The _outputcapture module is in the list of sys.builtin_module_names and the syntax of its functions is (relatively) easy to guess. It is always imported (because it is necessary for console IO), but it is hidden from the autocompletion list. If you want to experiment with it, use import _outputcapture as outputcapture to import it under a name that isn't hidden from autocompletion.

    Pythonista's custom in/out/err stream replacements are normal Python classes/objects, and although there is no source code for them, they can be decompiled using the dis module. Python bytecode is not too hard to read in its decompiled form, so it was relatively easy to translate it back into Python source code.

    Figuring out the interactive autocompletion was a little more challenging. At first I only noticed that when typing a single _ into the console without hitting Return, one of the completions is _pythonista_complete_line. However when trying to use that function it isn't there - it is only created when Pythonista needs a line completion, and once the results are returned it is immediately deleted. It still lists itself in the completion list though, because it exists in the main namespace until it is fully executed once.

    Later I found out how I could get a reference to the actual function object - I (accidentally) shadowed the built-in set type with another function, which caused the line completion function to raise exceptions every time it ran. (This made the interactive prompt completely unusable, so from then on I had to use short Python scripts as a console replacement.) Because the exceptions occurred inside of _pythonista_complete_line, their tracebacks had a reference to the function, which I could dis-assemble to reconstruct the code:

    def pythonista_complete_line():
        import string
        import sys
        from rlcompleter import Completer
        completions = _importcompletion.complete("current input text", "text")
        if completions is not None and len(completions) > 0 and completions[0] != "importcompletion":
            sys.stdout.write(string.join(completions, "|"))
        else:
            completer = Completer()
            completions = []
            exclusions = set(["___omz_complete___(", "_importcompletion", "_outputcapture"])
            for i in range(1000):
                c = completer.complete("current input text")
                if not c: break
                if c not in exclusions: completions.append(c)
            if len(completions) > 0: sys.stdout.write(string.join(completions, "|"))
            else: sys.stdout.write("|")
    
    omz

    Nice detective work there, @dgelessus! :)

    JonB

    Omg, thank you! I had discovered the function, but was never able to figure out the interface, or hoe it got access to the current input text line. Will this work for allowing completions within Cmd loops? Or ... Ok, your current input text Is really a placeholder for some inaccessible string?

    I would LOVE to implement a history system usable within pdb.pm() for example.

    dgelessus

    Right, I forgot to explain the placeholder strings I used. For whatever reason _pythonista_complete_line is constructed with the current input string in it as literals. (Who needs function arguments anyway?) What is "current input text" in my example is the full input line, and "text" is the last word of the input (i. e. "current input text".rsplit(" ", 1)) because importcompletion requires it.

    @JonB, the line completion is only called when in interactive Python mode. Pythonista doesn't attempt any completion during raw_input and such, so there's nowhere to inject your own code into. I don't think there would be any way to get the input before Pythonista executes it, so there isn't much use in hacking the line completion at the moment. In any case you'd need some way to differentiate normal output and completion lists - _pythonista_complete_line uses sys.stdout and there's no way to change that.

    As far as I can tell the input history is separate from line completion, the two last matching history items are always added under the result list no matter what the code completer returns.

    PS: Found something. This code should work as it is with no preparations necessary.

    def write(self, s):
        ##if s == "|":
        inside_pcl = False
        try:
            raise Exception()
        except Exception:
            if sys.exc_info()[2].tb_frame.f_back.f_code.co_name == "_pythonista_complete_line":
                inside_pcl = True
    
        if inside_pcl:
            self.real_write("A suggestion" if s == "|" else (s + "|A suggestion"))
        else:
            self.real_write(s)
    
    write_im = type(sys.stdout.write)(write, sys.stdout, type(sys.stdout))
    if not hasattr(sys.stdout, "real_write"):
        sys.stdout.real_write = sys.stdout.write
    sys.stdout.write = write_im
    
    mteep

    I have recently installed the latest 1.6 beta over an existing 1.5 on both an iPad and an iPhone. On both devices I lost all keychain data (dropbox credentials) during the update, which has never happened before. As described by others, I also once got a file blanked out. It was a file that I just had left open when switching to another app and at some later time Pythonista got unloaded.

    I've mainly been testing the cb module, successfully connecting to a WowWee MiP self-balancing (segway like) toy robot. The robot uses a single characteristic to send commands, and another characteristic with notifications for responses. Though I am new to BLE, everything seems to work. At least when I'm just sending a few commands and printing responses to the console.

    However, when I tried to make a simple driving app using the ui module, I started to get a lot of crashes. I suspect these are due to threading issues, but I'm not proficient enough in Python yet to understand how to avoid them. The robot can drive continously at a given speed, but the command needs to be repeated every 50 ms or the robot will stop as a safety measure. I tried to accomplish this using ui.delay(), which crashed immediately when used by itself. But in combination with ui.in_background it sometimes worked for a while before crashing.

    But I assume the crashes due to threading problems aren't specific to the beta or the cb module, and should be discussed elsewhere. Unless Ole wants to share any special threading considerations that only apply to the cb module.

    ccc

    The MiP sounds like a fun thing to hack with. Have you tried using the yaw, pitch, and roll of your iOS devise to control the MiP? motion.get_attitude() gives you access to these values.

    wradcliffe

    I believe that there are issues with threads but have not been able to pin anything down enough to give omz anything to work with. I spent a great deal of time working on a way of logging that did not interfere with the cb modules natural functionality. I kept having the issue that a crashing problem (crash or weird behavior) would go away when I got the logging going. I can say for sure that each callback is run in a new thread each time and it looks a lot like IOS is using some kind of thread pool. The thread names seem to count up and then start over. I have been able to get dozens of different threads going using several peripherals. What all this means is that any code you write in a callback needs to be thread safe and some elements of the python libraries may not be thread safe in these callbacks. What omz needs is a sample app that clearly shows if one of the callbacks can be running simultaneously in multiple threads. Perhaps your app is just doing something that takes a long time in a callback and somehow gets python to context switch and allow the callback to get re-enterred? I am drawing at straws here. I would be willing to get a WowWee MIP in order to help repo this. This would be a good one to use as the sample app for cb in the future IMHO. Anything with a fairly high data transfer rate would be the best. I have been using a BLE MIDI interface and these tend to generate lots of data.

    JonB

    @dgelessus nice!
    So, I gather the history is handled through some builtin that we cannot access? (Builtins can't be disassembled, right?)
    I was hoping for the ability to add to history programatically, oh well. That leads to a feature request: @omz Add console.history command to get / set history. And/or some way to enable completion on raw_input ( which we could finesse into our own history system :)

    ccc

    Reported elsewhere but repeated here for completeness:

    import photos                     # When you tap 'Cancel' in the image picker...
    x = photos.pick_image()                # x = None which is the correct behavior.
    y = photos.pick_image(include_metadata=True)  # Exception thrown which is a bug.
    
    mteep

    @ccc
    No, I haven't tried using the device orientation to control the MiP. I first need to be able to update its speed continuously. Also, in my experience, virtual joysticks works better to control physical things, mainly because you easily can let go of them. So, like the original app by default, I use two of them.

    @wradcliffe
    I don't get any crashes if I just send the speed to the robot whenever the joystick changes (in the action function). But, unless I constantly move my finger, the robot stutters. And even if I do this, the robot turns slightly counter-clockwise every now and then. (I only look at the joystick y position and always sends straight forward or backward speeds.) This never happens with the original app. So my current theory is that I send data too fast in this case. That is, that the value of the characteristic changes before it has been fully parsed within the robot. This turning also didn't seem to occur during the short periods I was able to drive while limiting the rate using ui.delay().

    So, I don't think the crashing issue is related specifically to the cb module. (But possibly to ui.delay(), since it tends to crash Pythonista also if I try to update the UI in a posted function.) I will try different approaches, but it might take a few days.

    wradcliffe

    @mteep - one big question I have about CoreBluetooth is whether it buffers the data being transferred to and from a characteristic. I believe it does and you can get it to crash be overrunning its buffering. This is just a theory. I have read quite of posts on stackoverflow having to do with people not getting enough speed and what to expect for speed of transfer. Some of this is confusion about Bluetooth LE (Smart) being both for low power and high speed. It is NOT for high speed and it seems difficult to actually make it low power.

    JonB

    Mteep, do you call ui.cancel_delays at the start of the function that was calling ui.delay? That might not do exactly what you want, but it would keep you from calling the cb function simultaneously.

    One pattern for dealing with this issue is that you've got one thread only that talks to the resource, and other threads send messages. You might consider using a threading.Thread, which gets commands from a queue.Queue. Your ui would put values into the queue on touch moved, or whatever your interface is.

    The Thread would manage how frequently it sends cb commands, for instance maybe you send with a 10msec delay if the queue.get returned a value, but if you get Queue.Empty, send the last command then wait for 50 msec. You'd probably want to handle the Queue getting too full, in which case you'd be falling behind.. Like check qsize, and if more than some value, you could read multiple values from the queue, but only send the latest.

    mteep

    I got my MiP driving app to work like the original app once I set up a dedicated thread to send the speed every 50 ms.

    @JonB
    The ui.delay() seems to be more or less the same as creating a threading.Timer, which is a new thread. I had hoped/guessed that it would instead schedule the function to be run in the UI thread, as this is something most UI frameworks I am familiar with supports and often requires. (As a side note, that erroneous approach worked for a longer time when I tried it on a newer (A8X class) device.)

    The turning seems to have been caused by a misreading of the MiP protocol description. You send it a command byte followed by a number of argument bytes that depend on the command. The problem is that the robot often accept commands with fewer arguments, if there is sufficient delay afterwards. So I sent two too short driving commands after each other, and the second one was interpreted the turning argument of the first one.

    So, as long as you observe thread safety and send commands of the correct length, controlling the MiP using the cb module seems entirely deterministic.

    @wradcliffe
    I will see if I can come up with a test of your buffering theory, but in the MiP case, there is really no point in trying to push data faster than the robot physically can act. So throttling on the host side seems reasonable. (A possible exception is if you open up the robot and use the hacker port UART to communicate with some other micro-controller.)

    wradcliffe

    @mteep - can you post your script as an example. I would like to see how you setup the dedicated thread and did the timing and communication between the callback and the thread.

    wradcliffe

    @omz - I found a real bug (probably in 1.5 as well). The animation function curve_bounce_in_out calls two non-existent functions.

    def curve_bounce_in_out(x):
        t = x
        d = 1.0
        if t < (d / 2.0):
            return curve_ease_in_bounce(x * 2) * 0.5
        return curve_ease_out_bounce(x * 2 - d) * 0.5 + 0.5
    

    Should be curve_bounce_in and curve_bounce_out - right?

    0942v8653

    @wradcliffe: confirmed in 1.5.

    mteep

    @wradcliffe
    Sorry about the late reply. The forum was down last time I tried answering. My script is a little too long for this thread, so I have removed almost everything except the sending thread. (I will post the whole thing to GitHub once it's a little more complete.) The MipManager starts the SpeedUpdater thread once it gets the write characteristic in a callback. My UI calls action methods in MipManager (not shown), which either sets the current speed or turn in the SpeedUpdater, or occasionally queues a special command. Currently, I only send other commands when the robot has fallen over in order to attempt to get up (something the original app doesn't do at all), so the simple timing works. Finally, when my UI is closed, I call the shutdown method of SpeedUpdater via the MipManager.

    Note that I'm not sure of the thread safety of this code or of Queue.Queue. I just noticed that the threading support was closely modeled after Java and hoped the memory model would be too.

    
    import cb
    import threading
    import Queue
    
    class SpeedUpdater(threading.Thread):
        def __init__(self, peripheral, characteristic):
            threading.Thread.__init__(self)
            self.peripheral = peripheral
            self.characteristic = characteristic
            self.state_lock = threading.Condition()
            self.keep_alive = True
            self.speed_code = 0
            self.turn_code = 0
            self.queue = Queue.Queue()
    
        def run(self):
            keep_alive = True
            while keep_alive:
                try:
                    msg = self.queue.get_nowait()
                    self.peripheral.write_characteristic_value(self.characteristic, msg, False)
                except Queue.Empty:
                    with self.state_lock:
                        self.state_lock.wait(0.05)
                        speed_code = self.speed_code
                        turn_code = self.turn_code
                    if (speed_code != 0) or (turn_code != 0):
                        msg = chr(0x78)+chr(speed_code) + chr(turn_code)
                        self.peripheral.write_characteristic_value(self.characteristic, msg, False)
                with self.state_lock:
                    keep_alive = self.keep_alive
    
        def set_speed(self, speed_code):
            with self.state_lock:
                self.speed_code = speed_code
    
        def set_turn(self, turn_code):
            with self.state_lock:
                self.turn_code = turn_code
    
        def queue_cmd(self, cmd):
            self.queue.put(cmd)
    
        def shutdown(self):
            with self.state_lock:
                self.keep_alive = False
                self.state_lock.notifyAll()
            self.join()
    
    class MiPManager (object):
        def __init__(self):
            self.state_lock = threading.RLock()
            self.peripheral = None
            self.speed_updater = None
    
        def did_discover_characteristics(self, s, error):
            log('Did discover characteristics...')
            for c in s.characteristics:
                if c.uuid == 'FFE9':
                    with self.state_lock:
                        self.speed_updater = SpeedUpdater(self.peripheral, c)
                    self.speed_updater.start()
    
    wradcliffe

    @mteep - did you determine that the RLock used in the MiPManager object was necessary to prevent crashes?

    mteep

    @wradcliffe
    Not really, I just synchronized everything to be safe. (Absence of a crash doesn't make it safe.) I use the RLock for all potentially concurrently accessed state in MipManager, which is more than I included above. Also, the self.speed_updater state is accessed by my code from two threads (UI and main). I used RLock instead of Lock since it has nice properties that I am well used to.

    reefboy1

    I sent an email. Hope I can get te beta soon

    tdamdouni

    I sent you an email. Can't wait to test the Beta!

    S1029514

    I have just sent my name in, hope the line isn't long!

    ebewo

    I would be highly interested in being a test guinea pig 😃. As a researcher who depends on Python for simulations, Pythonista is a blessing.

    ccc

    Python 2.7.10 final candidate 1 is now out and 2.7.10 is scheduled to be released in 9 days.

    polymerchm

    @OMZ: There is a "glitch" in numpy. I get the following trying to do an import:

    Loading NumPy...
    Traceback (most recent call last):
      File "/var/mobile/Containers/Data/Application/762ACFDE-3824-4115-AF2E-EDE94F8CB4AF/Documents/Examples/Function Plot.py", line 7, in <module>
        import numpy
      File "/private/var/mobile/Containers/Bundle/Application/400D5B78-8EA1-4AD3-AEE4-EE1AE9DBF78E/Pythonista.app/pylib_ext/numpy/__init__.py", line 154, in <module>
        from . import add_newdocs
    ImportError: cannot import name add_newdocs
    
    
    JonB

    @poly -- not sure if this is the same problem, but I've found similar import problems can happen if I cancel a script that imports numpy without letting it complete.

    I think numpy gets partially imported, and entries start getting created in sys.modules, but it gets cancelled and therefore is just totally confused. restarting pythonista can help. I think I also had luck with a script that crawled through sys.modules, and del'd any entries starting with numpy.

    I have imported numpy in the beta without problems -- you just have to make sure the first time is a clean import.

    Moe

    I found a bug with the autocompletion which causes Pythonista to crash.

    Suppose you have a generator:

    def fibonacci_gen(up_to):
        count = 0
        var1 = 0
        var2 = 1
        while count <= up_to:
            yield var1
            count += 1
            var1,var2 = var2,var1+var2
    

    If you now try to write

    gen = fibonacci_gen(10)
    number = gen.next()
    

    Pythonista crashes a few seconds after writing the dot behind the gen. If you write .next() somewhere else and copy it you can paste it in and it works fine.

    polymerchm

    @JonB - as always, you're correct. restart fixed it.

    @Moe - a VERRY unreproducible bug that @OMZ is chasing. It's seems to always involve instance variable or methods, but not all objects and not every time. Best suggestion - mistype the object name intentionally to suppress the autocompletion and go back after the fact and correct.

    However, yours fits the bill of reproducible and pretty small. Even this blows it up:

    def gen(x):
       count = 0
       while count < x:
          yield count
          count += 1
    mygen = gen(10)
    print mygen.next()
    
    JonB

    FYI, just noticed there is a new beta posted, which supposedly fixes this problem.

    polymerchm

    All I see is a beta that expires in 7 days, which I would expect is the current one. @OMZ please clarify?

    zencuke

    Testflight says the current beta expires Saturday May 30, a week from now. I haven't heard of any other updates.

    JonB

    I have 160014 ( I recently got a new device, and had installed test flight from scratch). Maybe I missed when this was updated.
    The following does still crash though:

    def gen():
        yield 1
    x=gen()
    x.
    

    Here are the 160014 release notes. Maybe I just missed this one on my old device.

    Build 160014

    • Experimental Apple Watch support – the Pythonista Watch App allows you to launch scripts in the special "Watch" folder. The scripts still run on the iPhone (usually in the background) – the Watch basically just acts a remote control. Console output appears on the watch, including images, but things like console.set_font() or console.clear() are not supported. The only way to get user input is currently to call raw_input(). This brings up the dictation interface on the Watch with an optional list of suggestions to pick from. To set the list of text input suggestions, call appex.watch.set_input_suggestions() before raw_input. I'm not sure yet how useful this actually is, and if I'll include it in the final release.

    • Added a Done button in the console output panel of the app extension

    • Cmd-Left/Right shortcuts (external keyboard) should work properly now

    • Background audio (experimental) – Note: This has the side effect of the mute switch being ignored when playing audio (like in pretty much all music/video player apps). You can also abuse this to make your script run in the background indefinitely by looping a silent track (can be useful for servers and the like).

    • New is_authorized() functions in the location, contacts, reminders and photos modules

    • New did_pause and did_resume methods that can be overridden in sk.Scene subclasses to handle interruptions (e.g. when the app is backgrounded)

    • New console.is_in_background() function to determine whether Pythonista is currently the foreground app or not

    • Fixed crash when importing requests in the app extension

    • Fixed crash when initializing sound.MIDIPlayer without an explicit soundbank path (the "Random MIDI Melody" example shouldn't crash anymore)

    • Fixed crashes related to code completion (somewhat hard to reproduce, so I'm not 100% sure)

    • Fixed ui.View.bounds (and a couple of other attributes) returning a Rect, but accepting only a tuple (any 4-number sequence should work now)

    • Geometry types in the sk and ui modules (sk.Rect, sk.Point...) support slicing now (slices are always tuples)

    • Reverted to an older version of requests for now to get rid of "InsecurePlatform" warnings that are logged to the console. I'm well aware that this isn't an actual solution, but a real fix turns out to be non-trivial to implement (the cleanest approach would be to upgrade to Python 2.7.9, but this is quite a lot of work and I'm not sure if I want to get into that for 1.6)

    JonB

    Btw:omz, how is the background audio enabled? Is this just automatically done in the sound.Player class?

    ccc

    Python 2.7.10 is now out... https://hg.python.org/cpython/raw-file/15c95b7d81dc/Misc/NEWS

    techteej

    @JonB This is an older beta.

    ccc

    There are only 48 hours left on the current beta. Tick tock.

    ccc
    Extra modules in the current Pythonista Beta that are not up-to-date.
    
    | module        | local    | PyPI       |
    | name          | version  | version    |
    | ------------- | -------- | ---------- |
    | bottle        | 0.12.5   | 0.12.8     | Upgrade possible
    | Crypto        | 2.6      | 2.6.1      | Upgrade possible
    | ecdsa         | 0.11     | 0.13       | Upgrade possible
    | feedparser    | 5.1.3    | 5.2.0      | Upgrade possible
    | html2text     | 2014.4.5 | 2015.4.14  | Upgrade possible
    | html5lib      | 0.999    | 0.99999    | Upgrade possible
    | httplib2      | 0.8      | 0.9.1      | Upgrade possible
    | jinja2        | 2.7      | 2.7.3      | Upgrade possible
    | markdown      | 2.2.0    | 2.6.2      | Upgrade possible
    | markdown2     | 2.2.1    | 2.3.0      | Upgrade possible
    | matplotlib    | 1.3.1    | 1.4.3      | Upgrade possible
    | mpmath        | 0.18     | 0.19       | Upgrade possible
    | numpy         | 1.8.0    | 1.9.2      | Upgrade possible
    | paramiko      | 1.13.0   | 1.15.2     | Upgrade possible
    | parsedatetime | 1.3      | 1.4        | Upgrade possible
    | PIL           | ?????    | 1.1.6      | Pillow upgrade possible
    | pycparser     | 2.10     | 2.13       | Upgrade possible
    | pyflakes      | 0.7.3    | 0.8.1      | Upgrade possible
    | pygments      | 1.6      | 2.0.2      | Upgrade possible
    | pyparsing     | 2.0.1    | 2.0.3      | Upgrade possible
    | PyPDF2        | 1.22     | 1.24       | Upgrade possible
    | pytz          | 2013b    | 2015.4     | Upgrade possible
    | requests      | 2.6.0    | 2.7.0      | Upgrade possible
    | simpy         | 3.0.2    | 3.0.7      | Upgrade possible
    | six           | 1.6.1    | 1.9.0      | Upgrade possible
    | sqlalchemy    | 0.9.7    | 1.0.4      | Upgrade possible
    | sympy         | 0.7.4.1  | 0.7.6      | Upgrade possible
    | werkzeug      | 0.9.4    | 0.10.4     | Upgrade possible
    | xmltodict     | 0.8.7    | 0.9.2      | Upgrade possible
    | yaml          | 3.09     | 3.11       | Upgrade possible
    | ------------- | -------- | ---------- |
    

    ================

    polymerchm

    New beta just arrived.

    dgelessus

    Major bug when updating: whichever script was open in the editor before the update is cleared and becomes an empty file, probably because the release notes are automatically opened.

    Before updating, close all scripts and open only an empty tab!

    ccc

    Confirmed. Fortunately I had just checked my file into GitHub or all of my changes would have been lost.

    Also, in this upgrade, the requests module was downgraded (v2.6.0 --> v2.5.1) so that it no longer throws warnings.

    JonB

    posted this to another thread, but in case omz doesn't follow all:

    ui.SegmentedControl raises a SystemError when accessing subviews, and crashes the app when using add_subview.

    MartinPacker

    Just got my watch and playing with it and Pythonista 1.6.

    Any update on things you can do with appex.watch?

    Any more examples?

    omz

    @MartinPacker To be honest, I probably won't ship the watch app with 1.6. I don't think it works well enough, and I can't see a lot of compelling use cases. I'll probably revisit this at some point though.

    MartinPacker

    So I just tried a simple one on the watch:

    name=raw_input("What is your name?\n")

    print "Hello"+name+"\n"

    The prompt seems to appear AFTER the input is dictated.

    I'm on the latest beta.

    Is it me or the beta?

    Gerzer

    @MartinPacker: Here is a very simple appex example script to import a file from a URL in, say, Safari. This one copies files already stored in other apps, like the Mail app. I personally don't have an Apple Watch, so I can't give you any examples on that front. It seems, though, from the documentation of the Apple Watch interface, it just executes scripts and provides text to the raw_input() function, and nothing more. I might be wrong, but that is all I can gather.

    MartinPacker

    Thanks @Gerzer.

    Actually the Roll Dice python script uses ui but doesn't take input - and works nicely on the watch. If I do more research on that it might yield something.

    One difficulty is actually getting scripts off iPad onto iPhone in "Watch" folder. Anyone know how to do it.

    The quid pro quo is I'm motivated to work on getting the Watch functionality known - for when 1.6 is shipped. If that's a post (on my blog) or examples that'd be fine.

    I should work with @ccc to get something working from the Watch that talks to an IBM mainframe... :-)

    (Google me if you think that last is odd.) :-)

    MartinPacker

    Ah I see @omz's post. I would think if the ui route, rather than raw_input(), works we'd have a winner.

    But obviously your prerogative.

    omz

    @MartinPacker

    The prompt seems to appear AFTER the input is dictated.

    The way the current watch "app" beta works is very simple: It can send a request to the iPhone app to start a script, and then it starts polling for events every few seconds (as long as the app is visible on the watch). An event can be text/image console output, or a text input request (raw_input). The only way to get text input on the watch is to present a full-screen dictation view (which can include "quick reply" options that are set via appex.watch). If you call raw_input directly after printing, it's quite likely that the two events arrive at the same time on the watch because of the polling interval, so the text input view covers the screen before you see the print output...

    Obviously, this is pretty inefficient, but I haven't found a better way to handle input/output with the current WatchKit SDK. I'm fairly sure that a better app will be possible when watchOS 2 comes out, but I haven't really looked into it yet.

    Webmaster4o

    Any estimation for when pythonista 1.6 will be public? Haven't been able to join the beta, and I really wanna use it! A month? Three? A year?

    MartinPacker

    Thanks @omz. I can see the current infrastructure makes it tough to make a decent watch-originated app.

    I'm wondering if I can use Workflow to front-end an "on the phone" app with a half decent UI.

    As it happens I was just playing; I don't have a real application just yet.

    bslayton

    @webmaster4o same here. I've been trying to be as patient as possible. Hopefully it's soon!

    userista

    Bug: in Editor Actions only the last script shows in the share sheet. Also reported by @GCarver http://omz-forums.appspot.com/pythonista/post/6187778610036736#post-5310158079000576

    JonB

    hyshai, I was about to respond that I didn't have this problem.... until I clicked more, and reordered the scripts.... then all but one disappeared. so the symptom appears to be caused once the more menu is entered...

    userista

    @JonB
    Oops! Sorry :(

    dgelessus

    The unpredictable script clearing bug has returned for me recently and has happened multiple times. The few common circumstances that I could observe were the following:

    • I was using an external keyboard
    • The files were empty when I reopened the app after it was terminated (either automatically or manually by swiping it out of the app switcher)
    • The affected files were the currently active editor tab
    • More than one tab was open (not entirely sure about this one)
    • None of the files were Python scripts (two Markdown files and one plain text file)

    One time this even happened twice in a row. I had about 5 files open, one of them got cleared. I closed that tab, restarted the app, and the next file was cleared as well.

    Can't say if this is related to the bug that emptied files when updating to a new beta version. That still happened with the last update, but that was a few weeks ago. I've got a feeling the beta might be expiring again soon. *hint hint*

    omz

    @dgelessus

    I think I know why this happens now, and it should be fixed in the next build (later this week). Basically, the editor sometimes auto-saves a file even though it hasn't finished loading yet, resulting in an empty file.

    In the case of the beta release notes, the app first restores the previous tabs, but then immediately opens the release notes file in the selected tab, before the previous file has actually been loaded completely. Opening a different file (the release notes) in the same tab triggers an auto-save, so the previous file is overwritten with an empty file...

    In this case, it's trivial to reproduce, but in other situations, the bug appears to be more "random". It could for example happen if the app goes into the background (triggering an auto-save) while a file is being loaded etc. The fix is pretty simple – just don't save anything before a file is loaded (duh!).

    It's possible that there are other reasons for this behavior, but I'm relatively confident that this is it.

    JonB

    not sure if this is a bug or feature...

    when creating and presenting a ui.View, the default bg color is (0,0,0,1), or black.

    however when presenting as panel, the bg color shows up as white, even though the attribute is unchanged.

    setting the bg color manually does correct this.

    I want to say that in 1.5, the default color was white, but I could be wrong.

    Webmaster4o

    I can confirm that in 1.5, default background_color is (0.0, 0.0, 0.0, 1.0). view.present shows black, but view.present("panel") shows up white. The same bug exists in 1.5.

    omz

    @JonB @Webmaster4o

    The value that you get from the background_color attribute is actually incorrect, it should be either (0.0, 0.0, 0.0, 0.0) or None, i.e. the default color is transparent, which has different effects depending on the presentation style.

    Webmaster4o

    Oh! So there is a bug, but not the one we thought :P

    Tizzy

    What's the word on iOS 9 beta... are there any known 1.6 issues with it? Apple beta software program just sent out notifications and I want to jump in.

    Update:

    Just went for it and there appears to be a problem with the speech module - specifically speech.get_languages() now returns an empty array rather than the lost of languages and corresponding codes.

    https://flic.kr/p/vwhLpC

    PS anybody have any scripts to use as action extensions to get the direct URL of a Flickr photo?

    alt text

    ccc

    The current Beta expires in 21 hours...

    JonB

    just click the download button, choose the size, then copy the url

    Webmaster4o

    In the newest beta, pythonista becomes completely unresponsive if dialogs.share_image is called while the keyboard is showing. In my UI, the user puts text into a box, then presses a button that calls dialogs.share_image. If the user fails to close the keyboard before pressing the button, pythonista doesn't crash, it just freezes. If the keyboard is hidden when the button is pressed, everything works perfectly.

    Perfect:

    2

    Broken:

    1
    When the keyboard is open, the dialog fails to show, and the app must be quit from multitasking as it is completely unresponsive.

    Webmaster4o

    For now, is there a way to close the keyboard from a script?

    ccc

    Calling .end_editing() on your text field should do the trick.

    http://omz-forums.appspot.com/pythonista/post/5050025600090112

    Webmaster4o

    Yep. Thanks.

    Webmaster4o

    Possible bug: scripts run from the app extension can't see modules in site-packages. If I run my script, it works, but if I run it from the app extension:

    Is this a bug or am I missing something? Of course, most app extensions that use appex don't work anyway in this beta, because of from _appex import watch :)

    JonB

    the appex has its own sandbox... the main app can see the appex sandbox, but not vice versa. I think this is a security feature. bottom line, you have to reinstall all of the modules you need into extension/site-packages folders.

    if you want to do this using stash, you can find the Extensions folder by opening a file, then using editor.get_path. otherwise just copy the folder using the file manager.

    Webmaster4o

    My file manager doesn't seem to have a way to move a folder into extensions. Also, pythonista crashes if I try to create a file in a sub folder of extensions.

    Webmaster4o

    Ok, this worked for copying the folder, I got path like you reccommended, @JonB

    JonB

    As a workaround for the import appex bug:

    import _appex
    _appex.watch = None
    import appex
    
    Webmaster4o

    Oh, wow! Thanks!

    Tizzy

    Build 160025 bug report.

    I have a folder structure /AAAdriver/ inside of which I have DriverInfo.py and TripDataA.py.

    previously, from within DriverInfo.py,

    "import TripDataA"
    

    worked just fine. With the latest beta, I'm getting "ImportError: No module named TripDataA"

    dgelessus

    From someone who indents with spaces - there are a few things that don't work as well with spaces indentation as with tabs:

    • Backspace on a space indent only deletes a single space, as opposed to {indentwidth} spaces like in 1.5
    • The "indent" option in the selection popup menu always indents with tabs no matter what
    • The "dedent" option always dedents by only a single space
    • The width of a tab character in the editor doesn't match the width of {indentwidth} spaces. Or rather it only matches if the default font settings are used.

    Slightly related, it is still not possible to indent in the console using an external keyboard's tab key.

    It would also be useful if there was a way to set indent mode for individual files. Not necessarily in a permanent way. For example most C source files assume that tabs are 8 chars wide, and some things look very "unaligned" otherwise.

    georg.viehoever

    I find the mechanism for opening multiple existing files a bit confusing: In editor, select [+. Then open file selection (hamburger menu), then select file. I would like to see a more straight forward way to do this from file selection directly, for instance hold file name for a longer time, show context menu allowing to "open in new tab" (similar to link in safari).
    Georg

    omz

    @georg-viehoever You can swipe left on a file to get an "Open in new tab" option.

    georg.viehoever

    @omz that was quick :) Frankly, I always associated left swipe with delete (as in the mail app), not with open... Maybe "long hold" is more intuitive, and could also be used for other advanced actions such as rename etc..

    Another wish: Copy and Paste on tracebacks. Would also be useful for some people in this forum that post long programs for correction...

    Georg

    Webmaster4o

    @roosterboy197 said:

    Any chance of getting lxml in Pythonista 1.6 (and the next version of Editorial too, while I'm at it)? I would really, really like to use openpyxl (or python-xlsx) and python-docx to read/edit Excel and Word docs in my Dropbox but they have lxml as a dependency and, as a C-based library, it's not user-installable.

    I'd love to see this. Tried to play with the roku module today, but it requires lxml. One of the files tries to import flask, but that doesn't seem to be an essential part of the module, so I think lxml is the only thing preventing it from working.

    sanit

    Actually, I can wait for releasing Pythonista 1.6 but It has been so long. Could I play with the next version of Pythonista? I try to run script from the forum but some of them only support 1.6. I will send my Apple ID via email.
    Thank you,
    Sanit

    Webmaster4o

    There's also an online beta signup form floating around somewhere.

    daveman

    @omz can you please send me an invite to try the beta please I also sent you a email :)

    daveman

    @omz is there any more beta slots left if so can I be a tester ?

    omz

    @daveman Check your email. ;)

    jai_b

    Hi OMZ, can I also be added to the the 1.6 Beta please?

    Thanks in advance,
    Jai

    Phuket2

    The keyboard tweak to the latest beta is great for ios9. It was driving me crazy.

    daveman

    @omz hey could you send the invite to my other email d.radmore23@gmail.com please :)

    daveman

    @omz ok got the beta installed thanks :)

    jai_b

    @omz is it too late to be added to the beta test?

    techteej

    It would definitely make it easier on @omz if you use his form to sign up for the beta

    jai_b

    @techteej Oh, thanks. I will use it.

    tasuku

    Hi, I am very interested in try 1.6 beta.
    Especially I would like to access BLE using Pythinista.
    If slots are available yet, I will send you a email.
    Thanks.
    Tasuku

    Webmaster4o

    @tasuku please use his form to sign up for the beta.

    Olaf

    @omz, 160036 will expire in 3 days according to Test Flight. Will there be a 160037 by then?

    Webmaster4o

    @Olaf I contacted him on Twitter about this

    OI

    I just signed for Beta 1.6, I hope there are still seats available, thanks

    aanchal

    LEO PRIVACY GUARD 3.0fingerprint scanner is an app cover fooling others who don't know the trick to open it. Hope you enjoy!
    Steps: AppLock

    donnieh

    aanchal was banned ^ He must be a bad boy.

    Tizzy

    Just wanted to report that xCode 7.2 and iOS 9.2 working with the existing template and beta. (Maybe we should have a dedicated upgrades thread)

    Tizzy

    [insert bad joke]

    Phuket2

    @Tizzy , he had some weird posts. Not sure if he was trying to advertise or what. Was strange. But I reported the posts, as I am sure others did also.

    ste_c

    I know this thread has been knocking around a bit now but I'd be interested in trying out the features if slots are still available?

    dgelessus

    @omz said in another thread that he has submitted the Pythonista 2.0 update, once it's through Apple's review process you can play with all the new features without needing beta access. Of course if you want to test future Pythonista versions, you can sign up for the beta through the form that has been linked to very often in this thread. ;)

    DBGE

    @omz First of all, many thanks for all the good and useful work you have done. As I have an app with Bluetooth LE, I'll be happy to test the newest beta release.

    My AppleID is

    daniel.bertrand@hiqscreen.com

    Looking forward to hear from you.

    Webmaster4o

    @DBGE the app has already been submitted to the App Store, I am very doubtful that he is accepting new beta members. The app will (hopefully) be public by the end of the year

    omz

    Quick status update:

    I've submitted Pythonista 2.0 (formerly known as 1.6 beta) to the App Store about 10 days ago.

    It was rejected after a week because I had accidentally declared external-accessory as a background mode in the Info.plist. I had added that for testing something while working on the cb (CoreBluetooth) module a while back (forgot to remove it), but it's actually only intended for apps that support licensed MFi (Made for iPhone) accessories, using the ExternalAccessory framework...

    Anyway, I've removed that, and re-submitted immediately, so it's been back in the review queue for 3 days now. I'm hoping that it'll be reviewed again before the holidays.

    donnieh

    Thanks for the update. For people using the 1.5, should we expect to have all our files safe after the update? Of course I will back up just in case.

    donnieh

    It is amazing how much Apple scrubs submitted apps. But it is good for customers who want only solid apps in the app store (unlike android).

    Phuket2

    @omz , once accepted, holiday time 😈😈😈

    omz

    Just got the second rejection, unfortunately.

    This time it's about the audio and Core Bluetooth background modes. I'm not sure yet if I'll try to appeal, or if I'll just re-submit without those background modes.

    I'm almost certain that I'll have a hard time convincing the review team that background audio playback is essential for a programming app (and there's precedence for that mode getting abused, which, let's be honest, was the entire point of adding it to Pythonista), and Bluetooth communication in the background would be nice to have, but isn't that important in my opinion.

    When I added background audio playback, I already had a feeling that it could cause trouble in app review... It was worth a shot, but at this point, I just want this update out there, to be honest.

    ccc

    Is it possible to remove these two capabilities, resubmit the app, get it approved, and then afterwards start an appeal on the Bluetooth issue?

    Tizzy

    @omz. I'm sorry to hear that.

    On the one hand, I agree just get it shipped before the holidays.

    On the other, I think it's perfectly legitimate for a python development app to use background audio. What if somebody wants to make a music player? What if you're a scientist using a bluetooth connected sensor and analyzing data using pythonista?

    Maybe submit it without those modes, but then immediately submit anther version with the modes, and appeal that rejection? Maybe your rejection is completely automated and nobody actually has an issue with it?

    In any case, if there's anything we can do to voice our support of Pythonista, please let us know. Who should we email? (UPDATE: Phil schiller (@pschiller) is now in charge of app stores!!!)
    Also, you could presumably still include those modes in the templates for people who want to move to the next step in which case they would deal with those rejections on an app by app basis.

    Tizzy

    Also, you're genuinely benefitting the iOS platform. You're bringing developers into the iOS fold, helping get people to start working with apple's platform in some capacity, some of which might go on to be swift developers etc. So there's that.

    Phuket2

    @omz , I think fb really brought the background audio to the forefront recently. I think if it wasn't for that, maybe would not have been a problem.

    liberty

    Background audio and background Bluetooth are both valuable to have in specific use cases. But they can lead to battery life problems easily with programming mistakes. Would it be possible to provide per file activation? That would at least show that people are actively seeking this capability and are not going to blame Apple for any extra power drain.

    Personally I would find background Bluetooth quite useful to log some data over longer periods.

    Webmaster4o

    @omz If there's any way the community can help to voice support for this app, I'd be happy to help. Hope it gets through review soon. I don't have a huge problem with background audio/bluetooth being removed. What rule do they violate, though?

    In my opinion, Workflow bends some of the rules, but they seem to be on apple's "good side", getting awarded Most Innovative and all...

    omz

    I've decided to re-submit without background audio/bt for now. I think the chances of getting background audio through the review board are very slim, and it's probably not worth the trouble (and additional delay). As it is, the chance of getting another review before the iTunes Connect holiday break (Dec 22nd-29th) are not good.

    donnieh

    Good things come to those who wait.

    Phuket2

    @donnieh , LOL. if you build it they will come

    dmkanter

    Very excited! Fingers crossed!

    donnieh

    Any new update on the release?

    omz

    The status has changed to "in Review" again yesterday.

    athros

    Here's to hoping!

    Webmaster4o

    And it's up!

    donnieh

    Christmas in January

    chaeron

    Any way to keep Pythonista running in the background? I find that a long running logging task (using the paho-mqtt client), dies after a while, when my iPad autolocks.

    Also seem to have some reconnection issues with mqtt to my iPad running a Pythonista script that monitors log entries being sent by my thermostat over mqtt. Don't get those issues with my linux machines or on my Raspberry Pi's. Keep getting "Connection reset by peer" errors in Pythonista for some reason.

    Thx!

    Webmaster4o

    I honestly would not try to use Pythonista for long background processes if you could use a desktop computer instead. I run this sort of thing on a dedicated raspberry pi in my basement

    ccc

    You might try the ideas in https://forum.omz-software.com/topic/947/keeping-the-display-on but I do not know if they will help. You could also run something free on Bluemix, Python Anywhere, etc.

    chaeron

    I don't plan to run long running tasks on Pythonista. Just noticed that it died after autolock while I was running some tests using mqtt. The actual log processing will run on a dedicated Pi.

    Thanks for the link ccc.....I've added the console.set_idle_timer_disabled() call and will see if that helps.

    Any way to put a custom icon on your iDevice home bar or a page to automagically start a Pythonista script? Be nice to be able to write cool scripts and be able to initiate them just like a native IOS app.

    Thx!

    JonB

    With the pythonista URL scheme, you can launch pythonista and run a script.

    Phuket2

    @JonB , I hope not talking taboo, if I am let me know and I will delete the post.
    I haven't written anything yet using appex. But I have been excited by the idea that if I have a little utility app (well any app), anywhere I can use a share sheet I could run that app. Wether or not it relates to the app it's called in or not. Is there a flaw in that thinking?

    Edit
    well I just tried it. Simple.

    JonB

    Yes appex works as well. I meant that I think you could have a home screen icon which runs a pythonista script see for example:
    http://omz-software.com/pythonista/shortcut/

    Phuket2

    @JonB , ok great.
    Edit
    I just noticed that is supported with devices supporting 3D Touch. I didn't really notice before as not using my iphone much