Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ThreadedCell
# coding: utf-8
import threading
import ui
import random, time
from random import randint
pyui_data = ''' [
{
"selected" : false,
"frame" : "{{0, 0}, {270, 288}}",
"class" : "View",
"nodes" : [
{
"selected" : false,
"frame" : "{{0, 0}, {270, 42}}",
"class" : "Label",
"nodes" : [
],
"attributes" : {
"font_size" : 22,
"frame" : "{{60, 128}, {150, 32}}",
"uuid" : "46C39608-B1DD-4CCC-8817-A72CB062E4F0",
"text" : "test",
"alignment" : "center",
"class" : "Label",
"name" : "lb",
"font_name" : "<System-Bold>"
}
},
{
"selected" : false,
"frame" : "{{0, 40}, {135, 135}}",
"class" : "ImageView",
"nodes" : [
],
"attributes" : {
"frame" : "{{85, 94}, {100, 100}}",
"class" : "ImageView",
"name" : "img1",
"uuid" : "34C10386-4F29-421F-A4C2-14C6219FB3F9"
}
},
{
"selected" : false,
"frame" : "{{135, 40}, {135, 135}}",
"class" : "ImageView",
"nodes" : [
],
"attributes" : {
"frame" : "{{85, 94}, {100, 100}}",
"class" : "ImageView",
"name" : "img2",
"uuid" : "34C10386-4F29-421F-A4C2-14C6219FB3F9"
}
},
{
"selected" : true,
"frame" : "{{175, 239}, {80, 32}}",
"class" : "Button",
"nodes" : [
],
"attributes" : {
"border_width" : 0.5,
"frame" : "{{95, 128}, {80, 32}}",
"title" : "Test",
"class" : "Button",
"uuid" : "35CD0683-8263-41B9-B692-384CA67029A0",
"corner_radius" : 3,
"font_bold" : false,
"font_size" : 15,
"name" : "btn"
}
}
],
"attributes" : {
"enabled" : true,
"background_color" : "RGBA(1.000000,1.000000,1.000000,1.000000)",
"tint_color" : "RGBA(0.000000,0.478000,1.000000,1.000000)",
"border_color" : "RGBA(0.000000,0.000000,0.000000,1.000000)",
"flex" : ""
}
}
]'''
test_images = ['ionicons-ios7-alarm-32.png', 'ionicons-ios7-alarm-outline-32.png', 'ionicons-ios7-albums-32.png', 'ionicons-ios7-albums-outline-32.png', 'ionicons-ios7-arrow-back-32.png', 'ionicons-ios7-arrow-down-32.png', 'ionicons-ios7-arrow-forward-32.png', 'ionicons-ios7-arrow-left-32.png', 'ionicons-ios7-arrow-right-32.png', 'ionicons-ios7-arrow-thin-down-32.png', 'ionicons-ios7-arrow-thin-left-32.png', 'ionicons-ios7-arrow-thin-right-32.png', 'ionicons-ios7-arrow-thin-up-32.png', 'ionicons-ios7-arrow-up-32.png']
__DEBUG__ = False
class CellBase(object):
def __init__(self, w, h, item_index, threaded = False, thread_auto_start = False):
self.item_index = item_index
if __DEBUG__: self.debug_print('entered init')
self.width = w
self.height = h
# threading stuff
self.threading = threaded
self.event = None
self.thread = None
# activity indicator
self.act_ind = None
# flags
self.data_loaded = False
self.data_loading_error = 0
# debugging
self.debug = False
# create the view for the cell.
self.create_cell_contents()
if thread_auto_start:
self.start_thread()
if __DEBUG__: self.debug_print('exit init')
def start_thread(self):
# if we are threaded, and we have a thread that
# is alive, bug out. if it made sense, these
# exta request could be queued. will try to get
# this going properly first :)
if self.threading:
if self.thread:
if self.thread.isAlive():
print 'thread failed - thread still running' # debug
return
# Was going to try and resuse the thread.
# apprently, you cant. at least try to clean
# it up
# i will look at using thread 'with thread'..
# apprently the Threading module supports
# the context manager. i need to read more
if self.thread:
self.thread = None
self.thread =threading.Thread(target = self.fetch_data)
self.event = threading.Event()
self.thread.start()
def start_activity(self):
# if we dont have an activity indicator
# create one...
if not self.act_ind:
self.act_ind = ui.ActivityIndicator()
self.act_ind.style = 12
self.add_subview(self.act_ind)
#self.act_ind.center = self.center
self.act_ind.frame = (0,0, self.act_ind.width, self.act_ind.height)
self.act_ind.start()
self.act_ind.bring_to_front()
def stop_activity(self):
# if we have a activity ind. ,try and stop it
if self.act_ind:
self.act_ind.stop()
# this method to be overriden in the child
def create_cell_contents(self):
# the place you would typically create your
# cells ui elements
#self.add_item_id_label()
self.border_width = .5
self._set_row_alt_color('tan', 'orange')
# this method to be overriden in the child
def fetch_data(self):
self.start_activity()
for i in range(3):
if self.event.isSet() :
self.stop_activity()
self.data_loaded = 'False'
self.data_loading_error = 1
return
# same as do something
time.sleep(random.random())
if not self.event.isSet():
self.set_bgcolor_to_random()
self.stop_activity()
self.data_loaded = True
self.data_loading_error = 0
def thread_check(self):
# blah
if self.event.isSet():
self.stop_activity()
return True
return False
# aux , helper methods
def random_color(self):
return(randint(0,255) / 255., randint(0,255) / 255., randint(0,255) / 255.)
def set_bgcolor_to_random(self):
self.background_color = self.random_color()
def get_random_image(self):
pass
def _set_row_alt_color(self, color_1, color_2):
if not self.item_index % 2:
self.background_color = color_1
else:
self.background_color = color_2
def add_item_id_label(self):
# just add a label to see the item_index.
# handy for debugging
txt = '{:,}'.format(self.item_index)
w, h = 10 * len(txt), 20
f = (self.width - w, 0, w , h)
lb = ui.Label( frame = f)
lb.frame = f
lb.background_color = 'black'
lb.text_color = 'white'
lb.text = txt
lb.alignment = ui.ALIGN_RIGHT
self.add_subview(lb)
lb.bring_to_front()
# might need this if a single frame
def layout(self):
pass
# no need to add a btn to the view for a click
# event. this works very well.
# As long as we are ok with the whole cell
# ignoring, touch_began, touch_moved. Of course,
# other opporturnities to use these events.
# KISS at the moment :)
def touch_ended(self, touch):
self.cell_clicked()
# override this method if you want click events
def cell_clicked(self):
print self.item_index #debug
pass
# explicity call this method, to free up
# resources. whatever they maybe. dont rely on
# __del__
# can override this method, but then have to do all
# your own clean up. The general idea is to get
# this method working enough so you dont need to
# override it.
# hmmm, will add a callback, that if set, this
# method will call it.
def release(self):
# deal with the threading first
if self.threading:
if self.thread:
if self.thread.isAlive():
self.event.set()
self.stop_activity()
# call a user release method. one that can
# be overriden without disturbing this method!
# Not sure about the placement of this call.
# i was tempted to put it at the end of this
# method, would mean all the subviews would
# have been removed! hmmmm, not sure
self.user_release()
# remove ourself from the superview
# maybe this is a stupid idea. Maybe
# smarter for the caller to remove us?
if hasattr(self, 'superview'):
self.superview.remove_subview(self)
# this would not always be required. but
# i found when a button for example has a
# action attached, even internal to this
# class. Memory is not being released unless
# the subviews are removed. thats only one
# example. could be others, if using other
# callbacks such as delegates.
for sv in self.subviews:
self.remove_subview(sv)
self.thread = None
# if you need to release memory/resources in the
# child, override this method. Its called from
# inside the release method. threads are stopped
# and cleaned up, all views are still avaliable
# and released after this method returns.
def user_release(self):
pass
#debugging
def debug_print(self, msg):
print '{} , item {}'.format(msg, self.item_index)
class TestCellVirtualViewCell2(ui.View , CellBase):
def __init__(self, w, h, item_index, Threaded = False, auto_start_thread =True):
CellBase.__init__(self, w, h , item_index, threaded = Threaded, thread_auto_start = auto_start_thread)
self.cust_view = None
self.add_item_id_label()
# This method is not threaded, is overrided method
# from Cellbase. is called automatically.
def create_cell_contents(self):
self.cust_view = ui.load_view('Cell_layout')
self.cust_view.frame = self.frame
self.add_subview(self.cust_view)
self.cust_view.border_width = .5
self.cust_view['lb'].text = 'hello world'
self.cust_view['img1'].image = rand_image()
self.mylabel = self.cust_view['lb']
# this a limitation i dont understand.
# in the thread, i can not access
# self.cust_view['img2']. i am sure its easy
# to know why, but i dont know why.
self.img2 = self.cust_view['img2']
self.start_activity()
# this method is overridden from Cellbase and
# is called on a thread. is called from Cellbase
def fetch_data(self):
# simulate loading a web resource
time.sleep(random.random())
# this is a method in Cellbase class.
# a way to exit the thread
if self.thread_check():return
time.sleep(random.random())
if self.thread_check():return
time.sleep(random.random())
if self.thread_check():return
time.sleep(random.random())
if self.thread_check():return
time.sleep(random.random())
if self.thread_check():return
if self.thread_check():return
self.img2.image = rand_image()
# in the non threaded code, i can access
# self.cust_view['lb'].text, in this method,
# creates an exeception. basically,
# self.cust_view['lb'] evaluates to None
# THIS FAILS
#self.cust_view['lb'].text = 'Loaded'
# THIS IS OK!
self.mylabel.text = 'Loaded'
self.stop_activity()
def rand_image():
# just return an image from, a list (subset)
# of pythonista built in images.
# testing/demo purposes
return ui.Image.named(random.choice(test_images))
# for testing purposes, need to write out a pyui file
def create_pyui_file(file_name):
import os
import console
if os.path.isfile(file_name):
return True
result = console.alert('Please pay attention!', 'To proceed, need to write a pyui file to the same directory as this file.', "It's Ok")
if result <> 1 :
return False
with open(file_name, 'wb') as file:
file.write(pyui_data)
return True
def StandAloneTest(w, h, present):
v = ui.View(frame = (0,0,w, h))
item_w = w / 2
item_h = h / 2
# in this demo, the cells thread is being started
# manually at the end of the loop. Can pass
# True to auto_start_thread and remove the
# cell.start_thread() call.
for i in range(0,4):
cell= TestCellVirtualViewCell2(item_w, item_h, i, Threaded = True, auto_start_thread = False)
cell.frame = ((i % 2) * item_w, (i / 2) * cell.height , cell.width, cell.height)
v.add_subview(cell)
cell.start_thread()
v.present(present)
if __name__ == '__main__':
pyui_file_name = 'Cell_layout.pyui'
if create_pyui_file(pyui_file_name):
w, h = 540, 576
StandAloneTest(w,h, 'sheet')
else:
print 'application aborted!'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment