def remove_shortcut(self): if not is_windows(): logger.debug("Removing shortcut") return if self.shortcut_path.exists(): self.shortcut_path.unlink()
def s99_client_path(): if IS_EXE: client_dir = BASE_DIR else: client_dir = BASE_DIR / "../../dist" if is_windows(): return client_dir / "s99-client.exe" return client_dir / "s99-client"
def make_shortcut(self): version = self.modlunky_config.playlunky_version if not version: return exe_path = PLAYLUNKY_DATA_DIR / version / PLAYLUNKY_EXE if not is_windows(): logger.debug("Making shortcut to %s", exe_path) return install_dir = self.modlunky_config.install_dir arguments = f'--exe_dir="{install_dir}"' with winshell.shortcut(f"{self.shortcut_path}") as link: link.path = f"{exe_path}" link.working_directory = f"{exe_path.parent}" link.arguments = arguments link.description = "Shortcut to playlunky"
def check_requirements(): # Only check requirements in dev environments if IS_EXE: return all_req_files = [ "requirements.txt", "requirements-dev.txt", ] if is_windows(): all_req_files.append("requirements-win.txt") installed = pip_api.installed_distributions() missing_req_files = set() for req_file in all_req_files: requirements = pip_api.parse_requirements(req_file) for req in requirements.values(): if req.name not in installed: missing_req_files.add(req_file) logger.warning("Missing required package '%s'", req.name) continue installed_ver = installed[req.name].version if req.specifier.contains(installed_ver): continue missing_req_files.add(req_file) logger.warning( "Installed version of '%s' is %s, doesen't meet %s", req.name, installed_ver, req.specifier, ) if not missing_req_files: return r_args = " ".join([f"-r {r}" for r in missing_req_files]) logger.warning( "Some requirements aren't met. Run pip install --upgrade %s", r_args, )
def _unbind_from_mousewheel(self, _event): if is_windows(): self.canvas.unbind_all("<MouseWheel>") else: self.canvas.unbind_all("<Button-4>") self.canvas.unbind_all("<Button-5>")
def _bind_to_mousewheel(self, _event): if is_windows(): self.canvas.bind_all("<MouseWheel>", self._on_mousewheel) else: self.canvas.bind_all("<Button-4>", self._on_mousewheel) self.canvas.bind_all("<Button-5>", self._on_mousewheel)
try: import winreg except ImportError: winreg = None from pathlib import Path from shutil import copyfile from urllib.parse import urlparse, urlunparse from platformdirs import user_config_dir, user_data_dir, user_cache_dir from serde import serialize, deserialize import serde.json from modlunky2.utils import is_windows if is_windows(): # Import for pyinstaller to detect this module import platformdirs.windows # pylint: disable=unused-import PROGRAMS_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" DEFAULT_PATH = Path("C:/Program Files (x86)/Steam/steamapps/common/Spelunky 2") EXE_NAME = "Spel2.exe" APP_AUTHOR = "spelunky.fyi" APP_NAME = "modlunky2" CONFIG_DIR = Path(user_config_dir(APP_NAME, APP_AUTHOR)) DATA_DIR = Path(user_data_dir(APP_NAME, APP_AUTHOR)) CACHE_DIR = Path(user_cache_dir(APP_NAME, APP_AUTHOR)) SHOW_PACKING_DEFAULT = False MIN_WIDTH = 1280
def __init__(self, modlunky_config: Config, log_level=logging.INFO): logger.debug("Initializing UI") self.modlunky_config = modlunky_config self.current_version = current_version() self.latest_version = None self.needs_update = False self._shutdown_handlers = [] self._shutting_down = False self.log_queue = Queue() self.queue_handler = QueueHandler(self.log_queue) register_queue_handler(self.queue_handler, log_level) self.task_manager = TaskManager(self.log_queue, log_level) self.task_manager.register_task( "modlunky2:update_start", update_start, True, on_complete="modlunky2:update_complete", ) self.task_manager.register_handler("modlunky2:update_complete", self.update_complete) self.task_manager.register_task( "modlunky2:check_for_latest", check_for_latest, True, ) self.task_manager.register_handler("modlunky2:latest_version", self.handle_modlunky_latest_version) self.root = tk.Tk(className="Modlunky2") self.load_themes() style = ttk.Style(self.root) self.root.default_theme = style.theme_use() valid_themes = self.root.call("ttk::themes") self.root.title("Modlunky 2") self.root.geometry(modlunky_config.geometry) self.last_geometry = modlunky_config.geometry self.root.bind("<Configure>", self.handle_resize) if modlunky_config.theme and modlunky_config.theme in valid_themes: style.theme_use(modlunky_config.theme) self.root.event_add("<<ThemeChange>>", "None") style.configure( "ModList.TCheckbutton", font=("Segoe UI", 12, "bold"), # TODO: dynamic sizing for larger windows wraplength="640", ) style.configure("Update.TButton", font="sans 12 bold") style.configure("TOptionMenu", anchor="w") style.configure("Link.TLabel", foreground="royal blue") style.configure( "Thicc.TButton", font=("Arial", 16, "bold"), ) default_background = style.lookup("TFrame", "background") self.root.configure(bg=default_background) self.root.minsize(MIN_WIDTH, MIN_HEIGHT) self.icon_png = PhotoImage(file=BASE_DIR / "static/images/icon.png") self.root.iconphoto(False, self.icon_png) sys.excepthook = exception_logger self.root.report_callback_exception = exception_logger self.root.grid_columnconfigure(0, weight=1) self.root.grid_rowconfigure(0, weight=1) self.top_frame = ttk.Frame(self.root) self.top_frame.grid(row=0, column=0, sticky="nsew") self.top_frame.grid_columnconfigure(0, weight=1) self.top_frame.grid_rowconfigure(0, weight=0) self.top_frame.grid_rowconfigure(1, weight=1) self.top_frame.grid_rowconfigure(2, weight=0) self.update_frame = ttk.LabelFrame(self.top_frame, text="Modlunky2 Update Available") self.update_frame.columnconfigure(0, weight=1) self.update_frame.rowconfigure(0, weight=1) self.update_button = ttk.Button( self.update_frame, text="Update Now!", command=self.update, style="Update.TButton", ) # Handle shutting down cleanly self.root.protocol("WM_DELETE_WINDOW", self.quit) self.root.bind("<Escape>", self.quit) self.tabs = {} self.tab_control = ttk.Notebook(self.top_frame) logger.debug("Registering Tabs") self.register_tab( "Playlunky", PlayTab, tab_control=self.tab_control, modlunky_config=modlunky_config, task_manager=self.task_manager, ) self.register_tab( "Install Mods", InstallTab, tab_control=self.tab_control, modlunky_config=modlunky_config, task_manager=self.task_manager, ) self.register_tab( "Overlunky", OverlunkyTab, tab_control=self.tab_control, modlunky_config=modlunky_config, task_manager=self.task_manager, ) self.register_tab( "Extract Assets", ExtractTab, tab_control=self.tab_control, modlunky_config=modlunky_config, task_manager=self.task_manager, ) if self.modlunky_config.show_packing: self.register_tab( "Pack Assets (Deprecated)", PackTab, tab_control=self.tab_control, modlunky_config=modlunky_config, task_manager=self.task_manager, ) self.register_tab( "Level Editor", LevelsTab, tab_control=self.tab_control, modlunky_ui=self, modlunky_config=modlunky_config, ) if is_windows(): self.register_tab( "Trackers", TrackersTab, tab_control=self.tab_control, ml_config=modlunky_config, ) self.register_tab( "Settings", SettingsTab, tab_control=self.tab_control, modlunky_config=modlunky_config, ) self.select_last_tab() self.tab_control.bind("<<NotebookTabChanged>>", self.on_tab_change) self.tab_control.grid(column=0, row=1, padx=2, pady=(4, 0), sticky="nsew") self.console_frame = ttk.LabelFrame(self.top_frame, text="Console") self.console_frame.columnconfigure(0, weight=1) self.console_frame.rowconfigure(0, weight=1) self.console = ConsoleWindow(self.queue_handler, self.console_frame) self.console.grid(column=0, row=0, padx=2, pady=2, sticky="ew") self.version_label = ttk.Label(self.top_frame, text=f"v{self.current_version}", font="Helvitica 9 italic") self.version_label.grid(column=0, row=3, padx=5, sticky="e") self.ws_thread = None self.task_manager.start_process() self.last_ping = time.time() self.root.after(100, self.after_task_manager) self.root.after(1000, self.after_ws_thread) self.root.after(1000, self.after_record_win) self.check_for_updates() self.check_requirements()