Example #1
0
    def test_check_docs(self):

        _example = abs_path('example.py', root=os.path.dirname(__file__))
        _py_file = PyFile(_example)
        with self.assertRaises(MissingDocs):
            _py_file.find_def('missing_docs').check_docs()
        with self.assertRaises(MissingDocs):
            _py_file.find_def('missing_docs_args').check_docs()
        with self.assertRaises(MissingDocs):
            _py_file.find_def('missing_period').check_docs()
Example #2
0
    def test(self):

        _example = abs_path('example.py', root=os.path.dirname(__file__))
        _py_file = PyFile(_example)
        assert _py_file.find_defs()
        assert _py_file.find_def('test')
        assert _py_file.find_def('test2')

        # Test docs
        _docs = _py_file.find_def('docs_example').get_docs()
        assert _docs.header == 'Header.'
        assert _docs.exc_type == 'ValueError'
        assert _docs.exc_desc == 'cause with new line'
        assert _docs.result_type == 'bool'
        assert _docs.result_desc == 'result with new line'
        assert len(_docs.args) == 2
        assert _docs.args[0].name == 'arg1'
        assert _docs.args[0].type_ == 'bool'
        assert _docs.args[0].desc == 'first arg with new line'
        assert _docs.args[1].name == 'arg2'
        assert _docs.args[1].type_ == 'str'
        assert _docs.args[1].desc == 'second arg'
        assert _docs.desc == 'Description - this is\na multi line.'
Example #3
0
    def __init__(self,
                 path,
                 title=None,
                 all_defs=False,
                 base_col=None,
                 mod=None,
                 verbose=0):
        """Constructor.

        Args:
            path (str): path to build interface from
            title (str): override gui title
            all_defs (bool): force all defs into interface (by default
                only defs decorated with the py_gui.install decorator
                are added)
            base_col (QColor|str): override base colour for this interface
            mod (module): py file module (to avoid reimport/calculate)
            verbose (int): print process data
        """

        # Store kwargs for rebuild
        self._kwargs = copy.copy(locals())
        self._kwargs.pop('self')

        self.py_file = PyFile(path)
        self.label_width = 70
        self._height = 400
        self.all_defs = all_defs
        self.section = None

        _mod = mod or self.py_file.get_module()

        self.mod_name = _mod.__name__
        self.title = title or getattr(_mod, 'PYGUI_TITLE', self.mod_name)
        self.ui_name = self.mod_name.replace(".", "_") + "_ui"
        self.settings_file = abs_path('{}/Psyop/settings/py_gui/{}.yml'.format(
            os.environ.get('HOME') or os.environ.get('HOMEDRIVE'),
            self.mod_name.replace('.', '_')))

        # Read attrs from module
        self.icon_set = getattr(_mod, 'PYGUI_ICON_SET', icons.FRUIT)
        self._width = getattr(_mod, 'PYGUI_WIDTH', 300)
        self.base_col = base_col or getattr(
            _mod, 'PYGUI_COL',
            str_to_seed(self.mod_name).choice(NICE_COLS))
        self.section_col = qt.HColor(self.base_col).blacken(0.5)
        if self.icon_set:
            assert isinstance(self.icon_set, Collection)

        # Build defs into ui
        self.read_settings_fns = copy.deepcopy(_EMPTY_SETTINGS)
        self.set_settings_fns = copy.deepcopy(_EMPTY_SETTINGS)
        _defs_data = self._get_defs_data()
        self.init_ui()
        lprint('FOUND {:d} DEFS TO ADD'.format(len(_defs_data)),
               verbose=verbose)
        for _last, (_fn, _opts) in last(_defs_data):
            _def = self.py_file.find_def(_fn.__name__, catch=True)
            lprint(" - TESTING DEF", _fn, _opts, _def, verbose=verbose)
            if _def:
                lprint(" - ADDING DEF", _def, _opts, verbose=verbose)
                self.add_def(_def, opts=_opts, last_=_last)
        self.finalise_ui()
        if os.path.exists(self.settings_file):
            self.load_settings()
