Initial commit
Dieser Commit ist enthalten in:
16
gui/handlers/__init__.py
Normale Datei
16
gui/handlers/__init__.py
Normale Datei
@ -0,0 +1,16 @@
|
||||
"""
|
||||
Handler modules for MainWindow refactoring
|
||||
Separates concerns and reduces God Class anti-pattern
|
||||
"""
|
||||
|
||||
from .gitea_operations import GiteaOperationsHandler
|
||||
from .process_manager import ProcessManagerHandler
|
||||
from .project_manager import ProjectManagerHandler
|
||||
from .ui_helpers import UIHelpersHandler
|
||||
|
||||
__all__ = [
|
||||
'GiteaOperationsHandler',
|
||||
'ProcessManagerHandler',
|
||||
'ProjectManagerHandler',
|
||||
'UIHelpersHandler'
|
||||
]
|
||||
19
gui/handlers/base_handler.py
Normale Datei
19
gui/handlers/base_handler.py
Normale Datei
@ -0,0 +1,19 @@
|
||||
"""
|
||||
Base Handler for common functionality
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from utils.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.main_window import MainWindow
|
||||
|
||||
|
||||
class BaseHandler:
|
||||
"""Base class for all handlers"""
|
||||
|
||||
def __init__(self, main_window: 'MainWindow'):
|
||||
"""Initialize with reference to main window"""
|
||||
self.main_window = main_window
|
||||
self.root = main_window.root
|
||||
logger.info(f"{self.__class__.__name__} initialized")
|
||||
1891
gui/handlers/gitea_operations.py
Normale Datei
1891
gui/handlers/gitea_operations.py
Normale Datei
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
101
gui/handlers/process_manager.py
Normale Datei
101
gui/handlers/process_manager.py
Normale Datei
@ -0,0 +1,101 @@
|
||||
"""
|
||||
Process Manager Handler
|
||||
Handles process monitoring and management operations
|
||||
"""
|
||||
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from utils.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.main_window import MainWindow
|
||||
from project_manager import Project
|
||||
|
||||
|
||||
class ProcessManagerHandler:
|
||||
"""Handles all process management operations for MainWindow"""
|
||||
|
||||
def __init__(self, main_window: 'MainWindow'):
|
||||
"""Initialize with reference to main window"""
|
||||
self.main_window = main_window
|
||||
self.root = main_window.root
|
||||
self.process_manager = main_window.process_manager
|
||||
self.process_tracker = main_window.process_tracker
|
||||
self.project_manager = main_window.project_manager
|
||||
logger.info("ProcessManagerHandler initialized")
|
||||
|
||||
def monitor_process(self, project: 'Project', process) -> None:
|
||||
"""Monitor a process for a project"""
|
||||
return self.main_window._original_monitor_process(project, process)
|
||||
|
||||
def check_process_status(self) -> None:
|
||||
"""Check status of all processes"""
|
||||
return self.main_window._original_check_process_status()
|
||||
|
||||
def stop_project(self, project: 'Project') -> None:
|
||||
"""Stop a running project"""
|
||||
return self.main_window._original_stop_project(project)
|
||||
|
||||
def update_status(self, message: str, error: bool = False) -> None:
|
||||
"""Update status bar message"""
|
||||
# Direct implementation
|
||||
from gui.styles import COLORS
|
||||
|
||||
if hasattr(self.main_window, 'status_label'):
|
||||
self.main_window.status_label.configure(
|
||||
text=message,
|
||||
text_color=COLORS['accent_error'] if error else COLORS['text_secondary']
|
||||
)
|
||||
logger.debug(f"Status updated: {message} (error={error})")
|
||||
|
||||
def download_log(self) -> None:
|
||||
"""Download comprehensive application log with all interactions"""
|
||||
# Direct implementation
|
||||
from tkinter import filedialog, messagebox
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
logger.info(f"Download log clicked - Total entries: {len(logger.log_entries)}")
|
||||
|
||||
try:
|
||||
# Generate default filename with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
default_filename = f"CPM_FullLog_{timestamp}.log"
|
||||
|
||||
# Open file dialog to choose save location
|
||||
file_path = filedialog.asksaveasfilename(
|
||||
defaultextension=".log",
|
||||
filetypes=[("Log files", "*.log"), ("Text files", "*.txt"), ("All files", "*.*")],
|
||||
initialfile=default_filename,
|
||||
title="Save Complete Application Log"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
# Export logs to chosen location with system info
|
||||
logger.export_logs(file_path, include_system_info=True)
|
||||
|
||||
# Add completion entry
|
||||
logger.info(f"Log export completed - Total entries: {len(logger.log_entries)}, Interactions: {logger.interaction_count}")
|
||||
|
||||
# Update status
|
||||
self.update_status(f"Log saved: {os.path.basename(file_path)} ({len(logger.log_entries)} entries)")
|
||||
|
||||
# Show success message with log details
|
||||
messagebox.showinfo(
|
||||
"Log Export Successful",
|
||||
f"Complete application log saved to:\n{file_path}\n\n"
|
||||
f"Total log entries: {len(logger.log_entries):,}\n"
|
||||
f"UI interactions logged: {logger.interaction_count:,}\n"
|
||||
f"File size: {os.path.getsize(file_path) / 1024:.1f} KB"
|
||||
)
|
||||
else:
|
||||
logger.info("Log export cancelled by user")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error saving log: {str(e)}"
|
||||
self.update_status(error_msg, error=True)
|
||||
logger.log_exception(e, "download_log")
|
||||
messagebox.showerror("Export Error", error_msg)
|
||||
|
||||
def _handle_process_ended(self, project: 'Project') -> None:
|
||||
"""Handle process end event (private method)"""
|
||||
return self.main_window._original_handle_process_ended(project)
|
||||
102
gui/handlers/project_manager.py
Normale Datei
102
gui/handlers/project_manager.py
Normale Datei
@ -0,0 +1,102 @@
|
||||
"""
|
||||
Project Manager Handler
|
||||
Handles project CRUD operations and project-related UI updates
|
||||
"""
|
||||
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from utils.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.main_window import MainWindow
|
||||
from project_manager import Project
|
||||
|
||||
|
||||
class ProjectManagerHandler:
|
||||
"""Handles all project management operations for MainWindow"""
|
||||
|
||||
def __init__(self, main_window: 'MainWindow'):
|
||||
"""Initialize with reference to main window"""
|
||||
self.main_window = main_window
|
||||
self.root = main_window.root
|
||||
self.project_manager = main_window.project_manager
|
||||
self.terminal_launcher = main_window.terminal_launcher
|
||||
self.readme_generator = main_window.readme_generator
|
||||
self.vps_connection = main_window.vps_connection
|
||||
logger.info("ProjectManagerHandler initialized")
|
||||
|
||||
def add_new_project(self) -> None:
|
||||
"""Add a new project with logging"""
|
||||
logger.info("Add new project initiated")
|
||||
return self.main_window._original_add_new_project()
|
||||
|
||||
def open_project(self, project: 'Project') -> None:
|
||||
"""Open a project with comprehensive logging"""
|
||||
logger.info(f"Opening project: {project.name} (ID: {project.id}) at {project.path}")
|
||||
return self.main_window._original_open_project(project)
|
||||
|
||||
def delete_project(self, project: 'Project') -> None:
|
||||
"""Delete a project"""
|
||||
# Direct implementation
|
||||
from tkinter import messagebox
|
||||
logger.info(f"Attempting to delete project: {project.name}")
|
||||
|
||||
if messagebox.askyesno("Projekt löschen",
|
||||
f"Möchten Sie das Projekt '{project.name}' wirklich aus dem Projekt-Manager entfernen?\n\n"
|
||||
"Hinweis: Die Dateien werden NICHT gelöscht."):
|
||||
self.project_manager.remove_project(project.id)
|
||||
self.main_window.refresh_projects()
|
||||
if hasattr(self.main_window, 'update_status'):
|
||||
self.main_window.update_status(f"Removed: {project.name}")
|
||||
logger.info(f"Project deleted: {project.name}")
|
||||
|
||||
def rename_project(self, project: 'Project') -> None:
|
||||
"""Rename a project with logging"""
|
||||
logger.info(f"Rename project initiated for: {project.name}")
|
||||
return self.main_window._original_rename_project(project)
|
||||
|
||||
def refresh_projects(self) -> None:
|
||||
"""Refresh the project display with logging"""
|
||||
logger.debug("Refresh projects called")
|
||||
logger.info(f"Refreshing projects - Total: {len(self.project_manager.projects)}")
|
||||
return self.main_window._original_refresh_projects()
|
||||
|
||||
def create_project_from_repo(self, repo_data: dict, project_path: str) -> None:
|
||||
"""Create a project from repository data"""
|
||||
return self.main_window._original_create_project_from_repo(repo_data, project_path)
|
||||
|
||||
def open_vps_connection(self, project: 'Project') -> None:
|
||||
"""Open VPS connection for project"""
|
||||
return self.main_window._original_open_vps_connection(project)
|
||||
|
||||
def open_admin_panel(self, project: 'Project') -> None:
|
||||
"""Open admin panel for project"""
|
||||
return self.main_window._original_open_admin_panel(project)
|
||||
|
||||
def open_vps_docker(self, project: 'Project') -> None:
|
||||
"""Open VPS Docker management"""
|
||||
return self.main_window._original_open_vps_docker(project)
|
||||
|
||||
def open_readme(self, project: 'Project') -> None:
|
||||
"""Open or generate README for project"""
|
||||
return self.main_window._original_open_readme(project)
|
||||
|
||||
def generate_readme_background(self, project: 'Project') -> None:
|
||||
"""Generate README in background"""
|
||||
return self.main_window._original_generate_readme_background(project)
|
||||
|
||||
def open_gitea_window(self) -> None:
|
||||
"""Open Gitea explorer window"""
|
||||
return self.main_window._original_open_gitea_window()
|
||||
|
||||
def on_gitea_repo_select(self, repo_data: dict) -> None:
|
||||
"""Handle Gitea repository selection"""
|
||||
return self.main_window._original_on_gitea_repo_select(repo_data)
|
||||
|
||||
def clear_project_selection(self) -> None:
|
||||
"""Clear current project selection"""
|
||||
return self.main_window._original_clear_project_selection()
|
||||
|
||||
def on_project_select(self, project: 'Project', tile) -> None:
|
||||
"""Handle project selection with logging"""
|
||||
logger.info(f"Project selected: {project.name} (has Gitea repo: {bool(getattr(project, 'gitea_repo', None))})")
|
||||
return self.main_window._original_on_project_select(project, tile)
|
||||
203
gui/handlers/ui_helpers.py
Normale Datei
203
gui/handlers/ui_helpers.py
Normale Datei
@ -0,0 +1,203 @@
|
||||
"""
|
||||
UI Helpers Handler
|
||||
Handles UI creation, updates, and helper functions
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from utils.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gui.main_window import MainWindow
|
||||
from project_manager import Project
|
||||
|
||||
|
||||
class UIHelpersHandler:
|
||||
"""Handles UI helper operations for MainWindow"""
|
||||
|
||||
def __init__(self, main_window: 'MainWindow'):
|
||||
"""Initialize with reference to main window"""
|
||||
self.main_window = main_window
|
||||
self.root = main_window.root
|
||||
logger.info("UIHelpersHandler initialized")
|
||||
|
||||
def setup_ui(self) -> None:
|
||||
"""Setup the main UI"""
|
||||
return self.main_window._original_setup_ui()
|
||||
|
||||
def create_header(self) -> None:
|
||||
"""Create header section"""
|
||||
# Direct implementation with proper references
|
||||
import customtkinter as ctk
|
||||
from gui.styles import COLORS, FONTS
|
||||
|
||||
header_frame = ctk.CTkFrame(self.main_window.main_container, fg_color=COLORS['bg_secondary'], height=80)
|
||||
header_frame.pack(fill="x", padx=0, pady=0)
|
||||
header_frame.pack_propagate(False)
|
||||
|
||||
# Title
|
||||
self.main_window.title_label = ctk.CTkLabel(
|
||||
header_frame,
|
||||
text="IntelSight - Claude Project Manager",
|
||||
font=FONTS['heading'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
self.main_window.title_label.pack(side="left", padx=30, pady=20)
|
||||
|
||||
# Toolbar buttons
|
||||
toolbar = ctk.CTkFrame(header_frame, fg_color="transparent")
|
||||
toolbar.pack(side="right", padx=30, pady=20)
|
||||
|
||||
# Log download button
|
||||
self.main_window.log_btn = ctk.CTkButton(
|
||||
toolbar,
|
||||
text="📥 Log",
|
||||
command=self.main_window.download_log,
|
||||
width=80,
|
||||
fg_color=COLORS['accent_primary'],
|
||||
hover_color=COLORS['accent_hover'],
|
||||
text_color="#FFFFFF",
|
||||
font=('Segoe UI', 12)
|
||||
)
|
||||
self.main_window.log_btn.pack(side="left", padx=(0, 10))
|
||||
|
||||
# Refresh button
|
||||
self.main_window.refresh_btn = ctk.CTkButton(
|
||||
toolbar,
|
||||
text="↻ Refresh",
|
||||
command=self.main_window.refresh_projects,
|
||||
width=100,
|
||||
fg_color=COLORS['bg_tile'],
|
||||
hover_color=COLORS['bg_tile_hover'],
|
||||
text_color=COLORS['text_primary']
|
||||
)
|
||||
self.main_window.refresh_btn.pack(side="left", padx=(0, 10))
|
||||
logger.debug("Header created")
|
||||
|
||||
def create_content_area(self) -> None:
|
||||
"""Create content area"""
|
||||
return self.main_window._original_create_content_area()
|
||||
|
||||
def create_status_bar(self) -> None:
|
||||
"""Create status bar"""
|
||||
# Direct implementation
|
||||
import customtkinter as ctk
|
||||
from gui.styles import COLORS, FONTS
|
||||
|
||||
self.main_window.status_bar = ctk.CTkFrame(
|
||||
self.main_window.main_container,
|
||||
fg_color=COLORS['bg_secondary'],
|
||||
height=30
|
||||
)
|
||||
self.main_window.status_bar.pack(fill="x", side="bottom")
|
||||
self.main_window.status_bar.pack_propagate(False)
|
||||
|
||||
self.main_window.status_label = ctk.CTkLabel(
|
||||
self.main_window.status_bar,
|
||||
text="Ready",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
self.main_window.status_label.pack(side="left", padx=20, pady=5)
|
||||
|
||||
# Project count
|
||||
self.main_window.count_label = ctk.CTkLabel(
|
||||
self.main_window.status_bar,
|
||||
text="0 projects",
|
||||
font=FONTS['small'],
|
||||
text_color=COLORS['text_secondary']
|
||||
)
|
||||
self.main_window.count_label.pack(side="right", padx=20, pady=5)
|
||||
logger.debug("Status bar created")
|
||||
|
||||
def create_project_tile(self, project: 'Project', parent) -> 'ProjectTile':
|
||||
"""Create a project tile"""
|
||||
return self.main_window._original_create_project_tile(project, parent)
|
||||
|
||||
def create_add_tile(self, parent) -> 'AddProjectTile':
|
||||
"""Create add project tile"""
|
||||
return self.main_window._original_create_add_tile(parent)
|
||||
|
||||
def create_project_tile_flow(self, project: 'Project', parent) -> 'ProjectTile':
|
||||
"""Create project tile for flow layout"""
|
||||
return self.main_window._original_create_project_tile_flow(project, parent)
|
||||
|
||||
def create_add_tile_flow(self, parent) -> 'AddProjectTile':
|
||||
"""Create add tile for flow layout"""
|
||||
return self.main_window._original_create_add_tile_flow(parent)
|
||||
|
||||
def refresh_ui(self) -> None:
|
||||
"""Refresh the UI"""
|
||||
return self.main_window._original_refresh_ui()
|
||||
|
||||
def load_and_apply_theme(self) -> None:
|
||||
"""Load and apply theme preference"""
|
||||
# Direct implementation - very simple method
|
||||
import customtkinter as ctk
|
||||
ctk.set_appearance_mode('dark')
|
||||
logger.info("Theme applied: dark mode")
|
||||
|
||||
def on_window_resize(self, event) -> None:
|
||||
"""Handle window resize event"""
|
||||
# Direct implementation
|
||||
# Only process resize events from the main window
|
||||
if event.widget == self.root:
|
||||
# Cancel previous timer
|
||||
if self.main_window.resize_timer:
|
||||
self.root.after_cancel(self.main_window.resize_timer)
|
||||
|
||||
# Set new timer to refresh after resize stops with differential update
|
||||
self.main_window.resize_timer = self.root.after(
|
||||
300,
|
||||
lambda: self.main_window.refresh_projects(differential=True)
|
||||
)
|
||||
logger.debug("Window resize event handled")
|
||||
|
||||
def setup_interaction_tracking(self) -> None:
|
||||
"""Setup interaction tracking"""
|
||||
return self.main_window._original_setup_interaction_tracking()
|
||||
|
||||
def _show_scrollable_info(self, title: str, content: str) -> None:
|
||||
"""Show scrollable information dialog"""
|
||||
# Direct implementation - standalone UI method
|
||||
import customtkinter as ctk
|
||||
|
||||
dialog = ctk.CTkToplevel(self.root)
|
||||
dialog.title(title)
|
||||
dialog.geometry("600x500")
|
||||
|
||||
# Center the dialog
|
||||
dialog.transient(self.root)
|
||||
dialog.update_idletasks()
|
||||
x = (dialog.winfo_screenwidth() - 600) // 2
|
||||
y = (dialog.winfo_screenheight() - 500) // 2
|
||||
dialog.geometry(f"600x500+{x}+{y}")
|
||||
|
||||
# Text widget with scrollbar
|
||||
text_frame = ctk.CTkFrame(dialog)
|
||||
text_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
text_widget = ctk.CTkTextbox(text_frame, width=580, height=450)
|
||||
text_widget.pack(fill="both", expand=True)
|
||||
text_widget.insert("1.0", content)
|
||||
text_widget.configure(state="disabled")
|
||||
|
||||
# Close button
|
||||
close_btn = ctk.CTkButton(
|
||||
dialog,
|
||||
text="Schließen",
|
||||
command=dialog.destroy,
|
||||
width=100
|
||||
)
|
||||
close_btn.pack(pady=(0, 10))
|
||||
|
||||
dialog.grab_set()
|
||||
dialog.focus_set()
|
||||
logger.debug(f"Scrollable dialog shown: {title}")
|
||||
|
||||
def _differential_update(self, current_projects: list, previous_projects: list) -> None:
|
||||
"""Perform differential update of projects"""
|
||||
return self.main_window._original_differential_update(current_projects, previous_projects)
|
||||
|
||||
def _update_project_tiles_colors(self) -> None:
|
||||
"""Update project tile colors"""
|
||||
return self.main_window._original_update_project_tiles_colors()
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren