Skip to content

Commit

Permalink
No more Singleton settings
Browse files Browse the repository at this point in the history
Settings is now a manager class in the model layer.
Some short circuiting was needed to allow functionality to continue.
Fixed issue with always on top and configuration windows.
Now correctly alerts user when loadouts has changed, and prompts to save.
Always saves app settings on exit.
  • Loading branch information
stixes committed Jun 25, 2024
1 parent fd1059e commit 6564104
Show file tree
Hide file tree
Showing 19 changed files with 96 additions and 165 deletions.
7 changes: 2 additions & 5 deletions hell_snake.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
#!.venv/bin/python3
import constants
import sys
from src.settings import Settings
from src.settings import SettingsManager
from src.controller import Controller
from src.model import Model

def main():
# Initialize settings
settings = Settings.getInstance()

# Initialize and run the app
model = Model()
controller = Controller(model)

# Initialize our presentation
if settings.view_framework == constants.VIEW_PYQT5:
if model.settingsManager.view_framework == constants.VIEW_PYQT5:
from src.view.pyqt5.pyqt5 import PyQT5View
view = PyQT5View(controller)
else:
Expand Down
47 changes: 28 additions & 19 deletions src/controller.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
import constants
import sys
from src.model import Model
from src.settings import Settings
from src.settings import SettingsManager
from src.listener_pynput import PynputKeyListener

class Controller:
def __init__(self, model: Model):
self.model = model
self.settings = Settings.getInstance()

self.loadouts_updated = False

self.keyListener = PynputKeyListener(self.model, self)

# Helper for view classes
def get_settings_manager(self):
return self.model.settingsManager

def get_loadouts_manager(self):
return self.model.loadoutsManager

def get_executor(self):
return self.executer

def set_executor(self):
if hasattr(self, "executer"):
self.executer.stop()

selectedExecutor = self.settings.selectedExecutor
selectedExecutor = self.model.settingsManager.selectedExecutor
if selectedExecutor == constants.EXECUTOR_PYNPUT:
from src.executer_pynput import PynputExecuter
self.executer = PynputExecuter()
self.executer = PynputExecuter(self)
elif selectedExecutor == constants.EXECUTOR_ARDUINO:
from src.executer_arduino import ArduinoPassthroughExecuter
self.executer = ArduinoPassthroughExecuter(self)
elif selectedExecutor == constants.EXECUTOR_PYAUTOGUI:
from src.executer_pyautogui import PyAutoGuiExecuter
self.executer = PyAutoGuiExecuter()
self.executer = PyAutoGuiExecuter(self)
elif selectedExecutor == constants.EXECUTOR_XDOTOOL:
from src.executer_xdotool import XdotoolExecuter
self.executer = XdotoolExecuter()
self.executer = XdotoolExecuter(self)
else:
raise ModuleNotFoundError
self.view.update_executor_menu()
Expand All @@ -44,8 +53,8 @@ def on_settings_changed(self):
def set_view(self, view):
self.view = view
self.set_executor()
if hasattr(self.settings, "currentLoadoutId"):
self.set_active_loadout(self.settings.currentLoadoutId)
if hasattr(self.model.settingsManager, "currentLoadoutId"):
self.set_active_loadout(self.model.settingsManager.currentLoadoutId)
self.view.show_interface()

def toggle_armed(self):
Expand All @@ -60,7 +69,7 @@ def set_armed(self, isArmed):
def cycle_loadout(self, offset):
# Get current active loadout ID and available loadout IDs
current_loadout_id = self.model.currentLoadoutId
loadout_ids = list(self.model.loadoutManager.loadouts.keys())
loadout_ids = list(self.model.loadoutsManager.loadouts.keys())

# Calculate the index of the next loadout
current_index = loadout_ids.index(current_loadout_id)
Expand All @@ -77,34 +86,34 @@ def show_change_macro_dialog(self, key):

