class CSFBaseSliderLoader(CommonService, HasLog): """ Loads Sliders. """ # noinspection PyMissingOrEmptyDocstring @property def mod_identity(self) -> CommonModIdentity: return ModInfo.get_identity() # noinspection PyMissingOrEmptyDocstring @property def log_identifier(self) -> str: return 'csf_slider_loader' @property def snippet_names(self) -> Tuple[str]: """ The names of snippets containing sliders. """ raise NotImplementedError() @CommonExceptionHandler.catch_exceptions(ModInfo.get_identity(), fallback_return=tuple()) def load(self) -> Iterator[CSFSlider]: """load() Loads all Sliders. :return: An iterable of Sliders. :rtype: Iterator[CSFSlider] """ snippet_names: Tuple[str] = self.snippet_names for snippet_package in CommonResourceUtils.load_instances_with_any_tags( Types.SNIPPET, snippet_names): try: sliders: Tuple[CSFSlider] = tuple(self._load(snippet_package)) for slider in sliders: slider: CSFSlider = slider if slider is None: continue (is_valid_result, is_valid_reason) = slider.is_valid() if is_valid_result: yield slider except Exception as ex: self.log.format_error( 'Error while parsing sliders from \'{}\''.format( snippet_package), exception=ex) def _load(self, package_slider: Any) -> Tuple[CSFSlider]: raise NotImplementedError()
def open(self, sim_info: SimInfo, page: int = 1) -> None: """ Open the dialog. """ self.log.format_with_message( 'Opening customize sliders dialog for Sim.', sim=sim_info) def _on_close() -> None: self.log.debug('Customize Slider dialog closed.') if self._on_close is not None: self._on_close() option_dialog = CommonChooseObjectOptionDialog( CSFStringId.CUSTOMIZE_SLIDERS, CSFStringId.CHOOSE_SLIDERS_TO_MODIFY, mod_identity=self.mod_identity, on_close=_on_close, per_page=400) def _reopen_dialog() -> None: self.log.debug('Reopening customize sliders dialog.') self.open(sim_info, page=option_dialog.current_page) option_dialog.add_option( CommonDialogActionOption( CommonDialogOptionContext( CSFStringId.SLIDER_TEMPLATES_NAME, CSFStringId.SLIDER_TEMPLATES_DESCRIPTION, tag_list=[ slider_category.name for slider_category in CSFSliderCategory.values ]), on_chosen=lambda *_, **__: CSFSliderTemplateDialog( on_close=_reopen_dialog).open(sim_info), always_visible=True)) def _on_reset_all_sliders() -> None: self.log.debug('Confirming all sliders reset.') def _on_confirm(_) -> None: self.log.debug('Resetting all sliders.') self.slider_application_service.reset_all_sliders(sim_info) _reopen_dialog() def _on_cancel(_) -> None: self.log.debug('Cancelled resetting of all sliders.') _reopen_dialog() CommonOkCancelDialog( CSFStringId.CONFIRMATION, CSFStringId.ARE_YOU_SURE_YOU_WANT_TO_RESET_ALL_SLIDERS, mod_identity=self.mod_identity).show( on_ok_selected=_on_confirm, on_cancel_selected=_on_cancel) def _on_slider_changed(slider_unique_identifier: str, amount: float, outcome: CommonChoiceOutcome): if slider_unique_identifier is None or amount is None or CommonChoiceOutcome.is_error_or_cancel( outcome): self.log.debug( 'No slider chosen, dialog closed, or no amount specified.') _reopen_dialog() return self.log.debug('Slider changed, attempting to apply.') self.slider_application_service.apply_slider_by_identifier( sim_info, slider_unique_identifier, amount) _reopen_dialog() self.log.debug('Opening Customize Slider dialog.') option_dialog.add_option( CommonDialogActionOption( CommonDialogOptionContext( CSFStringId.RESET_ALL_SLIDERS_NAME, CSFStringId.RESET_ALL_SLIDERS_DESCRIPTION, tag_list=[ slider_category.name for slider_category in CSFSliderCategory.values ]), on_chosen=lambda *_, **__: _on_reset_all_sliders(), always_visible=True)) sliders: Tuple[ CSFSlider] = self._slider_query_utils.get_sliders_for_sim(sim_info) if not sliders: from cncustomsliderframework.sliders.slider_query_registry import CSFSliderQueryRegistry if CSFSliderQueryRegistry()._collecting: CommonOkDialog( CSFStringId.SLIDERS_ARE_STILL_LOADING, CSFStringId.SLIDERS_ARE_STILL_LOADING_DESCRIPTION, description_tokens=( CSFStringId.FINISHED_LOADING_SLIDERS, ), mod_identity=ModInfo.get_identity()).show() else: CommonOkDialog(CSFStringId.NO_SLIDERS_FOUND, CSFStringId.NO_SLIDERS_FOUND, mod_identity=ModInfo.get_identity()).show() _on_close() return self.log.debug('Adding slider count {}'.format(len(sliders))) sorted_sliders = sorted(sliders, key=lambda s: s.name) slider_categories: List[CSFSliderCategory] = list() object_categories: List[str] = list() for custom_slider in sorted_sliders: for slider_category in custom_slider.categories: if slider_category.name in object_categories: continue slider_categories.append(slider_category) object_categories.append(slider_category.name) def _on_randomize_slider_category(category_name: str, category: CSFSliderCategory): self.log.debug( 'Confirming reset of sliders in category {}.'.format( category_name)) def _on_confirm(_) -> None: self.log.debug( 'Randomizing all sliders in category {}.'.format( category_name)) for slider in sorted_sliders: if category not in slider.categories: continue self.slider_application_service.apply_random( sim_info, slider) _reopen_dialog() def _on_cancel(_) -> None: self.log.debug( 'Cancelled randomization of sliders in category {}.'. format(category_name)) _reopen_dialog() CommonOkCancelDialog(CSFStringId.CONFIRMATION, CSFStringId.RANDOMIZE_SLIDER_CONFIRMATION, mod_identity=self.mod_identity).show( on_ok_selected=_on_confirm, on_cancel_selected=_on_cancel) for slider_category in slider_categories: option_dialog.add_option( CommonDialogSelectOption( slider_category.name, slider_category, CommonDialogOptionContext( CSFStringId.RANDOMIZE_SLIDER_NAME, CSFStringId.RANDOMIZE_SLIDER_DESCRIPTION, title_tokens=(slider_category.name, ), description_tokens=(slider_category.name, ), tag_list=[ slider_category.name for slider_category in CSFSliderCategory.values ]), on_chosen=_on_randomize_slider_category, always_visible=True)) for custom_slider in sorted_sliders: if custom_slider.description is not None: # noinspection PyTypeChecker option_description = CommonLocalizationUtils.create_localized_string( custom_slider.description, tokens=(str(0.0), str(custom_slider.minimum_value), str(custom_slider.maximum_value))) else: # noinspection PyTypeChecker option_description = CommonLocalizationUtils.create_localized_string( CSFStringId.CHANGE_THE_SLIDER, tokens=(custom_slider.display_name, )) option_dialog.add_option( CommonDialogInputFloatOption( custom_slider.unique_identifier, self.slider_application_service.get_current_slider_value( sim_info, custom_slider), CommonDialogOptionContext( custom_slider.display_name, option_description, icon=custom_slider.icon_id or None, tag_list=tuple([ category.name for category in custom_slider.categories ])), min_value=custom_slider.minimum_value, max_value=custom_slider.maximum_value, on_chosen=_on_slider_changed, dialog_description_identifier=CSFStringId.DEFAULT_MIN_MAX, dialog_description_tokens=( option_description, str(0.0), str(custom_slider.minimum_value), str(custom_slider.maximum_value)))) categories: List[CommonDialogObjectOptionCategory] = list() for category in object_categories: # noinspection PyTypeChecker categories.append( CommonDialogObjectOptionCategory( category, icon=CommonIconId.S4CLIB_UNFILLED_CIRCLE_ICON)) self.log.debug('Showing slider options.') option_dialog.show(sim_info=sim_info, page=page, categories=categories)
def mod_identity(self) -> CommonModIdentity: return ModInfo.get_identity()
def get_mod_identity(cls) -> CommonModIdentity: return ModInfo.get_identity()
This file is part of the Custom Slider Framework licensed under the Creative Commons Attribution-NoDerivatives 4.0 International public license (CC BY-ND 4.0). https://creativecommons.org/licenses/by-nd/4.0/ https://creativecommons.org/licenses/by-nd/4.0/legalcode Copyright (c) COLONOLNUTTY """ from typing import Any, Dict, Set from cncustomsliderframework.dtos.sliders.slider import CSFSlider from cncustomsliderframework.modinfo import ModInfo from cncustomsliderframework.sliders.slider_query_registry import CSFSliderQueryRegistry from sims4.commands import Command, CommandType, CheatOutput from sims4communitylib.utils.common_log_registry import CommonLogRegistry log = CommonLogRegistry().register_log(ModInfo.get_identity(), 'csf_slider_query') @Command('csf.log_slider_tags', command_type=CommandType.Live) def _csf_command_log_slider_tags(_connection: int = None): output = CheatOutput(_connection) output( 'Logging Slider tags, this will take awhile and your game may freeze. Be Patient!' ) try: log.enable() slider_results = [] for slider_tag_value in CSFSliderQueryRegistry()._slider_library.keys( ): sliders = CSFSliderQueryRegistry(
class CSFSliderQueryRegistry(CommonService, HasLog): """ Registry handling slider queries. """ # noinspection PyMissingOrEmptyDocstring @property def mod_identity(self) -> CommonModIdentity: return ModInfo.get_identity() # noinspection PyMissingOrEmptyDocstring @property def log_identifier(self) -> str: return 'csf_slider_query_registry' @property def _tag_handlers(self) -> List[CSFSliderTagHandler]: return self.__tag_handlers @property def slider_library(self) -> Dict[Tuple[CSFSliderTagType, Any], Set[str]]: """ A library of sliders organized by filter keys. """ return self._slider_library @slider_library.setter def slider_library(self, value: Dict[Tuple[CSFSliderTagType, Any], Set[str]]): self._slider_library = value def __init__(self) -> None: from cncustomsliderframework.sliders.slider_registry import CSFSliderRegistry super().__init__() self._collecting = False self.slider_library = collections.defaultdict(set) self.__tag_handlers: List[CSFSliderTagHandler] = list() self._all: List[CSFSlider] = list() self._registry = CSFSliderRegistry() def add_tag_handler(self, tag_handler_init: Callable[[CSFSliderTagType], CSFSliderTagHandler], tag: CSFSliderTagType): """ Add a tag handler. """ self.__tag_handlers.append(tag_handler_init(tag)) def create_query( self, slider_filters: Tuple[CSFSliderTagFilter], query_type: CSFQueryType = CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE ) -> CSFSliderQuery: """ Create a query for sliders. """ return CSFSliderQuery(slider_filters, query_type=query_type) def has_sliders(self, queries: Tuple[CSFSliderQuery]) -> bool: """ Determine if sliders are available for tags. """ self.log.format_with_message('Checking if has sliders', queries=queries) for _ in self.get_sliders(queries): return True return False def get_all_sliders(self) -> Tuple[CSFSlider]: """ Get all sliders. """ if self._collecting: return tuple() return tuple(self._all) def get_sliders(self, queries: Tuple[CSFSliderQuery]) -> Set[CSFSlider]: """ Retrieve sliders matching the queries. """ self.log.format_with_message('Getting sliders', queries=queries) if self._collecting: return set() sliders = set() for query in queries: found_sliders = self._query_sliders(query) if found_sliders: sliders = sliders | found_sliders if verbose_log.enabled: verbose_log.debug('Finished locating sliders [{}]'.format( ',\n'.join([ '{}:{}'.format(str(slider.raw_display_name), slider.author) for slider in sliders ]))) return sliders def _query_sliders(self, query: CSFSliderQuery) -> Set[CSFSlider]: self.log.format_with_message( 'Querying for sliders using query: {}'.format(query)) all_tags = query.include_all_tags any_tags = query.include_any_tags exclude_tags = query.exclude_tags found_slider_identifiers = None for include_all_tag in all_tags: if include_all_tag is None: continue if include_all_tag.key not in self.slider_library: # One of the All keys is not within the slider library! This means no sliders match ALL tags. return set() new_found_sliders = self.slider_library[include_all_tag.key] if found_slider_identifiers is not None: self.log.debug('Looking for tag {}'.format(include_all_tag)) self.log.debug('Before intersect for all_tags {}'.format( len(found_slider_identifiers))) new_found_sliders = found_slider_identifiers & new_found_sliders self.log.debug('After intersect for all_tags {}'.format( len(new_found_sliders))) else: self.log.debug('Found with all_tags {}'.format( len(new_found_sliders))) found_slider_identifiers = new_found_sliders if found_slider_identifiers is None and all_tags: self.log.debug('No sliders found for all_tags {}'.format(all_tags)) return set() self.log.debug('After all_tags {}'.format( len(found_slider_identifiers))) if verbose_log.enabled: found_all_sliders: List[CSFSlider] = list() for slider_identifier in found_slider_identifiers: slider = self._registry.locate_by_identifier(slider_identifier) if slider is None: continue found_all_sliders.append(slider) verbose_log.format_with_message( 'Found sliders via all tags', sliders=',\n'.join([ '{}:{}'.format(str(found_slider.raw_display_name), found_slider.author) for found_slider in found_all_sliders ])) found_sliders_via_any_tags = set() for include_any_tag in any_tags: if include_any_tag is None: continue if include_any_tag.key not in self.slider_library: continue found_sliders_via_any_tags = found_sliders_via_any_tags | self.slider_library[ include_any_tag.key] self.log.debug('Found sliders via any {}'.format( len(found_sliders_via_any_tags))) if verbose_log.enabled: found_any_sliders: List[CSFSlider] = list() for slider_identifier in found_sliders_via_any_tags: slider = self._registry.locate_by_identifier(slider_identifier) if slider is None: continue found_any_sliders.append(slider) verbose_log.format_with_message( 'Found sliders via any tags', sliders=',\n'.join([ '{}:{}'.format(str(found_slider.raw_display_name), found_slider.author) for found_slider in found_any_sliders ])) if found_slider_identifiers is None: self.log.debug('No sliders found for all_tags.') if not all_tags: self.log.debug('Returning any tags.') return found_sliders_via_any_tags else: query_type = query.query_type if not any_tags and ( query_type == CSFQueryType.ALL_INTERSECT_ANY or query_type == CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE): query_type = CSFQueryType.ALL_PLUS_ANY if query_type == CSFQueryType.ALL_PLUS_ANY: found_slider_identifiers = found_slider_identifiers | found_sliders_via_any_tags elif query_type == CSFQueryType.ALL_INTERSECT_ANY: found_slider_identifiers = found_slider_identifiers & found_sliders_via_any_tags if query_type == CSFQueryType.ALL_PLUS_ANY_MUST_HAVE_ONE: if not found_sliders_via_any_tags: return set() found_slider_identifiers = found_slider_identifiers | found_sliders_via_any_tags elif query_type == CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE: if not found_sliders_via_any_tags: return set() found_slider_identifiers = found_slider_identifiers & found_sliders_via_any_tags if found_slider_identifiers is None: self.log.debug( 'No found sliders after combining any tags. All Tags: {} Any Tags: {}' .format(all_tags, any_tags)) return set() self.log.debug('After any tags {}'.format( len(found_slider_identifiers))) excluded = set() for exclude_tag in exclude_tags: if exclude_tag is None: continue if exclude_tag.key not in self.slider_library: continue excluded = excluded | self.slider_library[exclude_tag.key] self.log.debug('Excluding sliders {}'.format(len(excluded))) if excluded: found_slider_identifiers = found_slider_identifiers - excluded self.log.debug('After exclude sliders {}'.format( len(found_slider_identifiers))) found_sliders: List[CSFSlider] = list() for slider_identifier in found_slider_identifiers: slider = self._registry.locate_by_identifier(slider_identifier) if slider is None: continue found_sliders.append(slider) if verbose_log.enabled: verbose_log.debug('Returning sliders [{}]'.format(',\n'.join([ '{}:{}'.format(str(found_slider.raw_display_name), found_slider.author) for found_slider in found_sliders ]))) return set(found_sliders) def _organize(self, sliders: Tuple[CSFSlider]): self.log.debug('Collecting Sliders Query Data...') self.slider_library.clear() new_slider_library = dict() tag_handlers = tuple(self._tag_handlers) for slider in sliders: self.log.format_with_message('Handling tags for Slider', cas_part=slider.name) slider_identifier = slider.unique_identifier slider_tag_keys = list() for tag_handler in tag_handlers: if not tag_handler.applies(slider): continue tag_type = tag_handler.tag_type for slider_tag in tag_handler.get_tags(slider): tag_key = (tag_type, slider_tag) slider_tag_keys.append(tag_key) if tag_key not in new_slider_library: new_slider_library[tag_key] = set() if slider_identifier in new_slider_library[tag_key]: continue new_slider_library[tag_key].add(slider_identifier) self.log.format_with_message('Applied tags to slider.', display_name=slider.name, keys=slider_tag_keys) self.log.format_with_message( 'Completed collecting Sliders Query Data.', slider_library=new_slider_library) self.slider_library = new_slider_library def trigger_collection(self, show_loading_notification: bool = True) -> None: """trigger_collection(show_loading_notification=True) Trigger the action to collect all sliders and organize them by a number of tags. :param show_loading_notification: If set to True, the Loading Sliders notification will be shown. If set to False, the Loading Sliders notification will not be shown. Default is True. :type show_loading_notification: bool, optional """ def _recollect_data() -> None: try: if show_loading_notification: CommonBasicNotification( CSFStringId.LOADING_SLIDERS, CSFStringId.LOADING_SLIDERS_DESCRIPTION).show() number_of_sliders = self._collect() if number_of_sliders == -1: return CommonBasicNotification( CSFStringId.FINISHED_LOADING_SLIDERS, CSFStringId.FINISHED_LOADING_SLIDERS_DESCRIPTION, description_tokens=(str(number_of_sliders), )).show() except Exception as ex: self.log.error('Error occurred while collecting sliders.', exception=ex) self._collecting = False _recollect_data() def _collect(self) -> int: if self._collecting: return -1 self._collecting = True try: stop_watch = CommonStopWatch() stop_watch.start() self._all = tuple(self._registry.sliders.values()) self.log.format_with_message( 'Loaded Sliders', all_list=self._all, ) enabled = self.log.enabled self.log.enable() self.log.debug('Took {}s to collect {} Sliders.'.format( '%.3f' % (stop_watch.stop()), len(self._all))) if not enabled: self.log.disable() stop_watch.start() self._organize(self._all) self._collecting = False self.log.enable() self.log.debug('Took {}s to organize Sliders'.format( '%.3f' % (stop_watch.stop()))) if not enabled: self.log.disable() if self.log.enabled: self.log.debug('Loaded {} Sliders.'.format(len(self._all))) return len(self._all) except Exception as ex: self.log.error('Error occurred while collecting Sliders.', exception=ex) return -1 finally: self._collecting = False @classmethod def register_tag_handler( cls, tag_type: CSFSliderTagType) -> Callable[[Any], Any]: """ Register a tag handler. """ def _method_wrapper( tag_handler_callback: Callable[[CSFSliderTagType], CSFSliderTagHandler]): cls().add_tag_handler(tag_handler_callback, tag_type) return tag_handler_callback return _method_wrapper @staticmethod @CommonEventRegistry.handle_events(ModInfo.get_identity()) def _load_sliders_on_zone_load(event_data: S4CLZoneLateLoadEvent): if event_data.game_loaded: # If the game is already loaded, we've already loaded the data once. return False CSFSliderQueryRegistry().trigger_collection( show_loading_notification=False) return True @staticmethod @CommonIntervalEventRegistry.run_once(ModInfo.get_identity(), milliseconds=10) def _show_sliders_loading_notification_on_first_update() -> None: if CSFSliderQueryRegistry()._collecting: CommonBasicNotification( CSFStringId.LOADING_SLIDERS, CSFStringId.LOADING_SLIDERS_DESCRIPTION).show()
from cncustomsliderframework.sliders.query.tag_handlers.slider_tag_handler import CSFSliderTagHandler from cncustomsliderframework.sliders.slider_tag_type import CSFSliderTagType from cncustomsliderframework.sliders.tag_filters.slider_tag_filter import CSFSliderTagFilter from sims4.commands import Command, CheatOutput, CommandType from sims4communitylib.classes.time.common_stop_watch import CommonStopWatch from sims4communitylib.events.event_handling.common_event_registry import CommonEventRegistry from sims4communitylib.events.interval.common_interval_event_service import CommonIntervalEventRegistry from sims4communitylib.events.zone_spin.events.zone_late_load import S4CLZoneLateLoadEvent from sims4communitylib.logging.has_log import HasLog from sims4communitylib.mod_support.mod_identity import CommonModIdentity from sims4communitylib.notifications.common_basic_notification import CommonBasicNotification from sims4communitylib.services.common_service import CommonService from sims4communitylib.utils.common_log_registry import CommonLogRegistry verbose_log = CommonLogRegistry().register_log( ModInfo.get_identity(), 'csf_slider_query_registry_verbose') class CSFSliderQueryRegistry(CommonService, HasLog): """ Registry handling slider queries. """ # noinspection PyMissingOrEmptyDocstring @property def mod_identity(self) -> CommonModIdentity: return ModInfo.get_identity() # noinspection PyMissingOrEmptyDocstring @property def log_identifier(self) -> str: return 'csf_slider_query_registry'