Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Pythonista 1.6 Beta
-
@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 :) -
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.
-
@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.
-
@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.
-
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
, whichget
s commands from aqueue.Queue
. Your ui wouldput
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.
-
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.) -
@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.
-
@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?
-
@wradcliffe: confirmed in 1.5.
-
@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()
-
@mteep - did you determine that the RLock used in the MiPManager object was necessary to prevent crashes?
-
@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. -
I sent an email. Hope I can get te beta soon
-
I sent you an email. Can't wait to test the Beta!
-
I have just sent my name in, hope the line isn't long!
-
I would be highly interested in being a test guinea pig 😃. As a researcher who depends on Python for simulations, Pythonista is a blessing.
-
Python 2.7.10 final candidate 1
is now out and2.7.10 is scheduled
to be released in 9 days. -
@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
-
@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.
-
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.