def update_macro_binding(self, key, stratagemId):
stratagem = self.model.stratagems[stratagemId]
stratagem.prepare_stratagem(self.executer)
stratagem.prepare_stratagem(self)
self.model.update_macro_binding(key, stratagemId)
self.loadouts_updated = True
self.view.update_macros()

def add_loadout(self, loadoutName):
self.model.loadoutManager.addLoadout(loadoutName)
self.set_active_loadout(self.model.loadoutManager.getCurrentLoadout())
self.model.loadoutsManager.addLoadout(loadoutName)
self.set_active_loadout(self.model.loadoutsManager.getCurrentLoadout())
self.loadouts_updated = True
self.view.update_loadout_menu_items()

def delete_loadout(self, loadoutId):
self.model.loadoutManager.deleteLoadout(loadoutId)
self.set_active_loadout(self.model.loadoutManager.getCurrentLoadout())
self.model.loadoutsManager.deleteLoadout(loadoutId)
self.set_active_loadout(self.model.loadoutsManager.getCurrentLoadout())
self.loadouts_updated = True
self.view.update_loadout_menu_items()

def update_loadout(self, id, loadout):
self.model.loadoutManager.updateLoadout(id, loadout)
self.model.loadoutsManager.updateLoadout(id, loadout)
self.set_active_loadout(id)
self.loadouts_updated = True
self.view.update_loadout_menu_items()

def set_active_loadout(self, loadoutId):
self.model.set_active_loadout(loadoutId)
for key, stratagem in self.model.macros.items():
stratagem.prepare_stratagem(self.executer)
self.settings.currentLoadoutId = loadoutId
stratagem.prepare_stratagem(self)
self.model.settingsManager.currentLoadoutId = loadoutId
self.view.update_current_loadout()

def trigger_macro(self, stratagem):
Expand All @@ -113,6 +122,6 @@ def trigger_macro(self, stratagem):
def on_exit(self):
if self.loadouts_updated:
if self.view.confirm_save_loadouts():
self.model.loadoutManager.saveToFile()
self.settings.saveToFile()
self.model.loadoutsManager.saveToFile()
self.model.settingsManager.saveToFile()
sys.exit(0)
7 changes: 3 additions & 4 deletions src/executer_arduino.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import utilities
from src.executer_base import BaseExecutor
from src.view.view_base import SettingsItem, MenuItem
from src.settings import Settings
from src.settings import SettingsManager

KEY_DELAY = "arduino_stratagemKeyDelay"
KEY_DELAY_DEFAULT = 30
Expand All @@ -21,10 +21,9 @@

class ArduinoPassthroughExecuter(BaseExecutor):
def __init__(self, controller):
super().__init__()
super().__init__(controller)
self.arduino = None
self.settings = Settings.getInstance()
self.controller = controller
self.settings = self.controller.get_settings_manager()

def start(self):
if getattr(self.settings, KEY_AUTO_RECONNECT, KEY_AUTO_RECONNECT_DEFAULT):
Expand Down
4 changes: 2 additions & 2 deletions src/executer_base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from abc import ABC, abstractmethod

class BaseExecutor(ABC):
def __init__(self):
pass
def __init__(self, controller):
self.controller = controller

def start(self):
pass
Expand Down
8 changes: 4 additions & 4 deletions src/executer_pyautogui.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import utilities
from src.executer_base import BaseExecutor
from src.view.view_base import SettingsItem
from src.settings import Settings
from src.settings import SettingsManager

TRIGGER_DELAY = "pyautogui_triggerDelay"
TRIGGER_DELAY_DEFAULT = 100
Expand All @@ -15,9 +15,9 @@
KEY_DELAY_JITTER_DEFAULT = 20

class PyAutoGuiExecuter(BaseExecutor):
def __init__(self):
super().__init__()
self.settings = Settings.getInstance()
def __init__(self, controller):
super().__init__(controller)
self.settings = self.controller.get_settings_manager()
pyautogui.FAILSAFE = True
self.triggerKey = self.parse_macro_key(self.settings.triggerKey)

