Created
September 5, 2015 11:28
-
-
Save Phuket2/a8e32519d67bc4d2c7ee to your computer and use it in GitHub Desktop.
ThreadedCell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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