Esempio n. 1
0
    def update_is_available(self, show_dialogs=True):
        """
        Returns whether or not a new Artella DCC plugin version is available to download
        :return:
        """

        from artella.plugins.updater import utils

        current_version = dccplugin.DccPlugin().get_version()
        if not current_version:
            return True
        latest_release_info = utils.get_latest_stable_artella_dcc_plugin_info(
            show_dialogs=show_dialogs)
        if not latest_release_info:
            return True
        latest_version = latest_release_info.get('version', None)
        if not latest_version:
            return True

        current_version_split = current_version.split('.')
        latest_version_split = latest_version.split('.')

        if latest_version_split[0] > current_version_split[0]:
            return True
        else:
            if latest_version_split[1] > current_version_split[1]:
                return True
            else:
                if latest_version_split[2] > latest_version_split[2]:
                    return True

        return False
Esempio n. 2
0
    def _fill_data(self):
        current_plugin_version = dccplugin.DccPlugin().get_version() or 'Undefined'
        self._artella_dcc_plugin_version_label.setText(current_plugin_version)

        added_packages = dict()

        # Retrieve Artella plugins versions
        all_plugins = plugins.plugins()
        for plugin_id, plugin_data in all_plugins.items():
            plugin_package = plugin_data.get('package', 'Not Defined')
            package_item = added_packages.get(plugin_package, None)
            if not package_item:
                package_item = QtWidgets.QTreeWidgetItem([plugin_package])
                self._plugins_tree.addTopLevelItem(package_item)
                added_packages[plugin_package] = package_item
            plugin_name = plugin_data['name']
            plugin_version = plugin_data.get('version', 'Undefined')
            plugin_item = QtWidgets.QTreeWidgetItem([plugin_name, plugin_version, plugin_id])
            package_item.addChild(plugin_item)

        package_item = added_packages.get('Artella', None)
        if not package_item:
            package_item = QtWidgets.QTreeWidgetItem(['Artella'])
            self._plugins_tree.addTopLevelItem(package_item)

        # Retrieve DCC plugin version
        dcc_version = None
        dcc_module_name = core_dcc.CURRENT_DCC_MODULE
        if dcc_module_name:
            try:
                dcc_module_version = '{}.__version__'.format(dcc_module_name)
                dcc_version_mod = utils.import_module(dcc_module_version)
                dcc_version = dcc_version_mod.get_version()
            except Exception as exc:
                logger.warning('Impossible to retrieve {} Artella plugin version: {}'.format(dcc.name(), exc))
        dcc_version = dcc_version or 'Undefined'
        dcc_version_item = QtWidgets.QTreeWidgetItem([dcc.nice_name(), dcc_version, dcc_module_name.replace('.', '- ')])
        package_item.insertChild(0, dcc_version_item)

        # Retrieve Artella core version
        core_version = None
        core_version_path = 'artella.__version__'
        try:
            core_version_mod = utils.import_module(core_version_path)
            core_version = core_version_mod.get_version()
        except Exception as exc:
            logger.warning('Impossible to retrieve Artella Core version: {}'.format(exc))
        core_version = core_version or 'Undefined'
        core_version_item = QtWidgets.QTreeWidgetItem(['Core', core_version, 'artella-plugins-core'])
        package_item.insertChild(0, core_version_item)

        self._plugins_tree.expandAll()
        self._plugins_tree.resizeColumnToContents(0)
        self._plugins_tree.resizeColumnToContents(1)
        self._plugins_tree.resizeColumnToContents(2)
Esempio n. 3
0
def is_artella_path(file_path=None):
    """
    Returns whether or not given file path is an Artella file path or not
    A path is considered to be an Artella path if the path is located inside the Artella project folder
    in the user machine

    :param str file_path: path to check. If not given, current DCC scene file path will be used
    :return: True if the given file path is an Artella path; False otherwise.
    :rtype: bool
    """

    return dccplugin.DccPlugin().is_artella_path(file_path)