Expand Down
8 changes: 4 additions & 4 deletions src/executer_pynput.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from src.executer_base import BaseExecutor
from src.key_parser_pynput import PynputKeyparser
from src.view.view_base import SettingsItem
from src.settings import Settings
from src.settings import SettingsManager

TRIGGER_DELAY = "pynput_triggerDelay"
TRIGGER_DELAY_DEFAULT = 100
Expand All @@ -16,9 +16,9 @@
KEY_DELAY_JITTER_DEFAULT = 20

class PynputExecuter(BaseExecutor):
def __init__(self):
super().__init__()
self.settings = Settings.getInstance()
def __init__(self, controller):
super().__init__(controller)
self.settings = self.controller.get_settings_manager()
self.keyboard_controller = Controller()
self.triggerKey = self.parse_macro_key(self.settings.triggerKey)

Expand Down
8 changes: 4 additions & 4 deletions src/executer_xdotool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import subprocess
from src.executer_base import BaseExecutor
from src.view.view_base import SettingsItem
from src.settings import Settings
from src.settings import SettingsManager

TRIGGER_DELAY = "xdotool_triggerDelay"
TRIGGER_DELAY_DEFAULT = 100
Expand All @@ -15,9 +15,9 @@
KEY_DELAY_JITTER_DEFAULT = 20

class XdotoolExecuter(BaseExecutor):
def __init__(self):
super().__init__()
self.settings = Settings.getInstance()
def __init__(self, controller):
super().__init__(controller)
self.settings = self.controller.get_settings_manager()
self.isExecuting = False
self.triggerKey = self.parse_macro_key(self.settings.triggerKey)

Expand Down
17 changes: 8 additions & 9 deletions src/listener_pynput.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import constants
from src.key_parser_pynput import PynputKeyparser
from src.model import Model
from src.settings import Settings
from src.settings import SettingsManager

class PynputKeyListener:
def __init__(self, model : Model, controller):
Expand All @@ -16,20 +16,19 @@ def __init__(self, model : Model, controller):
self.listener.start()

# Attach a listener to notify us of changes to settings
self.settings = Settings.getInstance()
self.settings.attach_change_listener(self._on_settings_changed)
self.model.settingsManager.attach_change_listener(self._on_settings_changed)
self._on_settings_changed()

### Handlers ###

# Global arm handler
def handle_global_arm_press(self, key):
if (self.settings.globalArmMode == constants.ARM_MODE_TOGGLE):
if (self.model.settingsManager.globalArmMode == constants.ARM_MODE_TOGGLE):
self.controller.toggle_armed()
elif (self.settings.globalArmMode == constants.ARM_MODE_PUSH and not self.model.isArmed):
elif (self.model.settingsManager.globalArmMode == constants.ARM_MODE_PUSH and not self.model.isArmed):
self.controller.set_armed(True)
def handle_global_arm_release(self, key):
if (self.settings.globalArmMode == constants.ARM_MODE_PUSH):
if (self.model.settingsManager.globalArmMode == constants.ARM_MODE_PUSH):
self.controller.set_armed(False)

