def _on_worker_task_complete(self, uid, data): """ Called when the computation is complete and we should update widget with the result """ if uid != self._worker_uid: return # stop spin self._timer.stop() # set thumbnail if data.get("thumbnail"): self.ui.thumbnail.setPixmap(QtGui.QPixmap(QtGui.QImage(data.get("thumbnail")))) # set light - red or green if data["up_to_date"]: icon = self._green_pixmap else: icon = self._red_pixmap self.ui.light.setPixmap(icon) # figure out if this item should be hidden if data["up_to_date"] == True and self._show_green == False: self.setVisible(False) if data["up_to_date"] == False and self._show_red == False: self.setVisible(False)
def run(self): """ Main thread loop """ # keep running until thread is terminated while self._process_queue: # Step 1. get the next item to process. # We check things in the following priority order: # - If there is anything in the thumb check queue, do that first # - Then check sg queue # - Lastly, check thumb downloads item_to_process = None item_type = None self._queue_mutex.lock() try: if len(self._thumb_check_queue) > 0: item_to_process = self._thumb_check_queue.pop(0) item_type = ShotgunDataRetriever._THUMB_CHECK elif len(self._sg_requests_queue) > 0: item_to_process = self._sg_requests_queue.pop(0) if item_to_process["action"] == "execute_find": item_type = ShotgunDataRetriever._SG_FIND_QUERY elif item_to_process["action"] == "get_schema": item_type = ShotgunDataRetriever._SCHEMA_DOWNLOAD elif item_to_process["action"] == "execute_update": item_type = ShotgunDataRetriever._SG_UPDATE_QUERY elif item_to_process["action"] == "execute_create": item_type = ShotgunDataRetriever._SG_CREATE_QUERY elif item_to_process["action"] == "execute_delete": item_type = ShotgunDataRetriever._SG_DELETE_QUERY elif item_to_process["action"] == "execute_method": item_type = ShotgunDataRetriever._EXECUTE_METHOD elif len(self._thumb_download_queue) > 0: item_to_process = self._thumb_download_queue.pop(0) item_type = ShotgunDataRetriever._THUMB_DOWNLOAD else: # no work to be done! # wait for some more work - this unlocks the mutex # until the wait condition is signalled where it # will then attempt to obtain a lock before returning self._wait_condition.wait(self._queue_mutex) # once the wait condition is triggered (usually by something # inserted into one of the queues), trigger the check to happen again continue finally: self._queue_mutex.unlock() # Step 2. Process next item and send signals. try: # process the item: if item_type == ShotgunDataRetriever._THUMB_CHECK: # check if a thumbnail exists on disk. If not, fall back onto # a thumbnail download from shotgun/s3 url = item_to_process["url"] path_to_cached_thumb = self._get_thumbnail_path(url, self._bundle) if os.path.exists(path_to_cached_thumb): # thumbnail already here! yay! if item_to_process["load_image"]: image = QtGui.QImage() image.load(path_to_cached_thumb) else: image = None self.work_completed.emit(item_to_process["id"], "thumb", {"thumb_path": path_to_cached_thumb, "image": image} ) else: # no thumb here. Stick the data into the thumb download queue to request download self._queue_mutex.lock() try: self._thumb_download_queue.append(item_to_process) finally: self._queue_mutex.unlock() elif item_type == ShotgunDataRetriever._SG_FIND_QUERY: # get stuff from shotgun sg = self.__get_sg_connection().find(*item_to_process["args"], **item_to_process["kwargs"]) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "find", {"sg": sg } ) elif item_type == ShotgunDataRetriever._SG_UPDATE_QUERY: # update stuff in shotgun sg = self.__sg.update(*item_to_process["args"], **item_to_process["kwargs"]) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "update", {"sg": sg } ) elif item_type == ShotgunDataRetriever._SG_CREATE_QUERY: # create stuff in shotgun sg = self.__get_sg_connection().create(*item_to_process["args"], **item_to_process["kwargs"]) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "create", {"sg": sg } ) elif item_type == ShotgunDataRetriever._SG_DELETE_QUERY: # delete stuff in shotgun sg = self.__get_sg_connection().delete(*item_to_process["args"], **item_to_process["kwargs"]) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "delete", {"sg": sg } ) elif item_type == ShotgunDataRetriever._EXECUTE_METHOD: # run method ret_val = item_to_process["method"](self.__get_sg_connection(), item_to_process["data"]) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "method", {"return_value": ret_val } ) elif item_type == ShotgunDataRetriever._SCHEMA_DOWNLOAD: if item_to_process["project_id"]: project = {"type": "Project", "id": item_to_process["project_id"]} else: project = None # read in details about all fields sg_field_schema = self.__get_sg_connection().schema_read(project) # and read in details about all entity types sg_type_schema = self.__get_sg_connection().schema_entity_read(project) # need to wrap it in a dict not to confuse pyqt's signals and type system self.work_completed.emit(item_to_process["id"], "schema", {"fields": sg_field_schema, "types": sg_type_schema } ) elif item_type == ShotgunDataRetriever._THUMB_DOWNLOAD: # download the actual thumbnail. Because of S3, the url # may have expired - in that case fall back, get a fresh url # from shotgun and try again entity_id = item_to_process["entity_id"] entity_type = item_to_process["entity_type"] field = item_to_process["field"] url = item_to_process["url"] path_to_cached_thumb = self._get_thumbnail_path(url, self._bundle) self._bundle.ensure_folder_exists(os.path.dirname(path_to_cached_thumb)) # first of all, there may be a case where another process has alrady downloaded # the thumbnail for us, so make sure that we aren't doing any extra work :) if not os.path.exists(path_to_cached_thumb): # first try to download based on the path we have try: tank.util.download_url(self.__get_sg_connection(), url, path_to_cached_thumb) except TankError, e: # Note: Unfortunately, the download_url will re-cast # all exceptions into tankerrors. # get a fresh url from shotgun and try again sg_data = self.__get_sg_connection().find_one(entity_type, [["id", "is", entity_id]], [field]) if sg_data is None or sg_data.get(field) is None: # no thumbnail! This is possible if the thumb has changed # while we were queueing it for download. # indicate the fact that the thumbnail no longer exists on the server # by setting the path to None path_to_cached_thumb = None else: # download from sg url = sg_data[field] tank.util.download_url(self.__get_sg_connection(), url, path_to_cached_thumb) if path_to_cached_thumb: # now we have a thumbnail on disk, either via the direct # download, or via the url-fresh-then-download approach # the file is downloaded with user-only permissions # modify the permissions of the file so it's writeable by others old_umask = os.umask(0) try: os.chmod(path_to_cached_thumb, 0666) finally: os.umask(old_umask) # finally, see if the worker thread should also load in the image if path_to_cached_thumb and item_to_process["load_image"]: image = QtGui.QImage() image.load(path_to_cached_thumb) else: image = None self.work_completed.emit(item_to_process["id"], "thumb", {"thumb_path": path_to_cached_thumb, "image": image} ) else: raise Exception("Unknown task type!")