Example #4
0
class BasePyGui(object):
    """Base class for any py_gui interface."""
    def __init__(self,
                 path,
                 title=None,
                 all_defs=False,
                 base_col=None,
                 mod=None,
                 verbose=0):
        """Constructor.

        Args:
            path (str): path to build interface from
            title (str): override gui title
            all_defs (bool): force all defs into interface (by default
                only defs decorated with the py_gui.install decorator
                are added)
            base_col (QColor|str): override base colour for this interface
            mod (module): py file module (to avoid reimport/calculate)
            verbose (int): print process data
        """

        # Store kwargs for rebuild
        self._kwargs = copy.copy(locals())
        self._kwargs.pop('self')

        self.py_file = PyFile(path)
        self.label_width = 70
        self._height = 400
        self.all_defs = all_defs
        self.section = None

        _mod = mod or self.py_file.get_module()

        self.mod_name = _mod.__name__
        self.title = title or getattr(_mod, 'PYGUI_TITLE', self.mod_name)
        self.ui_name = self.mod_name.replace(".", "_") + "_ui"
        self.settings_file = abs_path('{}/Psyop/settings/py_gui/{}.yml'.format(
            os.environ.get('HOME') or os.environ.get('HOMEDRIVE'),
            self.mod_name.replace('.', '_')))

        # Read attrs from module
        self.icon_set = getattr(_mod, 'PYGUI_ICON_SET', icons.FRUIT)
        self._width = getattr(_mod, 'PYGUI_WIDTH', 300)
        self.base_col = base_col or getattr(
            _mod, 'PYGUI_COL',
            str_to_seed(self.mod_name).choice(NICE_COLS))
        self.section_col = qt.HColor(self.base_col).blacken(0.5)
        if self.icon_set:
            assert isinstance(self.icon_set, Collection)

        # Build defs into ui
        self.read_settings_fns = copy.deepcopy(_EMPTY_SETTINGS)
        self.set_settings_fns = copy.deepcopy(_EMPTY_SETTINGS)
        _defs_data = self._get_defs_data()
        self.init_ui()
        lprint('FOUND {:d} DEFS TO ADD'.format(len(_defs_data)),
               verbose=verbose)
        for _last, (_fn, _opts) in last(_defs_data):
            _def = self.py_file.find_def(_fn.__name__, catch=True)
            lprint(" - TESTING DEF", _fn, _opts, _def, verbose=verbose)
            if _def:
                lprint(" - ADDING DEF", _def, _opts, verbose=verbose)
                self.add_def(_def, opts=_opts, last_=_last)
        self.finalise_ui()
        if os.path.exists(self.settings_file):
            self.load_settings()

    def init_ui(self, rebuild_fn=None, verbose=1):
        """Initiate ui.

        Args:
            rebuild_fn (func): override rebuild function
            verbose (int): print process data
        """
        dprint('Building ui {} ({})'.format(self.ui_name, self.base_col),
               verbose=verbose)

        # Add menu bar
        _interface = self.add_menu('Interface')
        self.add_menu_item(_interface,
                           label='Collapse all',
                           image=icons.EMOJI.find('Sponge', catch=True),
                           command=self.collapse_all)
        self.add_menu_item(_interface,
                           label='Rebuild',
                           image=icons.EMOJI.find('Hammer', catch=True),
                           command=rebuild_fn or self.rebuild)

        _settings = self.add_menu('Settings')
        self.add_menu_item(_settings,
                           label='Save',
                           image=icons.EMOJI.find('Floppy disk', catch=True),
                           command=self.save_settings)
        self.add_menu_item(_settings,
                           label='Reset',
                           image=icons.EMOJI.find('Shower', catch=True),
                           command=wrap_fn(
                               self.reset_settings))  # Wrap to discard args
        self._save_on_close = self.add_menu_item(_settings,
                                                 label='Save on close',
                                                 checkbox=False)

    def add_menu(self, name):
        """Add menu to interface.

        Args:
            name (str): menu name
        """

    def add_menu_item(self,
                      parent,
                      label,
                      command=None,
                      image=None,
                      checkbox=None):
        """Add menu item to interface.

        Args:
            parent (any): parent menu
            label (str): label for menu item
            command (func): item command
            image (str): path to item image
            checkbox (bool): item as checkbox (with this state)
        """

    def add_arg(self,
                arg,
                default,
                label=None,
                choices=None,
                label_width=None,
                update=None,
                browser=None,
                verbose=0):
        """Add an arg to the interface.

        Args:
            arg (PyArg): arg to add
            default (any): default value for arg
            label (str): override arg label
            choices (dict): list of options to show in the interface
            label_width (int): label width in pixels
            update (ArgUpdater): updater for this arg
            browser (BrowserLauncher): add launch browser button
            verbose (int): print process data
        """

    def add_def(self, def_, opts, last_, verbose=0):
        """Add a def to the interface.

        The opts dict will be empty in the case of adding all defs
        (ie. not using the install_gui decorator) - so the defaults
        here need to be applied.

        Args:
            def_ (PyDef): def to add
            opts (dict): display options
            last_ (bool): whether this is last def in interface
            verbose (int): print process data
        """
        _update = opts.get('update') or {}
        _choices = opts.get('choices') or {}
        _hide = opts.get('hide') or []
        _disable_reload = opts.get('disable_reload') or False
        _section = opts.get('section')
        _catch_error = opts.get('catch_error_', True)

        # If browser if list convert to default dict
        _browser = opts.get('browser') or {}
        if isinstance(_browser, list):
            _browser_dict = {}
            for _arg_name in _browser:
                print 'FIND ARG', _arg_name
                _arg = def_.find_arg(_arg_name)
                print 'ARG', _arg
                _browser_dict[_arg_name] = pyg_install.BrowserLauncher(
                    default_dir=_arg.default)
            _browser = _browser_dict

        if _section:
            self.set_section(_section)

        self.read_settings_fns['def'][def_.name] = {}
        self.set_settings_fns['def'][def_.name] = {}

        # Add args
        for _arg in def_.find_args():

            if _hide == '*' or _arg.name in _hide:
                continue
            lprint('  ADDING ARG', _arg, verbose=verbose)

            # Check for update fn
            _default = _arg.default
            _arg_choices = _choices.get(_arg.name)
            _arg_update = _update.get(_arg.name)
            if _arg_update and not isinstance(_arg_update,
                                              pyg_install.ArgUpdater):
                # Convert function to updater
                _arg_update = pyg_install.ArgUpdater(get_choices=_arg_update)
            if _arg_update:
                _arg_choices = _arg_update.get_choices() or _arg_choices
                _default = _arg_update.get_default() or _default

            _read_fn, _set_fn = self.add_arg(
                _arg,
                default=_default,
                label=to_nice(_arg.name),
                choices=_arg_choices,
                label_width=opts.get('label_width') or self.label_width,
                update=_arg_update,
                browser=_browser.get(_arg.name))
            self.read_settings_fns['def'][def_.name][_arg.name] = _read_fn
            self.set_settings_fns['def'][def_.name][_arg.name] = _set_fn

        # Add execute
        _icon = opts.get('icon') or get_def_icon(def_.name, set_=self.icon_set)
        _label = opts.get('label') or to_nice(def_.name)
        _col = opts.get('col') or self.base_col
        _exec_fn = get_exec_fn(
            def_=def_,
            read_arg_fns=self.read_settings_fns['def'][def_.name],
            disable_reload=_disable_reload,
            catch_error_=True,
            interface=self)
        _help_fn = get_help_fn(def_)
        _code_fn = get_code_fn(def_)
        self.add_execute(def_=def_,
                         exec_fn=_exec_fn,
                         help_fn=_help_fn,
                         icon=_icon,
                         label=_label,
                         col=_col,
                         code_fn=_code_fn)

        if not last_:
            self.add_separator()

    def add_execute(self,
                    def_,
                    exec_fn,
                    code_fn,
                    help_fn,
                    depth=35,
                    icon=None,
                    label=None,
                    col=None):
        """Add execute button for the given def.

        Args:
            def_ (PyDef): def being added
            exec_fn (fn): function to call on execute
            code_fn (fn): function to call on jump to code
            help_fn (fn): function to call on launch help
            depth (int): size in pixels of def
            icon (str): path to icon to display
            label (str): override label from exec button
            col (str): colour for button
        """
        raise NotImplementedError

    def add_separator(self):
        """Add a separator to the inteface."""

    def finalise_ui(self):
        """Finalise ui (implemented in subclass)."""

    def close_event(self, verbose=0):
        """Executed on close.

        Args:
            verbose (int): print process data
        """
        print 'CLOSING', self

    def _get_defs_data(self):
        """Get list of defs to add to interface.

        Returns:
            (fn/opts list): list of functions and options
        """

        # Get list of defs to add
        _defs_data, _sections, _hidden = pyg_install.get_installed_data(
            self.py_file)
        _mod = self.py_file.get_module()

        if not self.all_defs:
            return _defs_data

        return _read_all_defs(py_file=self.py_file,
                              mod=_mod,
                              defs_data=_defs_data,
                              sections=_sections,
                              hidden=_hidden)

    def load_settings(self, verbose=0):
        """Load settings from disk.

        Args:
            verbose (int): print process data
        """
        _settings = read_yaml(self.settings_file)
        lprint('LOADING', _settings, verbose=verbose)
        for _attr, _attr_settings in _settings.items():
            lprint('APPLING', _attr, verbose=verbose)
            for _name, _settings in _attr_settings.items():
                lprint(' - NAME', _name, verbose=verbose)
                for _arg_name, _val in _settings.items():

                    # Find set fn
                    _set_fns = self.set_settings_fns
                    try:
                        _set_fn = _set_fns[_attr][_name][_arg_name]
                    except KeyError:
                        _set_fn = None

                    # Apply value
                    _applied = False
                    if _set_fn:
                        lprint('   - APPLYING',
                               _arg_name,
                               _val,
                               _set_fn,
                               verbose=verbose)
                        try:
                            _set_fn(_val)
                        except TypeError:
                            continue
                        else:
                            _applied = True
                    if not _applied:
                        lprint('   - FAILED TO APPLY', _arg_name, _val)

        dprint('Loaded settings', self.settings_file)

    def _read_settings(self, verbose=0):
        """Read current settings from interface.

        Args:
            verbose (int): print process data

        Returns:
            (dict): current settings
        """
        _settings = copy.deepcopy(_EMPTY_SETTINGS)
        for _attr in ['def', 'section', 'window']:
            _items = self.read_settings_fns[_attr].items()
            lprint('READING', _attr, _items, verbose=verbose)
            for _name, _read_settings_fns in _items:
                lprint(' - READING',
                       _name,
                       _read_settings_fns,
                       verbose=verbose)
                _settings[_attr][_name] = {}
                for _arg, _arg_fn in _read_settings_fns.items():
                    _val = _arg_fn()
                    if isinstance(_val, six.string_types):
                        _val = str(_val)
                    _settings[_attr][_name][_arg] = _val
        return _settings

    def collapse_all(self, *xargs):
        """Collapse all sections."""
        del xargs
        for _section in self.set_settings_fns['section'].values():
            _section['collapse'](True)

    def rebuild(self):
        """Rebuild this interface."""
        self.save_settings()
        _class_name = self.__class__.__name__
        _mod_name = self.__class__.__module__
        _class = getattr(sys.modules[_mod_name], _class_name)
        _class(**self._kwargs)

    def reset_settings(self, def_=None):
        """Reset current settings to defaults.

        Args:
            def_ (PyDef): only reset this def
        """
        print 'RESETTING SETTINGS'
        _sections = set()
        for _fn, _data in self._get_defs_data():

            _py_def = self.py_file.find_def(_fn.__name__)
            if def_ and not def_ == _py_def:
                continue
            print ' - ADDING', _fn, _data
            _set_fns = self.set_settings_fns['def'][_py_def.name]
            for _py_arg in _py_def.find_args():
                if _py_arg.name not in _set_fns:
                    print ' - SETTING MISSING', _py_arg
                    continue
                print '   - SETTING', _py_arg, _py_arg.default
                _set_fn = _set_fns[_py_arg.name]
                _set_fn(_py_arg.default)

            _section = _data.get('section')
            if _section:
                print ' - SECTION', _section
                _sections.add(_section)

        if not def_:
            print 'SECTIONS', _sections
            for _section in _sections:
                print ' - ADDING', _section, _section.collapse
                _set_fn = self.set_settings_fns['section'][
                    _section.label]['collapse']
                _set_fn(_section.collapse)

    def save_settings(self, verbose=0):
        """Save current settings to disk.

        Args:
            verbose (int): print process data
        """
        _settings = self._read_settings()
        if verbose > 1:
            pprint.pprint(_settings)
        dprint('Saved settings', self.settings_file, verbose=verbose)
        write_yaml(file_=self.settings_file, data=_settings, force=True)

    def set_section(self, section, verbose=0):
        """Set current section (implemented in subclass).
Example #5
0
def _ph_add_show_toolkits(parent, verbose=0):
    """Add show toolkits options.

    Args:
        parent (str): parent menu
        verbose (int): print process data
    """
    _shows = cmds.menuItem(label='Shows',
                           parent=parent,
                           subMenu=True,
                           image=icons.EMOJI.find('Top Hat', catch=True))

    _shows_dir = File(shows.__file__).parent()

    # Get list of toolkits
    _toolkits = []
    for _py in _shows_dir.find(extn='py', depth=1, type_='f'):
        _file = PyFile(_py)
        if _file.basename.startswith('_'):
            continue
        try:
            _mod = _file.get_module(catch=True)
        except ImportError:
            continue
        if not _mod:
            continue
        _toolkits.append((_file, _file.basename))
    for _dir in _shows_dir.find(depth=1, type_='d'):
        _toolkit = PyFile('{}/toolkit.py'.format(_dir))
        if Dir(_dir).filename.startswith('_'):
            continue
        lprint(' - ADDING DIR', _dir, verbose=verbose)
        try:
            _mod = _toolkit.get_module(catch=True)
        except ImportError:
            continue
        if not _mod:
            continue
        _name = _toolkit.parent().filename
        _toolkits.append((_toolkit, _name))
    _toolkits.sort(key=operator.itemgetter(1))
    lprint('FOUND TOOLKITS', verbose=verbose)

    # Build show toolkit buttons
    for _toolkit, _name in _toolkits:
        lprint(' - ADDING TOOLKIT', _name, verbose=verbose)
        _mod = _toolkit.get_module()
        _rand = str_to_seed(_name)
        _icon = getattr(_mod, 'ICON', _rand.choice(icons.ANIMALS))
        _label = getattr(_mod, 'LABEL', to_nice(_name))
        _title = '{} tools'.format(_label)
        _cmd = '\n'.join([
            'import {py_gui} as py_gui',
            '_path = "{file}"',
            '_title = "{title}"',
            'py_gui.MayaPyGui(_path, title=_title, all_defs=True)',
        ]).format(py_gui=py_gui.__name__, file=_toolkit.path, title=_title)
        cmds.menuItem(command=_cmd, image=_icon, label=_label, parent=_shows)

        _btn_label = getattr(_mod, 'BUTTON_LABEL', _label)
        _btn = _add_psyhive_btn(label=_btn_label,
                                cmd=None,
                                icon=_icon,
                                tooltip=_title)
        py_gui.MayaPyShelfButton(mod=_mod,
                                 parent='PsyHive',
                                 image=_icon,
                                 label=_label,
                                 button=_btn)
Example #6
0
 def _callback__DisableViewportUpdatesHelp(self):
     _def = PyFile(yeti_viewport.__file__).find_def(
         disable_viewport_updates.__name__)
     qt.help_(_def.docs.split('Args:')[0].strip(), parent=self)
Example #7
0
 def _callback__CacheUpdateAllHelp(self):
     _def = PyFile(yeti_read.__file__).find_def(
         yeti_read.update_all.__name__)
     qt.help_(_def.docs.split('Args:')[0].strip(), parent=self)
Example #8
0
 def _callback__CacheReadRootHelp(self):
     _def = PyFile(yeti_read.__file__).find_def(
         apply_caches_in_root_namespace.__name__)
     qt.help_(_def.docs.split('Args:')[0].strip(), parent=self)
Example #9
0
 def _callback__CacheReadAssetHelp(self):
     _def = PyFile(yeti_read.__file__).find_def(
         apply_caches_to_sel_asset.__name__)
     qt.help_(_def.docs.split('Args:')[0].strip(), parent=self)
Example #10
0
 def _callback__CacheWriteAllNodesHelp(self):
     _def = PyFile(yeti_write.__file__).find_def(
         write_cache_from_all_yetis.__name__)
     qt.help_(_def.docs.split('Args:')[0].strip(), parent=self)
Example #11
0
 def _code_fn(*args):
     del args
     print 'SEARCHING FOR', def_.name, 'IN', def_.py_file
     _py_file = PyFile(def_.py_file.path)
     _py_file.find_def(def_.name).edit()