Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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)))
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
    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
Ejemplo n.º 14
0
    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())
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
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!")
Ejemplo n.º 18
0
    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())
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
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))
Ejemplo n.º 21
0
    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)
Ejemplo n.º 22
0
    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
Ejemplo n.º 24
0
    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)
Ejemplo n.º 25
0
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
Ejemplo n.º 26
0
    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)
Ejemplo n.º 27
0
    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
Ejemplo n.º 28
0
    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()
Ejemplo n.º 29
0
    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()
Ejemplo n.º 30
0
    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