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()
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.'
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()
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).
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)
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)
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)
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)
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)
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)
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()