def __init__(self, name, **kwargs): self.enable_changed = Event(str, bool) self.enable_changed += self.on_enabled_changed self._is_enabled = False self._init = None self.name = name self.is_enabled = kwargs.get('is_enabled', True)
class ScenarioModel: name_changed = Event(str) current_exercise_changed = Event(int, object) scenario_loaded = Event(int, int) scenario_ended = Event() def __init__(self): self._name = 'NONE' self.exercises = [] self.widgets = [] self._index = 0 self.correct_count = 0 @property def name(self): return self._name @name.setter def name(self, value): self._name = value self.name_changed.emit(value) @property def current_exercise(self): return self.exercises[self.index] if self.index < self.total else None @property def current_widget(self): return self.widgets[self.index] if self.index < self.total else None @property def index(self): return self._index @index.setter def index(self, value): self._index = value self.current_exercise_changed.emit(value, self.current_widget) @property def total(self): return len(self.exercises) def set_exercises(self, exercises_wids_tuple): self.reset() self.widgets, self.exercises = zip(*exercises_wids_tuple) self.scenario_loaded.emit(self.index, self.total) self.current_exercise_changed.emit(self.index, self.current_widget) def check_answer(self, answer): self.correct_count += 1 if self.current_exercise.check_answer(answer) else 0 self.index += 1 def reset(self): self._index = 0 self.correct_count = 0
class BaseExerciseWidget(QWidget): send_answer_event = Event() def __init__(self, exercise, parent=None): super().__init__(parent) self.data = exercise self.__init_ui() def __init_ui(self): self.grid = QGridLayout(self) self.setLayout(self.grid) self._init_quest_field() self._init_answer_field() def _init_quest_field(self): self.questTypeLbl = QLabel(self) self.questTypeLbl.setText(self.data.question_type.ui) self.questTypeLbl.setAlignment(Qt.AlignCenter) self.questTypeLbl.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) self.grid.addWidget(self.questTypeLbl, 0, 0, 1, 1) self.questLbl = QLabel(self) style = QUEST_LBL_CSS if len( self.data.question) > 1 else QUEST_KAN_LBL_CSS self.questLbl.setStyleSheet(style) self.questLbl.setAlignment(Qt.AlignCenter) self.questLbl.setText(self.data.question) self.grid.addWidget(self.questLbl, 1, 0, 1, 1) def _init_answer_field(self): pass
class CrossWidgetEvents: change_screen_event = Event(int) clsoe_main_window_event = Event() load_scenario_event = Event(object, str, bool) show_message_event = Event(str, str, str) start_editor_event = Event(EditorMode, list) reload_scenarios_event = Event() locale_changed_event = Event() show_question_event = Event(str, str, object)
class Module: __slots__ = ('name', '_is_enabled', '_init', 'enable_changed') def __init__(self, name, **kwargs): self.enable_changed = Event(str, bool) self.enable_changed += self.on_enabled_changed self._is_enabled = False self._init = None self.name = name self.is_enabled = kwargs.get('is_enabled', True) @property def ui_name(self): return self.init.get_name() if self.init else self.name @property def is_enabled(self): return self._is_enabled @is_enabled.setter def is_enabled(self, value): if self._is_enabled == value: return self._is_enabled = value self.enable_changed.emit(self.name, value) @property def init(self): return self._init def activate_module(self): self._init = get_module_init(self.name) self._is_enabled = self._init is not None def deactivate_module(self): self._is_enabled = False self._init = None def on_enabled_changed(self, name, value): self.activate_module() if value \ else self.deactivate_module()
def __init__(self, mod, quest_type, lazy_init): self.module = mod self.quest_type_changed = Event(str) if isinstance(quest_type, str): func = self.module.init.translate_local if self.module.init else lambda x: quest_type qt = QuestType(quest_type, func) else: qt = quest_type self._quest_type = NotifyProperty('quest_type', qt) self._quest_type += self.quest_type_changed.emit self.lazy_init = lazy_init self._data = None
class EditorBlockModel: keys_changed = Event(list) kanji_changed = Event(object) update_kanji_event = Event() update_label_event = Event() def __init__(self): self.kanji = None self.prop_path = '' self._keys = NotifyProperty('keys', list()) self._keys += self.keys_changed.emit self.update_kanji_event += self.update_label_event.emit self.update_kanji_event += lambda: self.kanji_changed.emit(self.kanji) def set_kanji(self, value): self.kanji = value self.kanji_changed.emit(value) @property def keys(self): return self._keys.get() @keys.setter def keys(self, value): self._keys.set(value) def set_kanji_prop(self, prop_name, value): @ChangeMemento(f'{self.prop_path}.{prop_name}', self.update_kanji_event) def memento_func(): setattr(self.kanji, prop_name, value) if not self.kanji or getattr(self.kanji, prop_name) == value: return memento_func()
class EditorBlockModel: word_changed = Event(object) update_word_event = Event() update_label_event = Event() def __init__(self): self.word = None self.prop_path = '' self.update_word_event += self.update_label_event.emit self.update_word_event += lambda: self.word_changed.emit(self.word) def set_word(self, value): self.word = value self.word_changed.emit(value) def set_word_prop(self, prop_name, value): @ChangeMemento(f'{self.prop_path}.{prop_name}', self.update_word_event) def memento_func(): setattr(self.word, prop_name, value) if not self.word or getattr(self.word, prop_name) == value: return memento_func()
class MainModel: modules_changed = Event(list) scenarios_changed = Event(list) locales_changed = Event(list) is_applied_settings_changed = Event(bool) is_options_enabled_changed = Event(bool) current_locale_changed = Event(str) modules_dir_changed = Event(str) scenarios_dir_changed = Event(str) def __init__(self): self.data_loader = DataLoader() self._is_options_enabled = NotifyProperty('is_options_enabled', False) self._is_options_enabled += self.is_options_enabled_changed.emit self._editor_mode = NotifyProperty('editor_mode', 0) self.locales = [] self._is_applied_settings = NotifyProperty('is_applied_settings', True) self._is_applied_settings += self.is_applied_settings_changed.emit self._current_locale = NotifyProperty('current_locale') self._current_locale += self.current_locale_changed.emit self._modules_dir = '' self._scenarios_dir = '' @property def is_debug(self): return self.data_loader.is_debug @property def modules(self): return self.data_loader.modules.values() @property def scenarios(self): return self.data_loader.scenarios.values() def update_modules(self): self.modules_changed.emit(self.modules) def update_scenarios(self): self.scenarios_changed.emit(self.scenarios) @property def is_options_enabled(self): return self._is_options_enabled.get() @is_options_enabled.setter def is_options_enabled(self, value): self._is_options_enabled.set(value) @property def editor_mode(self): return self._editor_mode.get() @editor_mode.setter def editor_mode(self, value): self._editor_mode.set(value) def set_locales(self, values): self.locales = values self.locales_changed.emit(values) @property def is_applied_settings(self): return self._is_applied_settings.get() @is_applied_settings.setter def is_applied_settings(self, value): self._is_applied_settings.set(value) @property def current_locale(self): return self._current_locale.get() @current_locale.setter def current_locale(self, value): self._current_locale.set(value) self.is_applied_settings = False @property def modules_dir(self): return self._modules_dir @modules_dir.setter def modules_dir(self, value): self._modules_dir = value self.modules_dir_changed.emit(value) self.is_applied_settings = False @property def scenarios_dir(self): return self._scenarios_dir @scenarios_dir.setter def scenarios_dir(self, value): self._scenarios_dir = value self.is_applied_settings = False self.scenarios_dir_changed.emit(value)
class MementoManager(metaclass=Singleton): can_undo_changed = Event(bool) can_redo_changed = Event(bool) index_changed = Event(int) def __init__(self): self.logger = logger self.super_subject = None def set_subject(self, subject): self._index = -1 self._history = [] self.subject = subject self.update_status() @property def index(self): return self._index @property def can_undo(self): return self._index >= 0 @property def can_redo(self): return self._index < len(self._history) - 1 def save(self, memento): memento.save(self.subject) if self.has_changes(memento): self._index += 1 if self._index < len(self._history): self._history = self._history[:self._index] self._history.append(memento) self.update_status() self.logger.debug(f'SAVE {str(memento)} (ID={self._index})') def has_changes(self, memento): def check_equal(memento_1, memento_2): return type(memento_1) == type( memento_2) and memento_1.prop_path == memento_2.prop_path last_mementos = [ m for m in self._history[:self._index + 1][::-1] if check_equal(m, memento) ] return not last_mementos[0] == memento if len( last_mementos) > 0 else True def cancel(self): while self.can_undo: self.undo() def undo(self): if not self.can_undo: return hist = self._history[self._index] hist.undo(self.subject) self._index -= 1 self.logger.debug(f'UNDO {str(hist)}') self.update_status() def redo(self): if not self.can_redo: return self._index += 1 hist = self._history[self._index] hist.redo(self.subject) self.logger.debug(f'REDO {str(hist)}') self.update_status() def update_status(self): self.can_undo_changed.emit(self.can_undo) self.can_redo_changed.emit(self.can_redo) self.index_changed.emit(self.index)
class EditorModel: SC_DATA = 'scenario_data' scenario_changed = Event(object) scenario_name_changed = Event(str) can_undo_changed = Event(bool) can_redo_changed = Event(bool) can_save_changed = Event(bool) blocks_changed = Event(list) quest_types_changed = Event(list) current_quest_type_changed = Event(str) current_sc_block_index_changed = Event(int) current_data_index_changed = Event(int) current_sc_block_changed = Event(object) current_data_changed = Event(object, str) listitem_widget_changed = Event(object) block_widget_changed = Event(object) update_scenario_event = Event() update_scenario_name_event = Event() update_curr_sc_block = Event() update_current_quest_type_event = Event() def __init__(self): self._scenario = NotifyProperty('scenario') self._scenario += self.scenario_changed.emit self._blocks = NotifyProperty('blocks', list()) self._blocks += self.blocks_changed.emit self._quest_types = NotifyProperty('quest_types_changed', list()) self._quest_types += self.quest_types_changed.emit self._current_sc_block_index = NotifyProperty('current_sc_block_index', -1) self._current_sc_block_index += self.current_sc_block_index_changed.emit self._current_data_index = NotifyProperty('current_data_index', -1) self._current_data_index += self.current_data_index_changed.emit self._current_data_index += lambda x: self.send_data() self._listitem_widget = NotifyProperty('listitem_widget') self._listitem_widget += self.listitem_widget_changed.emit self._block_widget = NotifyProperty('_block_widget') self._block_widget += self.block_widget_changed.emit self.update_scenario_event += self.update_scenario self.update_scenario_name_event += self.update_scenario_name self.update_curr_sc_block += self.send_sc_block self.update_current_quest_type_event += lambda: self.current_quest_type_changed.emit( self.current_quest_type) # region Top lvl @property def scenario(self): return self._scenario.get() @scenario.setter def scenario(self, value): self._scenario.set(value) @property def blocks(self): return self._blocks.get() @blocks.setter def blocks(self, value): self._blocks.set(value) def append_scenario_data(self, sc_data): prop_path = f'{self.SC_DATA}.[{len(self.scenario.scenario_data)}]' AddMemento(prop_path, self.update_scenario_event)\ (self.scenario.scenario_data.append)(sc_data) self.scenario_changed.emit(self.scenario) def remove_scenario_data(self, sc_data): prop_path = f'{self.SC_DATA}.[{self.scenario.scenario_data.index(sc_data)}]' RemoveMemento(prop_path, self.update_scenario_event)\ (self.scenario.scenario_data.remove)(sc_data) self.scenario_changed.emit(self.scenario) # endregion # region Mid lvl @property def quest_types(self): return self._quest_types.get() @quest_types.setter def quest_types(self, value): self._quest_types.set(value) @property def current_quest_type(self): return self.get_current_sc_block().quest_type @current_quest_type.setter def current_quest_type(self, value): @ChangeMemento(self.get_block_prop_path('quest_type'), self.update_current_quest_type_event) def wrapper(): self.get_current_sc_block().quest_type = value self.current_quest_type_changed.emit(value.value) curr_bl = self.get_current_sc_block() if curr_bl is None or curr_bl.quest_type == value: return wrapper() @property def current_sc_block_index(self): return self._current_sc_block_index.get() @current_sc_block_index.setter def current_sc_block_index(self, value): self._current_sc_block_index.set(value) @property def current_data_index(self): return self._current_data_index.get() @current_data_index.setter def current_data_index(self, value): self._current_data_index.set(value) def get_current_sc_block(self): index = self.current_sc_block_index return self.scenario.scenario_data[index] \ if 0 <= index < len(self.scenario.scenario_data) else None def get_current_data(self): index = self.current_data_index sc_block = self.get_current_sc_block() return sc_block.data[index] \ if 0 <= index < len(sc_block.data) else None def get_block_prop_path(self, prop_name): return f'{self.SC_DATA}.[{self.current_sc_block_index}].{prop_name}' def append_block_data(self, value): data = self.scenario.scenario_data[self.current_sc_block_index].data prop_path = f"{self.get_block_prop_path('data')}.[{len(data)}]" AddMemento(prop_path, self.update_curr_sc_block)(data.append)(value) self.current_kanji_index = len(data) - 1 self.send_sc_block() def remove_block_data(self, value): data = self.scenario.scenario_data[self.current_sc_block_index].data prop_path = f"{self.get_block_prop_path('data')}.[{data.index(value)}]" RemoveMemento(prop_path, self.update_curr_sc_block)(data.remove)(value) self.current_kanji_index = -1 self.send_sc_block() # endregion @property def listitem_widget(self): return self._listitem_widget.get() @listitem_widget.setter def listitem_widget(self, value): self._listitem_widget.set(value) @property def block_widget(self): return self._block_widget.get() @block_widget.setter def block_widget(self, value): self._block_widget.set(value) def update_scenario(self): self.scenario_changed.emit(self.scenario) def update_scenario_name(self): self.scenario_name_changed.emit(self.scenario.name) def send_sc_block(self): self.current_sc_block_changed.emit(self.get_current_sc_block()) def send_data(self): prop_path = f"{self.get_block_prop_path('data')}.[{self.current_data_index}]" self.current_data_changed.emit(self.get_current_data(), prop_path)