Esempio n. 4
0
    def init_ui(self):
        """
        Function that initializes plugin UI related functionality
        """

        main_menu = dccplugin.DccPlugin().get_main_menu()
        if not main_menu:
            return

        plugin_menu = self._config_dict.get('menu')
        plugin_package = self._config_dict.get('package', 'Artella')
        plugin_dccs = self._config_dict.get('dcc', list())

        can_load_plugin = True
        if plugin_dccs:
            can_load_plugin = dcc.name() in plugin_dccs

        if can_load_plugin:
            if plugin_menu and 'label' in plugin_menu:
                menu_parents = plugin_menu.get('parents', None)
                if menu_parents:
                    current_parent = 'Artella' if dcc.check_menu_exists(
                        'Artella') else None
                    for menu_parent in menu_parents:
                        if not dcc.check_menu_exists(menu_parent):

                            # TODO: Before More Artella menu addition we force the creation of a
                            # TODO: separator. We should find a way to avoid hardcoded this.
                            icon = ''
                            if menu_parent == 'More Artella':
                                dcc.add_menu_separator('Artella')
                                icon = 'artella.png'

                            dcc.add_sub_menu_item(menu_parent,
                                                  parent_menu=current_parent,
                                                  icon=icon)
                        current_parent = menu_parent

                menu_label = plugin_menu['label']
                menu_command = plugin_menu['command']
                menu_icon = self._config_dict.get('icon', '')
                if menu_parents:
                    menu_parent = menu_parents[-1]
                    dcc.add_menu_item(menu_label,
                                      menu_command,
                                      menu_parent,
                                      icon=menu_icon)
                else:
                    dcc.add_menu_item(menu_label,
                                      menu_command,
                                      main_menu,
                                      icon=menu_icon)
Esempio n. 5
0
def async_execute_in_main_thread(fn, *args, **kwargs):
    """
    Executes the given function in the main thread when called from a non-main thread. This call will return
    immediately and will not wait for the code to be executed in the main thread.

    :param fn: function to call
    :param args:
    :param kwargs:
    :return:
    """

    return dccplugin.DccPlugin().async_execute_in_main_thread(fn=fn,
                                                              *args,
                                                              **kwargs)
Esempio n. 6
0
def download_file(file_path, show_dialogs=True):
    """
    Downloads a file from Artella server

    :param str file_path: File we want to download from Artella server
    :param bool show_dialogs: Whether UI dialogs should appear or not.
    :return: True if the download operation was successful; False otherwise.
    :example:
    >>> self.download_file("C:/Users/artella/artella-files/ProjectA/Assets/test/model/hello.a")
    True
    """

    return dccplugin.DccPlugin().download_file(file_path=file_path,
                                               show_dialogs=show_dialogs)
Esempio n. 7
0
def show_warning_message(text, title='', duration=None, closable=True):
    """
    Shows a warning message

    :param text: str, warning text to show
    :param title: str, title of the warning message
    :param duration: float or None, if given, message only will appear the specified seconds
    :param closable: bool, Whether the message can be closed by the user or not (when duration is given)
    """

    return dccplugin.DccPlugin().show_warning_message(text=text,
                                                      title=title,
                                                      duration=duration,
                                                      closable=closable)
Esempio n. 8
0
def is_client_available(update=False):
    """
    Returns whether or not current client is available

    :param update: bool, Whether or not remote sessions should be updated
        :return: True if the client is available and running; False otherwise.
    :rtype: ArtellaDriveClient
    """

    artella_drive_client = dccplugin.DccPlugin().get_client()
    if not artella_drive_client or not artella_drive_client.check(
            update=update):
        return False

    return True
Esempio n. 9
0
def shutdown(dev=False):
    """
    Shutdown Artella Plugin

    :return: True if Artella shutdown was successful; False otherwise.
    :rtype: bool
    """

    try:
        plugins.shutdown(dev=dev)
        dccplugin.DccPlugin().shutdown(dev=dev)
    except Exception as exc:
        pass

    return True
Esempio n. 10
0
def convert_path(file_path):
    """
    Converts given path to a path that Artella can understand

    :param str file_path: File path we want to convert
    :return: str
    :rtype: str
    :example:
    >>> self.translate_path("C:/Users/Bobby/artella-files/ProjectA/Assets/Characters/A/Model/a.ma")
    "$$ART_LOCAL_ROOT/ProjectA/Assets/Characters/A/Model/a.ma"
    >>> self.translate_path("/ProjectA/Assets/Characters/A/Model/a.ma")
    "$ART_LOCAL_ROOT/ProjectA/Assets/Characters/A/Model/a.ma"
    """

    return dccplugin.DccPlugin().convert_path(file_path)
Esempio n. 11
0
def is_path_translated(file_path):
    """
    Returns whether or not given path is already translated to a valid Artella path

    :param file_path: str, path you want to check translation validation
    :return: True if the given path is an already translated Artella path; False otherwise
    :rtype: bool
    :example:
    >>> self.is_path_translated("C:/Users/Bobby/artella-files/ProjectA/Assets/Characters/A/Model/a.ma")
    False
    >>> self.is_path_translated("$ART_LOCAL_ROOT/ProjectA/refs/ref.png")
    True
    """

    return dccplugin.DccPlugin().is_path_translated(file_path)
