From 58bbff8e692c9b4bf2713065441b2657c247c82f Mon Sep 17 00:00:00 2001 From: Boris Yumankulov Date: Sun, 21 Dec 2025 19:34:32 +0500 Subject: [PATCH] chore: clean all vulture 80% confidence dead code Signed-off-by: Boris Yumankulov --- portprotonqt/config_utils.py | 16 --- portprotonqt/context_menu_manager.py | 3 +- portprotonqt/dialogs.py | 1 - portprotonqt/downloader.py | 5 - portprotonqt/egs_api.py | 38 ------- portprotonqt/game_library_manager.py | 31 +----- portprotonqt/main_window.py | 25 ++--- portprotonqt/search_utils.py | 141 +------------------------ portprotonqt/themes/standart/styles.py | 21 ---- portprotonqt/tray_manager.py | 5 - 10 files changed, 10 insertions(+), 276 deletions(-) diff --git a/portprotonqt/config_utils.py b/portprotonqt/config_utils.py index 7a93de0..eac55c5 100644 --- a/portprotonqt/config_utils.py +++ b/portprotonqt/config_utils.py @@ -77,22 +77,6 @@ def invalidate_config_cache(config_file: str = CONFIG_FILE): del _config_last_modified[config_file] logger.debug(f"Config cache invalidated for {config_file}") -def read_config(): - """Reads the configuration file and returns a dictionary of parameters. - Example line in config (no sections): - detail_level = detailed - """ - config_dict = {} - if os.path.exists(CONFIG_FILE): - with open(CONFIG_FILE, encoding="utf-8") as f: - for line in f: - line = line.strip() - if not line or line.startswith("#"): - continue - key, sep, value = line.partition("=") - if sep: - config_dict[key.strip()] = value.strip() - return config_dict def read_theme_from_config(): """Reads the theme from the [Appearance] section of the configuration file. diff --git a/portprotonqt/context_menu_manager.py b/portprotonqt/context_menu_manager.py index 1d0de62..a7cba34 100644 --- a/portprotonqt/context_menu_manager.py +++ b/portprotonqt/context_menu_manager.py @@ -29,7 +29,7 @@ class ContextMenuSignals(QObject): class ContextMenuManager: """Manages context menu actions for game management in PortProtonQt.""" - def __init__(self, parent, portproton_location, theme, load_games_callback, game_library_manager): + def __init__(self, parent, portproton_location, theme, game_library_manager): """ Initialize the ContextMenuManager. @@ -44,7 +44,6 @@ class ContextMenuManager: self.portproton_location = portproton_location self.theme = theme self.theme_manager = ThemeManager() - self.load_games = load_games_callback self.game_library_manager = game_library_manager self.update_game_grid = game_library_manager.update_game_grid self.legendary_path = os.path.join( diff --git a/portprotonqt/dialogs.py b/portprotonqt/dialogs.py index 3838329..2770680 100644 --- a/portprotonqt/dialogs.py +++ b/portprotonqt/dialogs.py @@ -853,7 +853,6 @@ class AddGameDialog(QDialog): from portprotonqt.context_menu_manager import CustomLineEdit # Локальный импорт self.theme = theme if theme else theme_manager.apply_theme(read_theme_from_config()) self.edit_mode = edit_mode - self.original_name = game_name self.last_exe_path = exe_path # Store last selected exe path self.last_cover_path = cover_path # Store last selected cover path self.downloader = Downloader(max_workers=4) # Initialize Downloader diff --git a/portprotonqt/downloader.py b/portprotonqt/downloader.py index a0fe5e0..e2b7f0e 100644 --- a/portprotonqt/downloader.py +++ b/portprotonqt/downloader.py @@ -126,8 +126,6 @@ class Downloader(QObject): self._has_internet = True return self._has_internet - def reset_internet_check(self): - self._has_internet = None def _get_url_lock(self, url): with self._global_lock: @@ -247,9 +245,6 @@ class Downloader(QObject): with self._global_lock: self._cache.clear() - def is_cached(self, url): - with self._global_lock: - return url in self._cache def get_latest_legendary_release(self): """Get the latest legendary release info from GitHub API.""" diff --git a/portprotonqt/egs_api.py b/portprotonqt/egs_api.py index c76023c..2ccd3f5 100644 --- a/portprotonqt/egs_api.py +++ b/portprotonqt/egs_api.py @@ -555,44 +555,6 @@ def get_egs_game_description_async( cleaned = re.sub(r'[^a-z0-9 ]', '', title.lower()).strip() return re.sub(r'\s+', '-', cleaned) - def get_product_slug(namespace: str) -> str: - """Fetches the product slug using the namespace via GraphQL.""" - search_query = { - "query": """ - query { - Catalog { - catalogNs(namespace: $namespace) { - mappings(pageType: "productHome") { - pageSlug - pageType - } - } - } - } - """, - "variables": {"namespace": namespace} - } - try: - response = requests.post( - "https://launcher.store.epicgames.com/graphql", - json=search_query, - headers=headers, - timeout=10 - ) - response.raise_for_status() - data = orjson.loads(response.content) - mappings = data.get("data", {}).get("Catalog", {}).get("catalogNs", {}).get("mappings", []) - for mapping in mappings: - if mapping.get("pageType") == "productHome": - return mapping.get("pageSlug", "") - logger.warning("No productHome slug found for namespace %s", namespace) - return "" - except requests.RequestException as e: - logger.warning("Failed to fetch product slug for namespace %s: %s", namespace, str(e)) - return "" - except orjson.JSONDecodeError: - logger.warning("Invalid JSON response for namespace %s", namespace) - return "" def fetch_legacy_description(url: str) -> str: """Fetches description from the legacy API, handling DNS failures.""" diff --git a/portprotonqt/game_library_manager.py b/portprotonqt/game_library_manager.py index 2c01175..51472d7 100644 --- a/portprotonqt/game_library_manager.py +++ b/portprotonqt/game_library_manager.py @@ -33,7 +33,6 @@ class MainWindowProtocol(Protocol): # Required attributes searchEdit: CustomLineEdit - _last_card_width: int card_width: int current_hovered_card: GameCard | None current_focused_card: GameCard | None @@ -134,7 +133,6 @@ class GameLibraryManager: self.sizeSlider.setToolTip(f"{self.card_width} px") save_card_size(self.card_width) self.main_window.card_width = self.card_width - self.main_window._last_card_width = self.card_width for card in self.game_card_cache.values(): card.update_card_size(self.card_width) self.update_game_grid() @@ -266,7 +264,7 @@ class GameLibraryManager: if self.is_filtering: # Filter mode: use the pre-computed filtered_games from optimized search # This means we already have the exact games to show - self._update_search_results() + self._update_search_results(search_text) else: # Full update: sorting, removal/addition, reorganization games_list = self.filtered_games if self.filtered_games else self.games @@ -385,13 +383,11 @@ class GameLibraryManager: if self.gamesListLayout is not None: self.gamesListLayout.update() self.gamesListWidget.updateGeometry() - self.main_window._last_card_width = self.card_width - self.force_update_cards_library() self.is_filtering = False # Reset flag in any case - def _update_search_results(self): + def _update_search_results(self, search_text: str = ""): """Update the grid with pre-computed search results.""" if self.gamesListLayout is None or self.gamesListWidget is None: return @@ -449,35 +445,12 @@ class GameLibraryManager: if self.gamesListLayout is not None: self.gamesListLayout.update() self.gamesListWidget.updateGeometry() - self.main_window._last_card_width = self.card_width self.force_update_cards_library() - def _apply_filter_visibility(self, search_text: str): - """Applies visibility to cards based on search, without changing the layout.""" - # This method is used for simple substring matching - # For the new optimized search, we'll use a different approach in update_game_grid - # when is_filter=True - visible_count = 0 - for game_key, card in self.game_card_cache.items(): - game_name = card.name # Assume GameCard has 'name' attribute - should_be_visible = not search_text or search_text in game_name.lower() - if card.isVisible() != should_be_visible: - card.setVisible(should_be_visible) - if should_be_visible: - visible_count += 1 - # Load image only for newly visible cards - if game_key in self.pending_images: - cover_path, width, height, callback = self.pending_images.pop(game_key) - load_pixmap_async(cover_path, width, height, callback) - - # Force full relayout after visibility changes - if self.gamesListLayout is not None: - self.gamesListLayout.invalidate() # Принудительно инвалидируем для пересчёта self.gamesListLayout.update() if self.gamesListWidget is not None: self.gamesListWidget.updateGeometry() - self.main_window._last_card_width = self.card_width # If search is empty, load images for visible ones if not search_text: diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py index 0514934..f8e7a29 100644 --- a/portprotonqt/main_window.py +++ b/portprotonqt/main_window.py @@ -57,7 +57,6 @@ class MainWindow(QMainWindow): def __init__(self, app_name: str, version: str): super().__init__() self.theme_manager = ThemeManager() - self.is_exiting = False selected_theme = read_theme_from_config() self.current_theme_name = selected_theme # Apply theme but defer heavy font loading @@ -65,7 +64,6 @@ class MainWindow(QMainWindow): self.tray_manager = TrayManager(self, app_name, self.current_theme_name) self.card_width = read_card_size() self.auto_card_width = read_auto_card_size() - self._last_card_width = self.card_width self.setWindowTitle(f"{app_name} {version}") self.setMinimumSize(800, 600) @@ -85,7 +83,6 @@ class MainWindow(QMainWindow): self, self.portproton_location, self.theme, - self.loadGames, self.game_library_manager ) @@ -144,7 +141,6 @@ class MainWindow(QMainWindow): QApplication.processEvents() # Process to show progress bar immediately self.installing = False - self.current_install_script = None self.install_process = None self.install_monitor_timer = None @@ -647,7 +643,6 @@ class MainWindow(QMainWindow): QMessageBox.warning(self, _("Warning"), _("Installation already in progress.")) return self.installing = True - self.current_install_script = script_name self.seen_progress = False self.current_percent = 0.0 start_sh = self.start_sh @@ -735,7 +730,6 @@ class MainWindow(QMainWindow): QMessageBox.warning(self, _("Error"), f"Installation failed (code: {exit_code}).") self.progress_bar.setVisible(False) - self.current_install_script = None if self.install_process: self.install_process.deleteLater() self.install_process = None @@ -1165,7 +1159,7 @@ class MainWindow(QMainWindow): self.searchEdit = CustomLineEdit(self, theme=self.theme) icon: QIcon = cast(QIcon, self.theme_manager.get_icon("search")) action_pos = cast(QLineEdit.ActionPosition, QLineEdit.ActionPosition.LeadingPosition) - self.search_action = self.searchEdit.addAction(icon, action_pos) + self.searchEdit.addAction(icon, action_pos) self.searchEdit.setMaximumWidth(200) self.searchEdit.setPlaceholderText(_("Find Games ...")) self.searchEdit.setClearButtonEnabled(True) @@ -1452,7 +1446,7 @@ class MainWindow(QMainWindow): self.autoInstallSearchLineEdit = CustomLineEdit(self, theme=self.theme) icon: QIcon = cast(QIcon, self.theme_manager.get_icon("search")) action_pos = QLineEdit.ActionPosition.LeadingPosition - self.search_action = self.autoInstallSearchLineEdit.addAction(icon, action_pos) + self.autoInstallSearchLineEdit.addAction(icon, action_pos) self.autoInstallSearchLineEdit.setMaximumWidth(200) self.autoInstallSearchLineEdit.setPlaceholderText(_("Find Games ...")) self.autoInstallSearchLineEdit.setClearButtonEnabled(True) @@ -1627,12 +1621,10 @@ class MainWindow(QMainWindow): def filterAutoInstallGames(self): """Filter auto install game cards based on search text.""" search_text = self.autoInstallSearchLineEdit.text().lower().strip() - visible_count = 0 for card in self.allAutoInstallCards: if search_text in card.name.lower(): card.setVisible(True) - visible_count += 1 else: card.setVisible(False) @@ -1781,7 +1773,7 @@ class MainWindow(QMainWindow): self.update_status_message.emit(_("Launching tool..."), 0) proc = QProcess(self) - proc.finished.connect(lambda exitCode, exitStatus: self._on_wine_tool_finished(exitCode, cli_arg)) + proc.finished.connect(lambda exitCode: self._on_wine_tool_finished(exitCode, cli_arg)) proc.errorOccurred.connect(lambda error: self._on_wine_tool_error(error, cli_arg)) proc.start(cmd[0], cmd[1:]) @@ -1878,7 +1870,7 @@ class MainWindow(QMainWindow): self.update_status_message.emit(_("Clearing prefix..."), 0) self.clear_process = QProcess(self) - self.clear_process.finished.connect(lambda exitCode, exitStatus: self._on_clear_prefix_finished(exitCode)) + self.clear_process.finished.connect(lambda exitCode: self._on_clear_prefix_finished(exitCode)) self.clear_process.errorOccurred.connect(lambda error: self._on_clear_prefix_error(error)) cmd = start_sh + ["cli", "--clear_pfx", selected_wine, selected_prefix] self.clear_process.start(cmd[0], cmd[1:]) @@ -1916,7 +1908,7 @@ class MainWindow(QMainWindow): return start_sh = self.start_sh self.backup_process = QProcess(self) - self.backup_process.finished.connect(lambda exitCode, exitStatus: self._on_backup_finished(exitCode)) + self.backup_process.finished.connect(lambda exitCode: self._on_backup_finished(exitCode)) cmd = start_sh + ["--backup-prefix", prefix_name, backup_dir] self.backup_process.start(cmd[0], cmd[1:]) if not self.backup_process.waitForStarted(): @@ -1934,7 +1926,7 @@ class MainWindow(QMainWindow): return start_sh = self.start_sh self.restore_process = QProcess(self) - self.restore_process.finished.connect(lambda exitCode, exitStatus: self._on_restore_finished(exitCode)) + self.restore_process.finished.connect(lambda exitCode: self._on_restore_finished(exitCode)) cmd = start_sh + ["--restore-prefix", file_path] self.restore_process.start(cmd[0], cmd[1:]) if not self.restore_process.waitForStarted(): @@ -2684,7 +2676,6 @@ class MainWindow(QMainWindow): if target_running: # Игра стартовала – устанавливаем флаг, обновляем кнопку на "Stop" - self._gameLaunched = True if self.current_running_button is not None: try: self.current_running_button.setText(_("Stop")) @@ -2693,7 +2684,6 @@ class MainWindow(QMainWindow): #self._inhibit_screensaver() elif not child_running: # Игра завершилась – сбрасываем флаг, сбрасываем кнопку и останавливаем таймер - self._gameLaunched = False self.resetPlayButton() #self._uninhibit_screensaver() if hasattr(self, 'checkProcessTimer') and self.checkProcessTimer is not None: @@ -2786,7 +2776,6 @@ class MainWindow(QMainWindow): self.checkProcessTimer = None self.current_running_button = None self.target_exe = None - self._gameLaunched = False else: # Запускаем игру через PortProton env_vars = os.environ.copy() @@ -2883,7 +2872,6 @@ class MainWindow(QMainWindow): self.checkProcessTimer = None self.current_running_button = None self.target_exe = None - self._gameLaunched = False #self._uninhibit_screensaver() else: # Сохраняем ссылку на кнопку для сброса после завершения игры @@ -2929,7 +2917,6 @@ class MainWindow(QMainWindow): return # Полное закрытие приложения - self.is_exiting = True event.accept() # Скрываем и удаляем иконку трея diff --git a/portprotonqt/search_utils.py b/portprotonqt/search_utils.py index 86bfb9d..cc155e8 100644 --- a/portprotonqt/search_utils.py +++ b/portprotonqt/search_utils.py @@ -1,15 +1,11 @@ """ Utility module for search optimizations including Trie, hash tables, and fuzzy matching. """ -import concurrent.futures -import threading -from collections.abc import Callable from typing import Any from rapidfuzz import fuzz from threading import Lock from portprotonqt.logger import get_logger -from PySide6.QtCore import QThread, QRunnable, Signal, QObject, QTimer -import requests +from PySide6.QtCore import QThread, Signal, QObject logger = get_logger(__name__) @@ -139,99 +135,12 @@ class SearchOptimizer: return [] -class RequestRunnable(QRunnable): - """Runnable for executing HTTP requests in a thread.""" - - def __init__(self, method: str, url: str, on_success=None, on_error=None, **kwargs): - super().__init__() - self.method = method - self.url = url - self.kwargs = kwargs - self.result = None - self.error = None - self.on_success: Callable | None = on_success - self.on_error: Callable | None = on_error - - def run(self): - try: - if self.method.lower() == 'get': - self.result = requests.get(self.url, **self.kwargs) - elif self.method.lower() == 'post': - self.result = requests.post(self.url, **self.kwargs) - else: - raise ValueError(f"Unsupported HTTP method: {self.method}") - - # Execute success callback if provided - if self.on_success is not None: - success_callback = self.on_success # Capture the callback - def success_handler(): - if success_callback is not None: # Re-check to satisfy Pyright - success_callback(self.result) - QTimer.singleShot(0, success_handler) - except Exception as e: - self.error = e - # Execute error callback if provided - if self.on_error is not None: - error_callback = self.on_error # Capture the callback - captured_error = e # Capture the exception - def error_handler(): - error_callback(captured_error) - QTimer.singleShot(0, error_handler) -def run_request_in_thread(method: str, url: str, on_success: Callable | None = None, on_error: Callable | None = None, **kwargs): - """Run HTTP request in a separate thread using Qt's thread system.""" - runnable = RequestRunnable(method, url, on_success=on_success, on_error=on_error, **kwargs) - - # Use QThreadPool to execute the runnable - from PySide6.QtCore import QThreadPool - thread_pool = QThreadPool.globalInstance() - thread_pool.start(runnable) - - return runnable # Return the runnable to allow for potential cancellation if needed -def run_function_in_thread(func, *args, on_success: Callable | None = None, on_error: Callable | None = None, **kwargs): - """Run a function in a separate thread.""" - def execute(): - try: - result = func(*args, **kwargs) - if on_success: - on_success(result) - except Exception as e: - if on_error: - on_error(e) - - thread = threading.Thread(target=execute) - thread.daemon = True - thread.start() - return thread -def run_in_thread(func, *args, **kwargs): - """Run a function in a separate thread.""" - with concurrent.futures.ThreadPoolExecutor() as executor: - future = executor.submit(func, *args, **kwargs) - return future.result() - - -def run_in_thread_async(func, *args, callback: Callable | None = None, **kwargs): - """Run a function in a separate thread asynchronously.""" - import threading - def target(): - try: - result = func(*args, **kwargs) - if callback: - callback(result) - except Exception as e: - if callback: - callback(None) # or handle error in callback - logger.error(f"Error in threaded operation: {e}") - - thread = threading.Thread(target=target) - thread.daemon = True - thread.start() - return thread # Threaded search implementation using QThread for performance optimization @@ -320,11 +229,6 @@ class ThreadedSearch(QThread): self.worker.search_finished.connect(self.search_finished) self.worker.search_error.connect(self.search_error) - def set_search_params(self, search_text: str, games_data: list, search_type: str = "auto"): - """Set parameters for the search operation.""" - self.search_text = search_text - self.games_data = games_data - self.search_type = search_type def set_games_data(self, games_data: list): """Set the games data to be searched.""" @@ -334,46 +238,3 @@ class ThreadedSearch(QThread): def run(self): """Run the search operation in the thread.""" self.worker.execute_search(self.search_text, self.search_type) - - -class SearchThreadPool: - """ - A simple thread pool for managing multiple search operations. - """ - def __init__(self, max_threads: int = 3): - self.max_threads = max_threads - self.active_threads = [] - self.thread_queue = [] - - def submit_search(self, search_text: str, games_data: list, search_type: str = "auto", - on_start: Callable | None = None, on_finish: Callable | None = None, on_error: Callable | None = None): - """ - Submit a search operation to the pool. - - Args: - search_text: Text to search for - games_data: List of game data tuples to search in - search_type: Type of search ("exact", "prefix", "fuzzy", "auto") - on_start: Callback when search starts - on_finish: Callback when search finishes (receives results) - on_error: Callback when search errors (receives error message) - """ - search_thread = ThreadedSearch() - search_thread.set_search_params(search_text, games_data, search_type) - - # Connect callbacks if provided - if on_start: - search_thread.search_started.connect(on_start) - if on_finish: - search_thread.search_finished.connect(on_finish) - if on_error: - search_thread.search_error.connect(on_error) - - # Start the thread - search_thread.start() - self.active_threads.append(search_thread) - - # Clean up finished threads - self.active_threads = [thread for thread in self.active_threads if thread.isRunning()] - - return search_thread diff --git a/portprotonqt/themes/standart/styles.py b/portprotonqt/themes/standart/styles.py index efe6e6c..7405bc8 100644 --- a/portprotonqt/themes/standart/styles.py +++ b/portprotonqt/themes/standart/styles.py @@ -6,7 +6,6 @@ current_theme_name = read_theme_from_config() # КОНСТАНТЫ favoriteLabelSize = 48, 48 -pixmapsScaledSize = 60, 60 # VARS font_family = "Play" @@ -652,26 +651,6 @@ PLAY_BUTTON_STYLE = f""" }} """ -# СТИЛЬ КНОПКИ "ОБЗОР..." В ДИАЛОГЕ "ДОБАВИТЬ ИГРУ" -DIALOG_BROWSE_BUTTON_STYLE = f""" - QPushButton {{ - background: rgba(20, 20, 20, 0.40); - border: {border_a} rgba(255, 255, 255, 0.20); - border-radius: {border_radius_b}; - color: {color_f}; - font-size: {font_size_a}; - padding: 5px 10px; - }} - QPushButton:hover {{ - background: qlineargradient(x1:0, y1:0, x2:1, y2:0, - stop:0 rgba(0,122,255,0.20), - stop:1 rgba(0,122,255,0.15)); - }} - QPushButton:pressed {{ - background: rgba(20, 20, 20, 0.60); - border: {border_a} rgba(255, 255, 255, 0.25); - }} -""" ADDGAME_INPUT_STYLE = f""" QLineEdit {{ diff --git a/portprotonqt/tray_manager.py b/portprotonqt/tray_manager.py index 855fc16..bd37824 100644 --- a/portprotonqt/tray_manager.py +++ b/portprotonqt/tray_manager.py @@ -72,8 +72,6 @@ class TrayManager: self.tray_icon.setContextMenu(self.tray_menu) self.tray_icon.show() - self.main_window.is_exiting = False - self.click_count = 0 self.click_timer = QTimer() self.click_timer.setSingleShot(True) @@ -231,7 +229,6 @@ class TrayManager: executable = sys.executable args = sys.argv - self.main_window.is_exiting = True QApplication.quit() subprocess.Popen([executable] + args) @@ -241,11 +238,9 @@ class TrayManager: save_theme_to_config("standart") executable = sys.executable args = sys.argv - self.main_window.is_exiting = True QApplication.quit() subprocess.Popen([executable] + args) def force_exit(self): - self.main_window.is_exiting = True self.main_window.close() sys.exit(0)