# Loadout browser
Expand All @@ -43,9 +42,9 @@ def handle_prev_loadout(self, key):
### Helpers ###
def _on_settings_changed(self):
''' This function is called when settings are updated, since we attach it as a listener in __init__ '''
self.globalArmKey = PynputKeyparser.parse_key(self.settings.globalArmKey)
self.nextLoadoutKey = PynputKeyparser.parse_key(self.settings.nextLoadoutKey)
self.prevLoadoutKey = PynputKeyparser.parse_key(self.settings.prevLoadoutKey)
self.globalArmKey = PynputKeyparser.parse_key(self.model.settingsManager.globalArmKey)
self.nextLoadoutKey = PynputKeyparser.parse_key(self.model.settingsManager.nextLoadoutKey)
self.prevLoadoutKey = PynputKeyparser.parse_key(self.model.settingsManager.prevLoadoutKey)
self.key_press_handlers = {
self.globalArmKey: self.handle_global_arm_press,
self.nextLoadoutKey: self.handle_next_loadout,
Expand Down
3 changes: 0 additions & 3 deletions src/loadouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ def __init__(self, name, macroKeys):
self.name = name
self.macroKeys = macroKeys

def to_json(self):
return {'name': self.name, 'macroKeys': self.macroKeys}

class LoadoutManager:
def __init__(self):
self._observers = []
Expand Down
16 changes: 8 additions & 8 deletions src/model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import constants
from src.loadouts import LoadoutManager,Loadout
from src.settings import SettingsManager
import utilities
import json
from src.stratagem import Stratagem
Expand All @@ -11,10 +12,12 @@ def __init__(self):
# List of stratagems, and the respective macro definition
self.stratagems = self.loadStratagemsFromFile()

# Settings manager handles app configuration (e.g. hotkeys)
self.settingsManager = SettingsManager()

# Loadout manager handles persistance of loadouts.
self.loadoutManager = LoadoutManager()
# a Loadout is a list of keys, and the respective stratagem to be activated
# self.set_active_loadout(self.loadoutManager.getCurrentLoadout())
self.loadoutsManager = LoadoutManager()
# a Loadout is a list of keys, and the respective stratagem to be activated

def loadStratagemsFromFile(self):
with open(constants.RESOURCE_PATH+"stratagems.json") as json_file:
Expand All @@ -33,10 +36,7 @@ def update_macro_binding(self, key, stratagemId):
self.macros.update({key:stratagem})

def getMacroForKey(self, key):
return self.macros[key]

# def get_next_loadout(self):
# return self.loadoutManager.getCurrentLoadout()
return self.macros.get(key, None)

def set_active_loadout(self, id):
self.currentLoadoutId = id
Expand All @@ -45,7 +45,7 @@ def set_active_loadout(self, id):
self.currentLoadout = None
self.macroKeys = None
else:
self.currentLoadout = self.loadoutManager.loadouts[id]
self.currentLoadout = self.loadoutsManager.loadouts[id]
self.macroKeys = self.currentLoadout.macroKeys
for key, stratagemId in self.macroKeys.items():
self.macros.update({key:self.stratagems[stratagemId]})
Expand Down
18 changes: 1 addition & 17 deletions src/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,7 @@
import json
import utilities

class Settings:
_instance = None # Class variable to hold the singleton instance

@classmethod
def getInstance(cls):
"""
Method to retrieve or create a singleton instance of the Settings class.
This method checks if an instance of the class has already been created (stored in `_instance`).
If not, it creates a new instance and stores it in `_instance`. It then returns this singleton instance.
Returns:
Settings: a singleton instance of the Settings class.
"""
if cls._instance is None:
cls._instance = cls() # Create a new instance if one doesn't exist
return cls._instance
class SettingsManager:

def __init__(self):
self._observers = []
Expand Down
8 changes: 4 additions & 4 deletions src/stratagem.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from src.settings import Settings
from src.settings import SettingsManager


class Stratagem:
Expand All @@ -11,8 +11,8 @@ def __init__(self, name, category, command, icon_name):
else:
self.icon_name = icon_name

def prepare_stratagem(self, executer):
def prepare_stratagem(self, controller):
self.commandArray=[]
for input in self.command:
key = Settings.getInstance().stratagemKeys[input]
self.commandArray.append(executer.parse_macro_key(key))
key = controller.get_settings_manager().stratagemKeys[input]
self.commandArray.append(controller.get_executor().parse_macro_key(key))
Loading

0 comments on commit 6564104

Please sign in to comment.