Esempio n. 12
0
def get_client(check=False, update=False):
    """
    Returns current Artella Drive Client being used by Artella DCC plugin

    :param check: bool, Whether check or not client is available and running
    :param update: bool, Whether or not remote sessions should be updated
    :return: Instance of current Artella Drive Client being used
    :rtype: ArtellaDriveClient
    """

    artella_drive_client = dccplugin.DccPlugin().get_client()
    if check and not artella_drive_client.check(update=update):
        return None
    if not artella_drive_client:
        return None

    return artella_drive_client
Esempio n. 13
0
def make_new_version(file_path=None, comment=None, do_lock=False):
    """
    Uploads a new file/folder or a new version of current opened DCC scene file

    :param str file_path: Optional path of the file we want to create new version of. If not given, current
        opened DCC scene file path will be used.
    :param str comment: Optional comment to add to new version metadata. If not given, a generic message will be
        used.
    :param bool do_lock: Whether or not to force the lock of the file to make a new version. With new Artella
        version this is not mandatory.
    :return: True if the make new version operation is completed successfully; False otherwise.
    :rtype: bool
    """

    return dccplugin.DccPlugin().make_new_version(file_path=file_path,
                                                  comment=comment,
                                                  do_lock=do_lock)
Esempio n. 14
0
def update_paths(file_path=None,
                 show_dialogs=True,
                 call_post_function=True,
                 skip_save=True):
    """
    Updates all file paths of the given file path to make sure that they point to valid Artella file paths

    :param  str or list(str) file_path:
    :param bool show_dialogs:
    :param bool call_post_function:
    :param bool skip_save:
    :return:
    """

    return dccplugin.DccPlugin().update_paths(
        file_path=file_path,
        show_dialogs=show_dialogs,
        call_post_function=call_post_function,
        skip_save=skip_save)
Esempio n. 15
0
def translate_path(file_path):
    """
    Converts a file path to a local file path taking into account the available projects.

    :param str file_path: File path we want to to translate to its user local version
    :return: User local version of the given path
    :rtype: str
    :example:
    >>> self.translate_path("C:/Users/Bobby/artella-files/ProjectA/Assets/Characters/A/Model/a.ma")
    "C:/Users/Tomi/artella/data/ProjectA/Assets/Characters/A/Model/a.ma"
    >>> self.translate_path("/ProjectA/Assets/Characters/A/Model/a.ma")
    "C:/Users/Tomi/artella/data/ProjectA/Assets/Characters/A/Model/a.ma"
    """

    translated_path = dccplugin.DccPlugin().translate_path(file_path)
    if isinstance(translated_path, dict):
        return ''

    return translated_path
Esempio n. 16
0
    def check_for_updates(self, show_dialogs=True):
        """
        Shows UI informing the user if there is available or not a new version of the DCC plugin to download
        """

        from artella.plugins.updater import utils
        from artella.plugins.updater.widgets import versioninfo

        latest_release_info = utils.get_latest_stable_artella_dcc_plugin_info(
            show_dialogs=show_dialogs)
        if not latest_release_info:
            return False

        current_version = dccplugin.DccPlugin().get_version()

        about_dialog = versioninfo.VersionInfoDialog(
            current_version=current_version,
            latest_release_info=latest_release_info)
        about_dialog.exec_()

        return True
