def run(self): """ Reads the contents of the pipe and adds it to the queue until the pipe is closed. """ while True: line = six.ensure_str(self.pipe.readline()) # blocking read if line == "": break self.target_queue.put(line)
def get_latest_version(self, constraint_pattern=None): """ Returns a descriptor object that represents the latest version. This will connect to remote git repositories. Depending on how git is configured, https repositories requiring credentials may result in a shell opening up requesting username and password. This will clone the git repository into a temporary location in order to introspect its properties. .. note:: The concept of constraint patterns doesn't apply to git commit hashes and any data passed via the constraint_pattern argument will be ignored by this method implementation. :param constraint_pattern: If this is specified, the query will be constrained by the given pattern. Version patterns are on the following forms: - v0.1.2, v0.12.3.2, v0.1.3beta - a specific version - v0.12.x - get the highest v0.12 version - v1.x.x - get the highest v1 version :returns: IODescriptorGitBranch object """ if constraint_pattern: log.warning( "%s does not handle constraint patterns. " "Latest version will be used." % self ) try: # clone the repo, get the latest commit hash # for the given branch commands = [ 'checkout -q "%s"' % self._branch, "log -n 1 \"%s\" --pretty=format:'%%H'" % self._branch, ] git_hash = self._tmp_clone_then_execute_git_commands(commands) except Exception as e: raise TankDescriptorError( "Could not get latest commit for %s, " "branch %s: %s" % (self._path, self._branch, e) ) # make a new descriptor new_loc_dict = copy.deepcopy(self._descriptor_dict) new_loc_dict["version"] = six.ensure_str(git_hash) desc = IODescriptorGitBranch( new_loc_dict, self._sg_connection, self._bundle_type ) desc.set_cache_roots(self._bundle_cache_root, self._fallback_roots) return desc
def __init__(self, parent, command_name, button_name, icon, tooltip, timestamp): """ :param str command_name: Name of the command. :param str button_name: Name of the button. :param str icon: Path to the icon for this command. :param str tooltip: Toolkit for this command. :param datetime.datetime timestamp: When the command was last launched. """ super(RecentButton, self).__init__(parent) # No borders self.setFlat(True) self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding) self.setFocusPolicy(QtCore.Qt.NoFocus) layout = QtGui.QVBoxLayout(self) layout.setAlignment(QtCore.Qt.AlignHCenter) layout.setSpacing(self.SPACING) layout.setContentsMargins(self.MARGIN, self.MARGIN, self.MARGIN, self.MARGIN) self._timestamp = timestamp self.icon_label = QtGui.QLabel(self) self.icon_label.setAlignment(QtCore.Qt.AlignHCenter) self.layout().addWidget(self.icon_label, QtCore.Qt.AlignHCenter) # setting the stretch to 0 on the icon means the the text label will # stretch and the icon won't creating a more stable looking effect. layout.setStretch(0, 0) self.text_label = QtGui.QLabel(parent) self.text_label.setWordWrap(True) self.text_label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.layout().addWidget(self.text_label, QtCore.Qt.AlignHCenter) self.setFocusPolicy(QtCore.Qt.NoFocus) self.setStyleSheet(BUTTON_STYLE) self.text_label.setText(button_name) if icon is None: self.icon_label.setPixmap(QtGui.QIcon().pixmap(ICON_SIZE)) else: self.icon_label.setPixmap(QtGui.QIcon(icon).pixmap(ICON_SIZE)) self.setToolTip(tooltip) self._command_name = command_name self.clicked.connect(lambda: self.command_triggered.emit( six.ensure_str(self._command_name)))
def _session_path(): """ Return the path to the current session :return: """ path = cmds.file(query=True, sn=True) if path is not None: path = six.ensure_str(path) return path
def get_documentation_url_str(self): """ Returns the documentation URL. """ if self.app: doc_url = self.app.documentation_url # Deal with nuke's inability to handle unicode. if type(doc_url) == six.text_type: doc_url = six.ensure_str( unicodedata.normalize("NFKD", doc_url), "ascii", "ignore") return doc_url return None
def __init__(self, host, http_proxy): """ Constructor. :param host: Host for this Shotgun user. :param http_proxy: HTTP proxy to use with this host. """ if not host: raise IncompleteCredentials("missing host") # We need to ensure that we are always storing utf-8 so that # we don't end up infecting API instances with unicode strings # that would then cause some string data to be unicoded during # concatenation operations. if http_proxy is not None: http_proxy = six.ensure_str(http_proxy) host = six.ensure_str(host) self._host = host self._http_proxy = http_proxy
def store(self, name, value, scope=SCOPE_GLOBAL): """ Stores a setting for an app. This setting is tied to the current login. :param name: Name of the setting to store :param value: Value to store. Use simple types such as ints, strings, dicts etc. In the interest of cross pyside/pyqt compatibility, any QStrings or QVariants passed in via value will be converted to strs and native python types. Unicode strs will be converted to utf-8. :param scope: The scope for this settings value, as defined by the constants belonging to this class. """ full_name = self.__resolve_settings_name(name, scope) self.__fw.log_debug("User Settings Manager: Storing %s" % full_name) try: # Weird behaviour incoming: # When QSettings sees a value that it deems to complicated for Qt, # it pickles it! # Pickling a class for example would force QSetting to pickle the value # first. But so does passing in a bytes object! # If you are running Python 3, then the protocol used will be 3, which # becomes an issue if you're switching to Python 2 to read the same setting # since QSetting will first try to unpickle the value and protocol 3 # didn't exist in Python 2. # # This very small scripts reproduces that weird behavior: # # from PySide2 import QtCore # import sys # # settings = QtCore.QSettings("test") # # if sys.version_info[0] == 2: # print(settings.value("test_value")) # else: # settings.setValue( # "test_value", # b"binary value" # ) # # Running this script once with Python 3 will write to QSettings and running # it with Python 2 will read it and raise a pickler error, even tough we # never used the pickler in our code in the first place. # # To get around this, we need to write a string inside the QSettings, so # use sgtk.util.pickle value_str = sgtk.util.pickle.dumps(sanitize_qt(value)) self.__settings.setValue(full_name, six.ensure_str(value_str)) except Exception as e: self.__fw.log_warning( "Error storing user setting '%s'. Error details: %s" % (full_name, e))
def __init__(self, filter_id, group_id, filter_data, parent=None): """ Constructor. Initialize the widget UI: - Left aligned checkbox - Left aligned icon (optional) - Left aligned filter value display text - Right aligned filter value count :param filter_id: The unique identifier for this widget. :type filter_id: str :param group_id: The unique identifier for the group this widget belongs to. :type group_id: str :param filter_data: Additional data to initialize the widget. :type filter_data: dict :param parent: The widget's parent :type parent: :class:`sgtk.platform.qt.QWidget` """ super(ChoicesFilterItemWidget, self).__init__(filter_id, group_id, parent) layout = QtGui.QHBoxLayout() layout.setAlignment(QtCore.Qt.AlignLeft) # Left-aligned checkbox self.checkbox = QtGui.QCheckBox() self.checkbox.stateChanged.connect(self.state_changed) layout.addWidget(self.checkbox) # Left-aligned (optional) icon icon = filter_data.get("icon") if icon: icon_label = QtGui.QLabel() icon_label.setPixmap(icon.pixmap(14)) layout.addWidget(icon_label) # Left-aligned filter value display text name = six.ensure_str( filter_data.get("display_name", filter_data.get("filter_value")) ) self.label = QtGui.QLabel(name) layout.addWidget(self.label) # Right-aligned count label count = filter_data.get("count") if count: self.count_label = QtGui.QLabel(str(count)) layout.addStretch() layout.addWidget(self.count_label) self.setLayout(layout)
def __init__( self, keys_path, encrypt, host, user_id, host_aliases, port=None, uses_intermediate_certificate_chain=False, ): """ Constructor. :param keys_path: Path to the keys. If the path is relative, it will be relative to the current working directory. Mandatory :param encrypt: If True, the communication with clients will be encrypted. :param host: Url of the host we're expecting requests from. :param user_id: Id of the user we're expecting requests from. :param host_aliases: List of aliases available for the current host. :param port: Port to listen for websocket requests from. :param low_level_debug: If True, wss traffic will be written to the console. """ self._port = port or self._DEFAULT_PORT self._keys_path = keys_path or self._DEFAULT_KEYS_PATH self._host = host self._user_id = user_id self._host_aliases = host_aliases self._uses_intermediate_certificate_chain = uses_intermediate_certificate_chain # If encryption is required, compute a server id and retrieve the secret associated to it. if encrypt: # urandom is considered cryptographically secure as it calls the OS's CSRNG, so we can # use that to generate our own server id. self._ws_server_id = six.ensure_str( base64.urlsafe_b64encode(os.urandom(16))) else: self._ws_server_id = None self.notifier = self.Notifier() if not os.path.exists(keys_path): raise MissingCertificateError(keys_path) twisted = get_logger("twisted") logger.debug("Browser integration using certificates at %s", self._keys_path) logger.debug("Encryption: %s", encrypt) # This will take the Twisted logging and forward it to Python's logging. self._observer = log.PythonLoggingObserver(twisted.name) self._observer.start()
def get_linux_version(cls): """ Returns a Linux friendly version string such as: "Ubuntu 12", "Fedora 24", "Red Hat 7", "Debian 8" etc :return: A str of a simple OS version string. """ os_version = "Unknown" try: # Get the distributon name and capitalize word(s) (e.g.: Ubuntu, Red Hat) distribution = six.ensure_str( distro.linux_distribution()[0].title()) raw_version_str = six.ensure_str(distro.linux_distribution()[1]) # For Linux we really just want the 'major' version component major_version_str = re.findall(r"\d*", raw_version_str)[0] os_version = "%s %s" % (distribution, major_version_str) except: pass return os_version
def _ensure_contains_str(input_value, visited): """ Convert the keys and values of arrays and dicts to ensure no ``unicode`` objects are present. :param object input_value: Value to validate. :param set visited: List of objects already visited. :returns: The converted value, if required. :rtype: object """ # It's important to keep track of visited lists and dictionary, as # there can be circular dependencies between those. Failing to # keep track of them will introduce cycles which will make this method # do a stack overflow. # # Certain parts of Toolkit, like the ShotgunModel's cache from shotgunutils # actually pickle structures with circular dependencies, so we have to # handle that case. # # Also, we need to keep the instances of the dict and array's the same so # those back references are kept, so we'll always in-place edit arrays # and dicts instead of instantiating a new array or dict with the # updated values. # If we've found a unicode object of a bytes string, convert them back to # string. if isinstance(input_value, (six.text_type, six.binary_type)): return six.ensure_str(input_value) # If we've found a new array, we must ensure each element is # not a unicode object. elif isinstance(input_value, list) and id(input_value) not in visited: visited.add(id(input_value)) for i in range(len(input_value)): item = input_value[i] input_value[i] = _ensure_contains_str(item, visited) return input_value # If we've found a new dict, we must ensure each key and value # is not a unicode object. elif isinstance(input_value, dict) and id(input_value) not in visited: visited.add(id(input_value)) for key in list(input_value.keys()): item = input_value.pop(key) converted_item = _ensure_contains_str(item, visited) converted_key = _ensure_contains_str(key, visited) input_value[converted_key] = converted_item return input_value # Not a unicode, bytes, list or array, so return as is. else: return input_value
def _import_script(self, path, sg_publish_data): """ Import contents of the given file into the scene. :param path: Path to file. :param sg_publish_data: Shotgun data dictionary with all the standard publish fields. """ import nuke # must use unicode otherwise path won't be found if not os.path.exists(six.ensure_str(path)): raise Exception("File not found on disk - '%s'" % path) nuke.nodePaste(path)
def __init__(self, view, text=None): """ :param view: The view where this delegate is being used """ super(SearchResultDelegate, self).__init__(view) self._pixmaps = CompleterPixmaps() self._text = six.ensure_str(text) self.selection_model = view.selectionModel() if self.selection_model: self.selection_model.selectionChanged.connect(self._on_selection_changed) self.__current_index = None
def __init__(self, parent, command_name, button_name, icon, tooltip): """ :param parent: Parent widget. :param str command_name: Name of the default command to execute. :param str button_name: Name of the button. :param str icon: Path to the icon. :param str tooltip: Tooltip for the button. """ super(CommandButton, self).__init__(parent) self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding) self.setFocusPolicy(QtCore.Qt.NoFocus) self.setIconSize(ICON_SIZE) self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.setStyleSheet(BUTTON_STYLE) self.setText(" %s" % button_name) self._set_default(tooltip, icon) self._commands = [] # This menu will implement the drop down behaviour of the tool button. self._menu = QtGui.QMenu(self) self._button_name = button_name # The data of an action contains the command name. self._menu.triggered.connect( # The .data method returns a unicode string in Python 2, so force it to a utf8 str. lambda action: self.command_triggered.emit( six.ensure_str(action.data()))) # This is a workaround for a PySide2 issue where the hover state of the button # is not properly cleared when the menu is dismissed or an action clicked. # # Inspired by this workaround: # https://forum.qt.io/topic/36348/solved-how-do-i-clear-hover-state-on-a-qgraphicswidget-wrapped-qtoolbutton def cleanup(): self.setAttribute( QtCore.Qt.WA_UnderMouse, # Check if the cursor is still over the widget and set the under mouse # property appropriately. self.rect().contains(self.mapFromGlobal(QtGui.QCursor.pos())), ) self._menu.aboutToHide.connect(cleanup) # Clicking on the button triggers the first action. self.clicked.connect(lambda: self._menu.actions()[0].trigger())
def filterAcceptsRow(self, source_row, source_parent_idx): """ Overridden from base class. This will check each row as it is passing through the proxy model and see if we should let it pass or not. """ if self._valid_type_ids is None: # accept all! return True model = self.sourceModel() current_item = model.invisibleRootItem().child( source_row) # assume non-tree structure # first analyze any search filtering if self._search_filter: # there is a search filter entered field_data = shotgun_model.get_sanitized_data( current_item, SgLatestPublishModel.SEARCHABLE_NAME) # all input we are getting from pyside is as unicode objects # all data from shotgun is utf-8. By converting to utf-8, # filtering on items containing unicode text also work. search_str = six.ensure_str(self._search_filter) if search_str.lower() not in field_data.lower(): # item text is not matching search filter return False # now check if folders should be shown is_folder = current_item.data(SgLatestPublishModel.IS_FOLDER_ROLE) if is_folder: return self._show_folders # lastly, check out type filter checkboxes sg_type_id = current_item.data(SgLatestPublishModel.TYPE_ID_ROLE) if sg_type_id is None: # no type. So always show. return True elif sg_type_id in self._valid_type_ids: return True else: return False
def _launch_rv(base_url, cmd, source=None, path_to_rv=None): app = sgtk.platform.current_bundle() if path_to_rv: # We need to set the server if not going via the SG site rvlink cmd = 'shotgun_review_app.theMode().setServer("%s"); %s' % (base_url, cmd) # Launch RV with the given list of args. # The command needs to be enclosed in quotes when using the rvlink protocol args = [ "-flags", "ModeManagerPreload=shotgun_review_app", "-eval", "'%s'" % cmd ] if source: args.append(source) combined_args = " " + " ".join(args) # We should encode the args so that no special characters exist in the command # Encode the string into binary so that we can convert the args to a hex binary_str = six.ensure_binary(combined_args) # Encode the binary into a hex encoded_args = codecs.encode(binary_str, "hex") # Now convert the binary back into a string which contains the hex representing the args. hex_encoded_args_string = six.ensure_str(encoded_args) # If no path to RV was provided, us the SG site rvlink protocol to launch RV if not path_to_rv: # Encode the RV args. We'll use shotgun to redirect to the RV app via the rvlink # custom protocol url = "%s/rvlink/baked/%s" % (base_url, hex_encoded_args_string) app.logger.info("Opening URL: %s" % url) webbrowser.open(url) else: url = "rvlink://baked/%s" % (hex_encoded_args_string) cmdLine = " ".join(['"%s"' % path_to_rv, url]) app.logger.info("Running %s" % cmdLine) subprocess.Popen(cmdLine, shell=True) # For some reason, on Python 3, it doesn't complete launching of the RV if python exits straight after. # Adding a small sleep seems to allow it enough time to launch, although this is ugly. # It was also found that launching Firefox or the Calculator app did work without the need for the sleep. # So it maybe something specific to RV. # This will only likely happen in the shell engine, so only add the sleep in shell engine. # FIXME: We need to find a better fix than waiting and hoping that is long enough to allow RV to start launching. if app.engine.name == "tk-shell": time.sleep(0.5)
def process_message(self, message): """ Callback which will be called whenever a message is received. :param str message: Raw message payload as sent by client. :raises: RuntimeError """ message = six.ensure_str(message) if self._state == self.AWAITING_HANDSHAKE: self._handle_protocol_handshake_request(message) elif self._state == self.AWAITING_SERVER_ID_REQUEST: self._handle_server_id_request(message) elif self._state == self.AWAITING_ENCRYPTED_REQUEST: self._handle_encrypted_request(message) else: raise RuntimeError("Unknown state!")
def _update_recents_list(self, command_name): """ Called when a command is launched from the panel. This is used to keep the recent list updated. """ if self._show_recents is False: return # Make sure the string is a str and not unicode. This happens in # Python 2.7. command_name = six.ensure_str(command_name) self._recents[command_name] = {"timestamp": datetime.datetime.utcnow()} self._store_recents() self._refresh_recent_list(command_name) self._restrict_recent_buttons(self._get_optimal_width())
def _convert(data): """ Converts all keys/values in a dictionary from unicode to utf-8 encoded strings. :param dict data: Object with unicode values :returns: Object with only utf-8 encoded strings """ if isinstance(data, six.string_types): return six.ensure_str(data) if isinstance(data, six.moves.collections_abc.Mapping): return {k: _convert(v) for k, v in data.items()} elif isinstance(data, six.moves.collections_abc.Iterable): return [_convert(v) for v in data] else: return data
def dumps(data): """ Return the pickled representation of ``data`` as a ``str``. This methods wraps the functionality from the :func:`pickle.dumps` method so pickles can be shared between Python 2 and Python 3. As opposed to the Python 3 implementation, it will return a ``str`` object and not ``bytes`` object. :param data: The object to pickle and store. :returns: A pickled str of the input object. :rtype: str """ # Force pickle protocol 0, since this is a non-binary pickle protocol. # See https://docs.python.org/2/library/pickle.html#pickle.HIGHEST_PROTOCOL # Decode the result to a str before returning. return six.ensure_str(cPickle.dumps(data, **DUMP_KWARGS))
def _new_connection(self, socket_id, name, address, port, request): """ Callback that fires when a new websockets connection is requested. :param str socket_id: Unique id for the connection :param str name: Name of connection :param int port: Port that connection is using. :param request: QNetworkRequest object describing the request. """ logger.debug("New ws connection %s from %s %s %s" % (socket_id, name, address, port)) # The origin coming from the request's raw header will be a bytearray. We're # going to want it as a string, so we'll go ahead and convert it right away. origin_site = request.rawHeader(b"origin").data() self._connections[socket_id] = WebsocketsConnection( socket_id, six.ensure_str(origin_site), self._encryption_handler, self)
def _create_context_menu(self): """ Create a context menu which displays the current context. :returns: A Qt menu instance representing the context menu. :rtype: QtGui.QMenu """ ctx = self._engine.context ctx_name = six.ensure_str(str(ctx)) context_menu = create_qt_menu(ctx_name) context_menu.addAction("Jump to ShotGrid", self._jump_to_sg) # Add the menu item only when there are filesystem locations. if ctx.filesystem_locations: context_menu.addAction("Jump to File System", self._jump_to_fs) return context_menu
def get_title(self, context): """ Returns the title that should be used for the version :param context: The context associated with the version. :returns: Version title string. """ # rather than doing a version numbering scheme, which we # reserve for publishing workflows, the default implementation # uses a date and time based naming scheme sg_version_name = "" # include the shot/link as part of the name # if context.entity and context.entity["name"]: # # start with the link # sg_version_name += "[%s %s] " % ( # context.entity["type"], # context.entity["name"] # ) # default name in case no nuke file name is set sg_version_name = "Quickreview" # now try to see if we are in a normal work file # in that case deduce the name from it current_scene_path = nuke.root().name() current_scene_path = six.ensure_str(current_scene_path) if current_scene_path and current_scene_path != "Root": current_scene_path = current_scene_path.replace("/", os.path.sep) # get just filename current_scene_name = os.path.basename(current_scene_path) # drop .nk current_scene_name = os.path.splitext(current_scene_name)[0] #name = current_scene_name.replace("_", " ").capitalize() sg_version_name += "\n%s" % current_scene_name # append date and time timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") sg_version_name += "\n%s" % timestamp return sg_version_name
def save_current_file(self, file_path): """Tell VRED to save the out the current project as a file.""" self.logger.debug("Save File: {}".format(file_path)) vrFileIO.save(file_path) if not os.path.exists(six.ensure_str(str(file_path))): msg = "VRED Failed to save file {}".format(file_path) self.logger.error(msg) raise Exception(msg) allowed_to_open = self._engine.execute_hook_method("file_usage_hook", "file_attempt_open", path=file_path) if not allowed_to_open: raise Exception( "Can't save file: a lock for this path already exists") self.set_render_path(file_path)
def store_env_var_pickled(key, data): """ Stores the provided data under the environment variable specified. .. note:: This method is part of Toolkit's internal API. In Python 3 pickle.dumps() returns a binary object that can't be decoded to a string for storage in an environment variable. To work around this, we encode the pickled data to base64, compress the result, and store that. :param key: The name of the environment variable to store the data in. :param data: The object to pickle and store. """ # Force pickle protocol 0, since this is a non-binary pickle protocol. # See https://docs.python.org/2/library/pickle.html#pickle.HIGHEST_PROTOCOL pickled_data = dumps(data) encoded_data = six.ensure_str(pickled_data) os.environ[key] = encoded_data
def __init__(self): """ :param str site_url: Shotgun site to create a site handler for. """ self._bundle = sgtk.platform.current_bundle() # Compute a server id and retrieve the secret associated to it. # urandom is considered cryptographically secure as it calls the OS's CSRNG, so we can # use that to generate our own server id. self._unique_server_id = six.ensure_str( base64.urlsafe_b64encode(os.urandom(16))) # get the secret from the shotgun site. # don't hold on to it but pass it to # the encryption library. ws_server_secret = self._retrieve_server_secret() # create encryption session self._fernet = Fernet(ws_server_secret)
def _add_context_menu(self): """ Adds a context menu which displays the current context :return: An :class:`alias_api.MenuItem` instance representing the context menu. """ ctx = self._engine.context ctx_name = six.ensure_str(str(self._engine.context)) # Create the menu object ctx_menu = self._alias_menu.add_menu(ctx_name) self._alias_menu.add_command("Jump to Shotgun", self._jump_to_sg, parent=ctx_menu) if ctx.filesystem_locations: self._alias_menu.add_command("Jump to File System", self._jump_to_fs, parent=ctx_menu) return ctx_menu
def _execute_action(self, path, action_str): """ Triggered from the engine when a user clicks an action :param str path: entity path representation. :param str action_str: serialized :class:`ExternalCommand` payload. """ action_str = six.ensure_str(action_str) # the 'loading' menu items currently don't have an action payload, # just an empty string. if action_str != "": # Get the Python pickle string out of the JSON obj comming from C++ json_obj = json.loads(action_str) if self.KEY_PICKLE_STR not in json_obj: raise RuntimeError( "The command's serialized Python data could not be found in the action's payload" "that ShotGrid Create provided. The action cannot be executed as a result." ) # and create a command object. pickle_string = json_obj[self.KEY_PICKLE_STR] action = external_config.ExternalCommand.deserialize(pickle_string) # Notify the user that the launch is occurring. If it's a DCC, there can # be some delay, and this will help them know that the work is happening. self._toolkit_manager.emitToast( "Launching %s..." % action.display_name, "info", False, # Not persistent, meaning it'll stay for 5 seconds and disappear. ) # run in a thread to not block thread_cb = lambda a=action: self._execute_action_payload(a) worker = threading.Thread(target=thread_cb) # setting daemon to True means the main process can quit # and the action process can live on worker.daemon = True worker.start()
def add_output(self, text): """ Append the supplied output text to the contents. The text is formatted and colored to make it obvious that it is output. :param text: The output text to display. """ if six: # if six can be imported sanitize the string. # This may lead to unicode errors if not imported in python 2 text = six.ensure_str(text) else: str(text) with self._write_lock: text = self._to_html(text) self.moveCursor(QtGui.QTextCursor.End) self.insertHtml(text) self._scroll_to_bottom()
def execute_action(self, name, params, sg_publish_data): """ Execute a given action. The data sent to this be method will represent one of the actions enumerated by the generate_actions method. :param name: Action name string representing one of the items returned by generate_actions. :param params: Params data, as specified by generate_actions. :param sg_publish_data: Shotgun data dictionary with all the standard publish fields. :returns: No return value expected. """ app = self.parent app.log_debug( "Execute action called for action %s. " "Parameters: %s. Publish Data: %s" % (name, params, sg_publish_data) ) # resolve path # toolkit uses utf-8 encoded strings internally and Maya API expects unicode # so convert the path to ensure filenames containing complex characters are supported path = six.ensure_str(self.get_publish_path(sg_publish_data)) asset_data = None if name == "reference": asset_data = self._create_reference(path, sg_publish_data) if name == "import": asset_data = self._do_import(path, sg_publish_data) if name == "texture_node": self._create_texture_node(path, sg_publish_data) if name == "udim_texture_node": self._create_udim_texture_node(path, sg_publish_data) if name == "image_plane": self._create_image_plane(path, sg_publish_data) return asset_data