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.
[HELP] Update View During Button Action
-
def changeBackground(sender): z=sender.superview x=1 while x<=10: button='button'+str(x) z[button].background_color=(0,0,0) x+=1 time.sleep(1)
In my mind, this code (assuming that this gets called by pressing a button) should change the background color of 'button1', 'button2', and so on in order. Changing one every second.
However, if this code was ran the program would wait for 10 seconds and then update all of the buttons' background colors at once. How can I get it to update the UI at the time when that line of code is ran? I have tried
z[button].set_needs_display()
to no avail.Thanks!
-
Use the
@ui.in_background
decorator so it doesn't run in the main UI thread (in your code,time.sleep
blocks the entire UI from updating for ten seconds). -
The
ui
module has some odd threading habits. In particular all functions called by the UI (button actions, delegate methods, etc.) are blocking by default, which means that the entire UI will not update until the function returns. As a workaround you can use theui.in_background
function decorator, which will cause the function to run in a different thread than the one that does UI updates. Decorators are used like this:@ui.in_background def changeBackground(sender): ....
A short (unrelated) suggestion regarding your code - Python provides a very easy way to iterate over containers using a
for
loop, without the need for an integer iteration counter. The function you posted could also be written like this:@ui.in_background def changeBackground(sender): for button in sender.superview: button.backgound_color = (0, 0, 0) time.sleep(1)
In cases where you need to iterate over a range of numbers, use the
range
function:for i in range(10): # creates an iterable object for the numbers from 0 (inclusive) to 10 (exclusive) print i
Or if you need to iterate over a container, but also need to know each element's index, there's
enumerate
:for i, word in ["These", "are", "words."]: print "Word", i, "=", word
-
In Python 2, you should almost always choose to use
xrange()
instead ofrange()
for performance and memory management reasons...import sys print(sys.getsizeof(range(10))) # 72 print(sys.getsizeof(xrange(10))) # 20 print(sys.getsizeof(range(1000000))) # 4000032 that is 4MB of RAM instead of 20 bytes print(sys.getsizeof(xrange(1000000))) # 20
-
Thank you all for the help! I once thought I was fairly fluent in Python, however I now realize how little I know.
EDIT: Another quick thing. Let's say I make a game on Pythonista, is it possible to make some sort of "savegame" file? That way You could save leaderboards and/or progress?
-
I'm no expert on this (still learning python myself), but there are countless different ways to save data, be it a database, JSON, shelve, and others. Which one is best depends on your application. Saving a leader board is simple enough that you could do it with with a simple shelve style database. Simply storing the last level a person was on would also be simple. If you need to save exactly what's going on in a game that has lots of independent characters that all have logic and history associated with them you'll probably want something more sophisticated.
-
@TQA, check our the
high-scores
module in theGames
section ofPythonista-Tools
. It does a lot of what you are looking for. -
Personally I prefer JSON for persistent data storage with Python. The types it has are very similar to Python's basic data types, so if you can represent all your data in some combination of
dict
s,list
s,str
s,float
s,int
s andbool
s you can easily dump and load that data using thejson
module.A database is probably more complex than necessary for something as simple as a score leaderboard, but if you need to store large data sets a database may be a better choice than JSON.
If you need to store the state of multiple Python objects, then
pickle
orshelve
are probably best. I'm not sure what exactly the limits of those modules are, certain objects can or cannot be pickled/shelved depending on how they are laid out. -
@dgelessus, I completely agree with your recommendation of
json
as the preferred method of storing Python objects into files. It is also super helpful that modules likeRequests
have built-in support for json and that the resulting files are very human-readable and human-editable.When @techteej was working on the high scores module that I mentioned above, he made the design choice to use pickle instead of json because it was too easy to cheat by opening up high_scores.json in the Pythonista Editor and increase your high score. The use of pickle (slightly) increases the difficulty of cheating in this way.
-
I just want to save a single integer along with the date/time. I feel pretty stupid. I have no idea how this works as I have never done anything with reading/writing files before. How do I create a file to be written to?
-
with open('myfile.txt','w') as f: f.write('something')
-
import datetime, json, random filename = 'my_file.json' data_a = [random.randint(1, 100), unicode(datetime.datetime.now())] print(data_a) with open(filename, 'w') as out_file: json.dump(data_a, out_file) with open(filename) as in_file: data_z = json.load(in_file) print(data_z) assert data_a == data_z, 'Something went wrong: {} != {}'.format(data_a, data_z)
-
I seem to have run into a problem..
I write
import os import os.path with open('save.txt','w') as f: f.write('hello') with open('save.txt','r') as f: f.read()
Nothing happens. I don't get any errors, but the console output window doesn't even open.
-
Try changing the last line to:
print(f.read())
. -
The issue you're facing is likely due to the fact that the code you described is running on the main thread, which is also responsible for updating the UI. When you use a time delay, as in your case, it blocks the main thread, preventing the UI updates from occurring until the delay is complete. To achieve the desired behavior of changing the background colors of buttons one at a time with a one-second delay in between, you should use a separate thread or queue to handle the time delays. In iOS, you can use Grand Central Dispatch (GCD) or an asynchronous task to perform the background color changes without blocking the main thread. Here's a simplified example in Swift:
DispatchQueue.global().async { for button in buttonsArray { usleep(1000000) // Sleep for 1 second (in microseconds) DispatchQueue.main.async { button.backgroundColor = // New background color } } }
This code runs the background color change on a separate thread, ensuring that the UI updates happen one at a time with a one-second delay in between, without freezing the main thread. The same concept can be adapted to Pythonista or your specific programming environment.