Esempio n. 17
0
def init(dcc_paths=None,
         init_client=True,
         plugin_paths=None,
         extensions=None,
         dev=False,
         load_plugins=True,
         create_menu=True,
         create_callbacks=True):
    """
    Initializes Artella Plugin

    :param bool init_client: Whether or not Artella Drive Client should be initialized during initialization.
        Useful to avoid to connect to Artella client when developing DCC specific functionality.
    :param list(str) plugin_paths: List of paths where Artella plugins can be located
    :param list(str) extensions: List of extensions to register
    :param bool dev: Whether or not initialization should be done in dev mode
    :param bool load_plugins: Whether or not Artella Plugins should be loaded
    :param bool create_menu: Whether or not Artella menu should be created
    :param bool create_callbacks: Whether or not Artella DCC plugin callbacks should be created
    :return: True if Artella initialization was successful; False otherwise.
    :rtype: bool
    """

    register_dcc_paths(dcc_paths)

    plugins_path = plugin_paths if plugin_paths is not None else list()
    extensions = extensions if extensions is not None else list()

    artella_logger = logging.getLogger('artella')

    # Make sure that Artella Drive client and DCC are cached during initialization
    current_dcc = dcc_core.current_dcc()
    if not current_dcc:
        artella_logger.error(
            'Impossible to load Artella Plugin because no DCC is available!')
        return False

    # Due to the TCP server, Artella plugin freezes some DCCs (such as Maya) if its execute in batch mode (through
    # console). For now we skip Artella plugin initialization in that scenario.
    if dcc.is_batch():
        return False

    shutdown(dev=dev)

    # Specific DCC extensions are managed by the client
    dcc_extensions = dcc.extensions() or list()
    extensions.extend(dcc_extensions)

    # Initialize resources and theme
    resource.register_resources_path(
        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources'))

    # Create Artella Drive Client
    artella_drive_client = client.ArtellaDriveClient.get(
        extensions=extensions) if init_client else None

    # Load Plugins
    if load_plugins:
        default_plugins_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'plugins')
        if default_plugins_path not in plugins_path:
            plugins_path.append(default_plugins_path)
        plugins.register_paths(plugin_paths)
        plugins.load_registered_plugins(dev=dev)

    # Initialize Artella DCC plugin
    dccplugin.DccPlugin(artella_drive_client).init(
        dev=dev,
        show_dialogs=False,
        create_menu=create_menu,
        create_callbacks=create_callbacks,
        init_client=init_client)

    if not dev:
        updater_plugin = plugins.get_plugin_by_id('artella-plugins-updater')
        if updater_plugin and updater_plugin.update_is_available(
                show_dialogs=False):
            updater_plugin.check_for_updates(show_dialogs=False)

    return True
Esempio n. 18
0
    def uninstall(self, show_dialogs=True):
        artella_path = artella.__path__[0]
        if not os.path.isdir(artella_path):
            msg = 'Artella folder "{}" does not exists!'.format(artella_path)
            if show_dialogs:
                api.show_warning_message(text=msg)
            else:
                logger.warning(msg)
            return False

        res = qtutils.show_question_message_box(
            'Artella Uninstaller',
            'All plugins will be removed.\n\nArtella plugin will not be accessible by any DCC after uninstall.\n\n'
            'Are you sure you want to uninstall Artella Plugin?')
        if not res:
            return False

        do_remove_install_folder = not dccplugin.DccPlugin().dev

        valid_uninstall = self._uninstall(artella_path)
        if not valid_uninstall:
            msg = 'Artella uninstall process was not completed!'.format(artella_path)
            if show_dialogs:
                api.show_error_message(text=msg)
            else:
                logger.error(msg)
            return False

        loader.shutdown(dev=False)

        if do_remove_install_folder:
            try:
                logger.info('Removing Artella Dcc Plugin directory: {}'.format(artella_path))
                utils.delete_folder(artella_path)
            except Exception as exc:
                logger.warning(
                    'Impossible to remove Artella Dcc plugin directory: {} | {}'.format(artella_path, exc))
                return False

        if os.path.isdir(artella_path):
            msg = 'Artella folder was not removed during uninstall process.\n\n{}\n\n Remove it manually if you ' \
                  'want to have a complete clean uninstall of Artella plugin.'.format(artella_path)
            if show_dialogs:
                dcc.show_info('Artella Uninstaller', msg)
            else:
                logger.info(msg)
            utils.open_folder(os.path.dirname(artella_path))

        # Remove specific DCC install folder if exists
        root_dcc_install_dir = os.path.dirname(os.path.dirname(os.path.dirname(artella_path)))
        dcc_install_dir = os.path.join(root_dcc_install_dir, dcc.name())
        if os.path.isdir(dcc_install_dir):
            utils.delete_folder(dcc_install_dir)

        # Cleanup artella directories from
        artella_dir = os.path.dirname(artella_path)
        sys_paths = [artella_path, artella_dir, utils.clean_path(artella_path), utils.clean_path(artella_dir)]
        paths_to_remove = list()
        for sys_path in sys.path:
            if sys_path in sys_paths:
                paths_to_remove.append(sys_path)
                sys.path.remove(sys_path)
            elif 'artella-plugins' in sys_path or 'artella-dccs' in sys_path:
                paths_to_remove.append(sys_path)
        for path_to_remove in paths_to_remove:
            if path_to_remove not in sys.path:
                continue
            sys.path.remove(path_to_remove)

        return True