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)
Example #2
0
    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!")